@rubytech/create-maxy-code 0.1.506 → 0.1.507
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 +2 -2
- package/payload/platform/config/brand.json +79 -18
- package/payload/platform/lib/account-schema-regions/dist/index.d.ts +17 -5
- package/payload/platform/lib/account-schema-regions/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/account-schema-regions/dist/index.js +20 -6
- package/payload/platform/lib/account-schema-regions/dist/index.js.map +1 -1
- package/payload/platform/lib/account-schema-regions/src/index.ts +20 -6
- package/payload/platform/plugins/admin/PLUGIN.md +36 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +48 -2
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +7 -0
- package/payload/platform/plugins/aeo/PLUGIN.md +3 -0
- package/payload/platform/plugins/browser/PLUGIN.md +18 -0
- package/payload/platform/plugins/connector/PLUGIN.md +4 -0
- package/payload/platform/plugins/contacts/PLUGIN.md +9 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +21 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +26 -0
- package/payload/platform/plugins/email/PLUGIN.md +17 -0
- package/payload/platform/plugins/filesystem/PLUGIN.md +6 -0
- package/payload/platform/plugins/google/PLUGIN.md +12 -0
- package/payload/platform/plugins/graph/PLUGIN.md +3 -0
- package/payload/platform/plugins/graph-viewer/PLUGIN.md +1 -0
- package/payload/platform/plugins/ledger/PLUGIN.md +7 -0
- package/payload/platform/plugins/memory/PLUGIN.md +41 -0
- package/payload/platform/plugins/outlook/PLUGIN.md +24 -0
- package/payload/platform/plugins/quickbooks/PLUGIN.md +15 -0
- package/payload/platform/plugins/replicate/PLUGIN.md +3 -0
- package/payload/platform/plugins/scheduling/PLUGIN.md +10 -0
- package/payload/platform/plugins/storage-broker/PLUGIN.md +12 -0
- package/payload/platform/plugins/telegram/PLUGIN.md +3 -0
- package/payload/platform/plugins/url-get/PLUGIN.md +1 -0
- package/payload/platform/plugins/voice-mirror/PLUGIN.md +5 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +12 -0
- package/payload/platform/plugins/work/PLUGIN.md +15 -0
- package/payload/platform/plugins/workflows/PLUGIN.md +8 -0
- package/payload/platform/scripts/check-risk-class.mjs +145 -0
- package/payload/platform/scripts/lib/__pycache__/account-schema-owned-dirs.cpython-314.pyc +0 -0
- package/payload/platform/scripts/lib/provision-account-dir.sh +4 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +4 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +8 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts +13 -0
- package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-surface.js +33 -8
- package/payload/platform/services/claude-session-manager/dist/tool-surface.js.map +1 -1
- package/payload/server/{chunk-5ADBNEJA.js → chunk-2WAXM5N2.js} +111 -111
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/{AdminLoginScreens-D5KOQA8S.js → AdminLoginScreens-YT7jsh_H.js} +1 -1
- package/payload/server/public/assets/{AdminShell-C8wazpnz.js → AdminShell-Ckht9hmU.js} +1 -1
- package/payload/server/public/assets/{activity-CFqkDhTs.js → activity-DH3Wgc3O.js} +1 -1
- package/payload/server/public/assets/admin-DetvulAu.js +1 -0
- package/payload/server/public/assets/{agents-DPulfmrd.js → agents-X_IvDHLZ.js} +1 -1
- package/payload/server/public/assets/{browser-T_5xbnJh.js → browser-Bym8QhfA.js} +1 -1
- package/payload/server/public/assets/{calendar-CKjuiG8c.js → calendar-CFnufJOy.js} +1 -1
- package/payload/server/public/assets/chat-wrckyIpw.js +1 -0
- package/payload/server/public/assets/chevron-left-Dtk3jEK5.js +1 -0
- package/payload/server/public/assets/chevron-right-Dih6IZ8B.js +1 -0
- package/payload/server/public/assets/clock-iJCrrmWM.js +1 -0
- package/payload/server/public/assets/data-D76--TYs.js +1 -0
- package/payload/server/public/assets/{file-text-c6cA4Evd.js → file-text-zch0E1GJ.js} +1 -1
- package/payload/server/public/assets/{graph-DIk-o45J.js → graph-BavHLzpV.js} +1 -1
- package/payload/server/public/assets/{graph-labels-5bjxhC_p.js → graph-labels-CBwh4g-6.js} +1 -1
- package/payload/server/public/assets/{maximize-2-BCQkFb1_.js → maximize-2-bTn_Ozpi.js} +1 -1
- package/payload/server/public/assets/{operator-0UnNo2ms.js → operator-a4IOwf62.js} +1 -1
- package/payload/server/public/assets/page-3T2mE63b.js +1 -0
- package/payload/server/public/assets/{page-HVtUfub2.js → page-D6M1CmPe.js} +4 -4
- package/payload/server/public/assets/{public-CKEYyIPn.js → public-B3y_YU1G.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-dTpO5dhL.js → rotate-ccw-BGvzNB1b.js} +1 -1
- package/payload/server/public/assets/{routines-C6P1-Dto.js → routines-C7Dr16am.js} +1 -1
- package/payload/server/public/assets/{skills-ClrpVqlz.js → skills-DFeb3XO3.js} +1 -1
- package/payload/server/public/assets/{tasks-C8SNhSFC.js → tasks-CSfnCKJe.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-CCO6N6XZ.js → time-entry-format-BgZqOOqG.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-CHjdKpag.js → triangle-alert-DBJ7xz_t.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-BTUu3-l8.js → useCopyFeedback-BFOujMl-.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-BkQ5tA8A.css +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-BeNqxmm2.js → useVoiceRecorder-BP6CpYQD.js} +1 -1
- package/payload/server/public/assets/{wrench-CeNnc7oO.js → wrench-DdaRhfzS.js} +1 -1
- package/payload/server/public/brand/fonts/inter-400.woff2 +0 -0
- package/payload/server/public/brand/fonts/inter-500.woff2 +0 -0
- package/payload/server/public/brand-defaults.css +47 -4
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +7 -7
- package/payload/server/public/chat.html +13 -13
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +9 -9
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +13 -13
- package/payload/server/public/routines.html +6 -6
- package/payload/server/public/skills.html +5 -5
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +702 -586
- package/payload/server/public/assets/admin-B-EGUWS4.js +0 -1
- package/payload/server/public/assets/chat-DWLYAA70.js +0 -1
- package/payload/server/public/assets/chevron-left-DBMCTKlT.js +0 -1
- package/payload/server/public/assets/chevron-right-SBAr6zzc.js +0 -1
- package/payload/server/public/assets/clock-kkzt7Scf.js +0 -1
- package/payload/server/public/assets/data-CV8N9qcy.js +0 -1
- package/payload/server/public/assets/page-JJa2xdzf.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-DyrH8DA8.css +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-DSW9vDi2.js → useSubAccountSwitcher-0PfRrN6i.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -118,7 +118,7 @@ import {
|
|
|
118
118
|
vncLog,
|
|
119
119
|
walkPremiumBundles,
|
|
120
120
|
writeAdminUserAndPerson
|
|
121
|
-
} from "./chunk-
|
|
121
|
+
} from "./chunk-2WAXM5N2.js";
|
|
122
122
|
import {
|
|
123
123
|
D1_MAX_SQL_STATEMENT_BYTES,
|
|
124
124
|
D1_QUERY_MAX_BODY_BYTES,
|
|
@@ -225,7 +225,16 @@ var require_dist2 = __commonJS({
|
|
|
225
225
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
226
226
|
exports.HOME_FIXED = void 0;
|
|
227
227
|
exports.parseSchemaRegions = parseSchemaRegions2;
|
|
228
|
-
exports.HOME_FIXED = Object.freeze([
|
|
228
|
+
exports.HOME_FIXED = Object.freeze([
|
|
229
|
+
"projects",
|
|
230
|
+
"contacts",
|
|
231
|
+
"documents",
|
|
232
|
+
"output",
|
|
233
|
+
"generated",
|
|
234
|
+
"extracted",
|
|
235
|
+
"sites",
|
|
236
|
+
"uploads"
|
|
237
|
+
]);
|
|
229
238
|
var ONT_START2 = "<!-- ontology-buckets:start -->";
|
|
230
239
|
var ONT_END2 = "<!-- ontology-buckets:end -->";
|
|
231
240
|
var PLUGIN_START = "<!-- plugin-owned-dirs:start -->";
|
|
@@ -1921,6 +1930,12 @@ function invalidate(resource, accountId, source = "action") {
|
|
|
1921
1930
|
keyGenerations.set(key, (keyGenerations.get(key) ?? 0) + 1);
|
|
1922
1931
|
markOrDrop(key);
|
|
1923
1932
|
}
|
|
1933
|
+
notifyAny(resource, accountId);
|
|
1934
|
+
}
|
|
1935
|
+
function notify(resource, accountId) {
|
|
1936
|
+
notifyAny(resource, accountId);
|
|
1937
|
+
}
|
|
1938
|
+
function notifyAny(resource, accountId) {
|
|
1924
1939
|
pendingNotifications.set(`${resource}::${accountId ?? "*"}`, { resource, accountId });
|
|
1925
1940
|
if (notifyTimer === null) {
|
|
1926
1941
|
notifyTimer = setTimeout(flushNotifications, NOTIFY_COALESCE_MS);
|
|
@@ -2277,11 +2292,102 @@ function attachRouteCacheWatchers() {
|
|
|
2277
2292
|
}
|
|
2278
2293
|
}
|
|
2279
2294
|
|
|
2295
|
+
// server/lib/manager-events-subscriber.ts
|
|
2296
|
+
var TAG = "[webchat-pointer]";
|
|
2297
|
+
var TURN_EVENTS = /* @__PURE__ */ new Set(["row-updated", "row-created"]);
|
|
2298
|
+
var MAX_BACKOFF_MS = 3e4;
|
|
2299
|
+
var STABLE_CONNECTION_MS = 3e4;
|
|
2300
|
+
var lastStatusBySession = /* @__PURE__ */ new Map();
|
|
2301
|
+
function handleManagerFrame(raw) {
|
|
2302
|
+
const lines = raw.split("\n");
|
|
2303
|
+
const eventLine = lines.find((l) => l.startsWith("event: "));
|
|
2304
|
+
const dataLine = lines.find((l) => l.startsWith("data: "));
|
|
2305
|
+
if (!eventLine || !dataLine) return null;
|
|
2306
|
+
if (!TURN_EVENTS.has(eventLine.slice("event: ".length).trim())) return null;
|
|
2307
|
+
let body;
|
|
2308
|
+
try {
|
|
2309
|
+
body = JSON.parse(dataLine.slice("data: ".length));
|
|
2310
|
+
} catch {
|
|
2311
|
+
console.error(`${TAG} op=skip reason=unparseable-frame`);
|
|
2312
|
+
return null;
|
|
2313
|
+
}
|
|
2314
|
+
const sessionId = String(body.sessionId ?? "unknown").slice(0, 8);
|
|
2315
|
+
const accountId = typeof body.accountId === "string" && body.accountId ? body.accountId : null;
|
|
2316
|
+
if (!accountId) {
|
|
2317
|
+
console.error(`${TAG} op=skip reason=no-account sessionId=${sessionId}`);
|
|
2318
|
+
return null;
|
|
2319
|
+
}
|
|
2320
|
+
const status = String(body.status ?? "unknown");
|
|
2321
|
+
const fullSessionId = String(body.sessionId ?? "");
|
|
2322
|
+
if (fullSessionId && lastStatusBySession.get(fullSessionId) === status) {
|
|
2323
|
+
return null;
|
|
2324
|
+
}
|
|
2325
|
+
if (fullSessionId) lastStatusBySession.set(fullSessionId, status);
|
|
2326
|
+
console.log(
|
|
2327
|
+
`${TAG} op=event sessionId=${sessionId} accountId=${accountId.slice(0, 8)} activity=${status}`
|
|
2328
|
+
);
|
|
2329
|
+
notify("webchat-pointer", accountId);
|
|
2330
|
+
return { resource: "webchat-pointer", accountId };
|
|
2331
|
+
}
|
|
2332
|
+
var connections = 0;
|
|
2333
|
+
var started = false;
|
|
2334
|
+
var abort = null;
|
|
2335
|
+
function startManagerEventsSubscriber() {
|
|
2336
|
+
if (started) return;
|
|
2337
|
+
started = true;
|
|
2338
|
+
abort = new AbortController();
|
|
2339
|
+
void connectLoop(abort.signal);
|
|
2340
|
+
}
|
|
2341
|
+
async function connectLoop(signal) {
|
|
2342
|
+
let backoffMs = 1e3;
|
|
2343
|
+
for (; ; ) {
|
|
2344
|
+
if (signal.aborted) return;
|
|
2345
|
+
let port2;
|
|
2346
|
+
try {
|
|
2347
|
+
port2 = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "webchat-pointer" });
|
|
2348
|
+
} catch {
|
|
2349
|
+
console.error(`${TAG} op=drop reason=manager-port-unset`);
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
const connectedAt = Date.now();
|
|
2353
|
+
try {
|
|
2354
|
+
const res = await fetch(`http://127.0.0.1:${port2}/events`, { signal });
|
|
2355
|
+
if (!res.ok || !res.body) throw new Error(`status ${res.status}`);
|
|
2356
|
+
connections += 1;
|
|
2357
|
+
console.log(`${TAG} op=subscribe upstream-count=${connections}`);
|
|
2358
|
+
const reader = res.body.getReader();
|
|
2359
|
+
const decoder = new TextDecoder();
|
|
2360
|
+
let buffer = "";
|
|
2361
|
+
for (; ; ) {
|
|
2362
|
+
const { done, value } = await reader.read();
|
|
2363
|
+
if (done) break;
|
|
2364
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2365
|
+
let cut = buffer.indexOf("\n\n");
|
|
2366
|
+
while (cut !== -1) {
|
|
2367
|
+
handleManagerFrame(buffer.slice(0, cut));
|
|
2368
|
+
buffer = buffer.slice(cut + 2);
|
|
2369
|
+
cut = buffer.indexOf("\n\n");
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
connections -= 1;
|
|
2373
|
+
} catch (err) {
|
|
2374
|
+
if (connections > 0) connections -= 1;
|
|
2375
|
+
if (signal.aborted) return;
|
|
2376
|
+
console.error(`${TAG} op=drop reason=${err instanceof Error ? err.message : String(err)}`);
|
|
2377
|
+
}
|
|
2378
|
+
if (signal.aborted) return;
|
|
2379
|
+
if (Date.now() - connectedAt >= STABLE_CONNECTION_MS) backoffMs = 1e3;
|
|
2380
|
+
console.log(`${TAG} op=reconnect inMs=${backoffMs} upstream-count=${connections}`);
|
|
2381
|
+
await new Promise((r) => setTimeout(r, backoffMs));
|
|
2382
|
+
backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2280
2386
|
// server/index.ts
|
|
2281
2387
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
2282
2388
|
|
|
2283
2389
|
// app/lib/whatsapp/inbound/media-download-guard.ts
|
|
2284
|
-
var
|
|
2390
|
+
var TAG2 = "[media-guard]";
|
|
2285
2391
|
var inFlight2 = 0;
|
|
2286
2392
|
var containedCount = 0;
|
|
2287
2393
|
function containedSinceBoot() {
|
|
@@ -2311,13 +2417,13 @@ function handleEscapedMediaError(err, channel) {
|
|
|
2311
2417
|
if (undiciFrame && inFlight2 > 0) {
|
|
2312
2418
|
containedCount += 1;
|
|
2313
2419
|
console.error(
|
|
2314
|
-
`${
|
|
2420
|
+
`${TAG2} op=contained channel=${channel} inFlight=${inFlight2} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)}
|
|
2315
2421
|
${stack}`
|
|
2316
2422
|
);
|
|
2317
2423
|
return true;
|
|
2318
2424
|
}
|
|
2319
2425
|
console.error(
|
|
2320
|
-
`${
|
|
2426
|
+
`${TAG2} op=escaped channel=${channel} inFlight=${inFlight2} undiciFrame=${undiciFrame} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)} contained=false`
|
|
2321
2427
|
);
|
|
2322
2428
|
return false;
|
|
2323
2429
|
}
|
|
@@ -2342,7 +2448,7 @@ function installMediaDownloadGuard() {
|
|
|
2342
2448
|
);
|
|
2343
2449
|
process.exit(1);
|
|
2344
2450
|
});
|
|
2345
|
-
console.error(`${
|
|
2451
|
+
console.error(`${TAG2} op=installed`);
|
|
2346
2452
|
}
|
|
2347
2453
|
|
|
2348
2454
|
// app/lib/whatsapp/ingest-audit.ts
|
|
@@ -2352,7 +2458,7 @@ import { join as join4 } from "path";
|
|
|
2352
2458
|
// app/lib/whatsapp/ingest-ledger.ts
|
|
2353
2459
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, appendFileSync as appendFileSync2, readFileSync as readFileSync3, writeFileSync, readdirSync as readdirSync3 } from "fs";
|
|
2354
2460
|
import { join as join3 } from "path";
|
|
2355
|
-
var
|
|
2461
|
+
var TAG3 = "[wa-ingest-ledger]";
|
|
2356
2462
|
var LEDGER_ROOT = () => join3(process.env.MAXY_DIR_OVERRIDE ?? MAXY_DIR, "data", "whatsapp-ingest-ledger");
|
|
2357
2463
|
var ensuredRoots = /* @__PURE__ */ new Set();
|
|
2358
2464
|
function ledgerFilePath(account) {
|
|
@@ -2374,7 +2480,7 @@ function appendStanza(entry) {
|
|
|
2374
2480
|
appendFileSync2(ledgerFilePath(entry.account), JSON.stringify(row) + "\n");
|
|
2375
2481
|
} catch (err) {
|
|
2376
2482
|
console.error(
|
|
2377
|
-
`${
|
|
2483
|
+
`${TAG3} FAIL op=append msgId=${entry.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
2378
2484
|
);
|
|
2379
2485
|
}
|
|
2380
2486
|
}
|
|
@@ -2388,7 +2494,7 @@ function markDeclined(entry) {
|
|
|
2388
2494
|
appendFileSync2(ledgerFilePath(entry.account), JSON.stringify(row) + "\n");
|
|
2389
2495
|
} catch (err) {
|
|
2390
2496
|
console.error(
|
|
2391
|
-
`${
|
|
2497
|
+
`${TAG3} FAIL op=decline msgId=${entry.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
2392
2498
|
);
|
|
2393
2499
|
}
|
|
2394
2500
|
}
|
|
@@ -2449,7 +2555,7 @@ function listLedgerAccounts() {
|
|
|
2449
2555
|
}
|
|
2450
2556
|
|
|
2451
2557
|
// app/lib/whatsapp/ingest-audit.ts
|
|
2452
|
-
var
|
|
2558
|
+
var TAG4 = "[wa-ingest-audit]";
|
|
2453
2559
|
var DEFAULT_GRACE_MS = 10 * 60 * 1e3;
|
|
2454
2560
|
var DEFAULT_RETAIN_MS = 24 * 60 * 60 * 1e3;
|
|
2455
2561
|
var idCache = /* @__PURE__ */ new Map();
|
|
@@ -2551,7 +2657,7 @@ function runIngestAudit(opts) {
|
|
|
2551
2657
|
}
|
|
2552
2658
|
result.oldestUnpersistedMin = Math.round(oldestUnpersistedAgeMs / 6e4);
|
|
2553
2659
|
console.error(
|
|
2554
|
-
`${
|
|
2660
|
+
`${TAG4} op=reconcile stanzas=${result.stanzas} persisted=${result.persisted} unpersisted=${result.unpersisted.length} ids=${result.unpersisted.join(",") || "none"} oldestUnpersistedMin=${result.oldestUnpersistedMin} skippedStatus=${result.skippedStatus} skippedReaction=${result.skippedReaction} declined=${result.declined} contained=${containedSinceBoot()} inFlight=${mediaDownloadsInFlight()} guard=${guardInstalled() ? "installed" : "absent"}`
|
|
2555
2661
|
);
|
|
2556
2662
|
return result;
|
|
2557
2663
|
}
|
|
@@ -3044,7 +3150,7 @@ import fsSync from "fs";
|
|
|
3044
3150
|
import fs from "fs/promises";
|
|
3045
3151
|
import path from "path";
|
|
3046
3152
|
import os from "os";
|
|
3047
|
-
var
|
|
3153
|
+
var TAG5 = "[whatsapp:auth]";
|
|
3048
3154
|
function resolveAuthDir(configDir2, accountId) {
|
|
3049
3155
|
return path.join(os.homedir(), configDir2, "credentials", "whatsapp", accountId);
|
|
3050
3156
|
}
|
|
@@ -3076,7 +3182,7 @@ async function authExists(authDir) {
|
|
|
3076
3182
|
JSON.parse(raw);
|
|
3077
3183
|
return true;
|
|
3078
3184
|
} catch (err) {
|
|
3079
|
-
console.warn(`${
|
|
3185
|
+
console.warn(`${TAG5} credential validation failed path=${credsPath}: ${String(err)}`);
|
|
3080
3186
|
return false;
|
|
3081
3187
|
}
|
|
3082
3188
|
}
|
|
@@ -3103,9 +3209,9 @@ function maybeRestoreCredsFromBackup(authDir) {
|
|
|
3103
3209
|
if (!backupRaw) return;
|
|
3104
3210
|
JSON.parse(backupRaw);
|
|
3105
3211
|
fsSync.copyFileSync(backupPath, credsPath);
|
|
3106
|
-
console.error(`${
|
|
3212
|
+
console.error(`${TAG5} restored corrupted creds.json from backup in ${authDir}`);
|
|
3107
3213
|
} catch (err) {
|
|
3108
|
-
console.warn(`${
|
|
3214
|
+
console.warn(`${TAG5} credential restore failed authDir=${authDir}: ${String(err)}`);
|
|
3109
3215
|
}
|
|
3110
3216
|
}
|
|
3111
3217
|
function readSelfId(authDir) {
|
|
@@ -3128,7 +3234,7 @@ function readSelfId(authDir) {
|
|
|
3128
3234
|
}
|
|
3129
3235
|
return { e164, jid, lid };
|
|
3130
3236
|
} catch (err) {
|
|
3131
|
-
console.warn(`${
|
|
3237
|
+
console.warn(`${TAG5} readSelfId failed authDir=${authDir}: ${String(err)}`);
|
|
3132
3238
|
return { e164: null, jid: null, lid: null };
|
|
3133
3239
|
}
|
|
3134
3240
|
}
|
|
@@ -3162,21 +3268,21 @@ async function clearAuth(authDir) {
|
|
|
3162
3268
|
try {
|
|
3163
3269
|
await fs.access(authDir);
|
|
3164
3270
|
} catch {
|
|
3165
|
-
console.error(`${
|
|
3271
|
+
console.error(`${TAG5} no auth dir found at ${authDir}`);
|
|
3166
3272
|
return false;
|
|
3167
3273
|
}
|
|
3168
3274
|
await fs.rm(authDir, { recursive: true, force: true });
|
|
3169
|
-
console.error(`${
|
|
3275
|
+
console.error(`${TAG5} cleared credentials at ${authDir}`);
|
|
3170
3276
|
return true;
|
|
3171
3277
|
}
|
|
3172
3278
|
function discardAuthDirSync(authDir) {
|
|
3173
3279
|
try {
|
|
3174
3280
|
if (!fsSync.existsSync(authDir)) return false;
|
|
3175
3281
|
fsSync.rmSync(authDir, { recursive: true, force: true });
|
|
3176
|
-
console.error(`${
|
|
3282
|
+
console.error(`${TAG5} discarded credential dir ${authDir}`);
|
|
3177
3283
|
return true;
|
|
3178
3284
|
} catch (err) {
|
|
3179
|
-
console.warn(`${
|
|
3285
|
+
console.warn(`${TAG5} discardAuthDirSync failed authDir=${authDir}: ${String(err)}`);
|
|
3180
3286
|
return false;
|
|
3181
3287
|
}
|
|
3182
3288
|
}
|
|
@@ -3208,7 +3314,7 @@ import { createRequire } from "module";
|
|
|
3208
3314
|
// app/lib/whatsapp/baileys-debug-window.ts
|
|
3209
3315
|
import fs2 from "fs";
|
|
3210
3316
|
import path2 from "path";
|
|
3211
|
-
var
|
|
3317
|
+
var TAG6 = "[whatsapp:session]";
|
|
3212
3318
|
var SENTINEL_BASENAME = "baileys-debug-until";
|
|
3213
3319
|
var STAT_INTERVAL_MS = 1e3;
|
|
3214
3320
|
var VALID_LEVELS = /* @__PURE__ */ new Set(["trace", "debug", "info"]);
|
|
@@ -3248,11 +3354,11 @@ function resolveDebugWindow(authDir, nowMs = Date.now()) {
|
|
|
3248
3354
|
const wasActive = prev?.state.active ?? false;
|
|
3249
3355
|
if (!wasActive && state.active) {
|
|
3250
3356
|
console.error(
|
|
3251
|
-
`${
|
|
3357
|
+
`${TAG6} op=baileys-debug-window-open account=${path2.basename(authDir)} level=${state.level} until=${new Date(deadlineMs).toISOString()}`
|
|
3252
3358
|
);
|
|
3253
3359
|
} else if (wasActive && !state.active) {
|
|
3254
3360
|
console.error(
|
|
3255
|
-
`${
|
|
3361
|
+
`${TAG6} op=baileys-debug-window-close account=${path2.basename(authDir)}`
|
|
3256
3362
|
);
|
|
3257
3363
|
}
|
|
3258
3364
|
cache2.set(authDir, { checkedAtMs: nowMs, state });
|
|
@@ -3260,7 +3366,7 @@ function resolveDebugWindow(authDir, nowMs = Date.now()) {
|
|
|
3260
3366
|
}
|
|
3261
3367
|
|
|
3262
3368
|
// app/lib/whatsapp/session.ts
|
|
3263
|
-
var
|
|
3369
|
+
var TAG7 = "[whatsapp:session]";
|
|
3264
3370
|
var WIRE_TAG = "[whatsapp:wire]";
|
|
3265
3371
|
var baileysPkgVersion = (() => {
|
|
3266
3372
|
try {
|
|
@@ -3270,7 +3376,7 @@ var baileysPkgVersion = (() => {
|
|
|
3270
3376
|
return "unknown";
|
|
3271
3377
|
}
|
|
3272
3378
|
})();
|
|
3273
|
-
console.error(`${
|
|
3379
|
+
console.error(`${TAG7} baileys package loaded version=${baileysPkgVersion}`);
|
|
3274
3380
|
var __BAILEYS_TAG = "[whatsapp:baileys]";
|
|
3275
3381
|
var INSPECT_OPTS = {
|
|
3276
3382
|
depth: null,
|
|
@@ -3331,7 +3437,7 @@ function createBaileysLogger(bindings = {}, authDir) {
|
|
|
3331
3437
|
}
|
|
3332
3438
|
var credsSaveQueue = Promise.resolve();
|
|
3333
3439
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
3334
|
-
console.error(`${
|
|
3440
|
+
console.error(`${TAG7} draining credential save queue\u2026`);
|
|
3335
3441
|
const timer = new Promise(
|
|
3336
3442
|
(resolve46) => setTimeout(() => resolve46("timeout"), timeoutMs)
|
|
3337
3443
|
);
|
|
@@ -3340,9 +3446,9 @@ async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
|
3340
3446
|
timer
|
|
3341
3447
|
]);
|
|
3342
3448
|
if (result === "timeout") {
|
|
3343
|
-
console.error(`${
|
|
3449
|
+
console.error(`${TAG7} credential save queue drain timed out after ${timeoutMs}ms`);
|
|
3344
3450
|
} else {
|
|
3345
|
-
console.error(`${
|
|
3451
|
+
console.error(`${TAG7} credential save queue drained`);
|
|
3346
3452
|
}
|
|
3347
3453
|
}
|
|
3348
3454
|
function readCredsJsonRaw2(filePath) {
|
|
@@ -3365,11 +3471,11 @@ async function safeSaveCreds(authDir, saveCreds) {
|
|
|
3365
3471
|
JSON.parse(raw);
|
|
3366
3472
|
fsSync2.copyFileSync(credsPath, backupPath);
|
|
3367
3473
|
} catch (err) {
|
|
3368
|
-
console.warn(`${
|
|
3474
|
+
console.warn(`${TAG7} backup pre-copy failed (parse or write) authDir=${authDir}: ${String(err)}`);
|
|
3369
3475
|
}
|
|
3370
3476
|
}
|
|
3371
3477
|
} catch (err) {
|
|
3372
|
-
console.warn(`${
|
|
3478
|
+
console.warn(`${TAG7} backup preparation failed authDir=${authDir}: ${String(err)}`);
|
|
3373
3479
|
}
|
|
3374
3480
|
try {
|
|
3375
3481
|
await Promise.resolve(saveCreds());
|
|
@@ -3377,20 +3483,20 @@ async function safeSaveCreds(authDir, saveCreds) {
|
|
|
3377
3483
|
try {
|
|
3378
3484
|
const stats = await fs3.stat(credsPath);
|
|
3379
3485
|
if (stats.size <= 1) {
|
|
3380
|
-
console.error(`${
|
|
3486
|
+
console.error(`${TAG7} WARNING: creds file empty/truncated after save path=${credsPath} size=${stats.size}`);
|
|
3381
3487
|
} else {
|
|
3382
|
-
console.error(`${
|
|
3488
|
+
console.error(`${TAG7} creds saved and verified path=${credsPath} size=${stats.size}`);
|
|
3383
3489
|
}
|
|
3384
3490
|
} catch (statErr) {
|
|
3385
|
-
console.error(`${
|
|
3491
|
+
console.error(`${TAG7} creds NOT found on disk after save path=${credsPath}: ${String(statErr)}`);
|
|
3386
3492
|
}
|
|
3387
3493
|
} catch (err) {
|
|
3388
|
-
console.error(`${
|
|
3494
|
+
console.error(`${TAG7} failed saving creds to ${authDir}: ${String(err)}`);
|
|
3389
3495
|
}
|
|
3390
3496
|
}
|
|
3391
3497
|
function enqueueSaveCreds(authDir, saveCreds) {
|
|
3392
3498
|
credsSaveQueue = credsSaveQueue.then(() => safeSaveCreds(authDir, saveCreds)).catch((err) => {
|
|
3393
|
-
console.error(`${
|
|
3499
|
+
console.error(`${TAG7} creds save queue error: ${String(err)}`);
|
|
3394
3500
|
});
|
|
3395
3501
|
}
|
|
3396
3502
|
async function createWaSocket(opts) {
|
|
@@ -3404,12 +3510,12 @@ async function createWaSocket(opts) {
|
|
|
3404
3510
|
if (!silent) {
|
|
3405
3511
|
const preCensus = credsCensus(authDir);
|
|
3406
3512
|
console.error(
|
|
3407
|
-
`${
|
|
3513
|
+
`${TAG7} op=creds-pre cid=${cid} account=${acct} credsPresent=${preCensus.credsPresent} registered=${preCensus.registered} hasAccount=${preCensus.hasAccountSignature}`
|
|
3408
3514
|
);
|
|
3409
3515
|
}
|
|
3410
3516
|
const openTs = Date.now();
|
|
3411
3517
|
console.error(
|
|
3412
|
-
`${
|
|
3518
|
+
`${TAG7} op=socket-open cid=${cid} account=${acct} baileysVersion=${baileysPkgVersion} waProtocolVersion=${version.join(".")} openTs=${openTs}`
|
|
3413
3519
|
);
|
|
3414
3520
|
const baileysLogger = createBaileysLogger({}, authDir);
|
|
3415
3521
|
const sock = makeWASocket({
|
|
@@ -3426,7 +3532,7 @@ async function createWaSocket(opts) {
|
|
|
3426
3532
|
fireInitQueries: false
|
|
3427
3533
|
});
|
|
3428
3534
|
sock.ev.on("creds.update", () => {
|
|
3429
|
-
console.error(`${
|
|
3535
|
+
console.error(`${TAG7} creds.update received \u2014 saving to ${authDir}`);
|
|
3430
3536
|
enqueueSaveCreds(authDir, saveCreds);
|
|
3431
3537
|
});
|
|
3432
3538
|
let qrSequence = 0;
|
|
@@ -3446,14 +3552,14 @@ async function createWaSocket(opts) {
|
|
|
3446
3552
|
parts.push(`disconnectReason=${formatError(update.lastDisconnect.error)}`);
|
|
3447
3553
|
}
|
|
3448
3554
|
if (!silent && parts.length > 0) {
|
|
3449
|
-
console.error(`${
|
|
3555
|
+
console.error(`${TAG7} connection.update ${parts.join(" ")}`);
|
|
3450
3556
|
}
|
|
3451
3557
|
if (update.qr && !silent) {
|
|
3452
3558
|
const now = Date.now();
|
|
3453
3559
|
const sinceLastRefMs = lastQrTs ? now - lastQrTs : null;
|
|
3454
3560
|
lastQrTs = now;
|
|
3455
3561
|
console.error(
|
|
3456
|
-
`${
|
|
3562
|
+
`${TAG7} op=qr cid=${cid} qr=#${qrSequence} sinceOpenMs=${now - openTs} sinceLastRefMs=${sinceLastRefMs ?? "n/a"}`
|
|
3457
3563
|
);
|
|
3458
3564
|
}
|
|
3459
3565
|
if (update.isNewLogin === true && !pairSuccessLogged && !silent) {
|
|
@@ -3462,7 +3568,7 @@ async function createWaSocket(opts) {
|
|
|
3462
3568
|
const sinceCodeIssuedMs = codeIssuedTs ? Date.now() - codeIssuedTs : null;
|
|
3463
3569
|
const afterCensus = credsCensus(authDir);
|
|
3464
3570
|
console.error(
|
|
3465
|
-
`${
|
|
3571
|
+
`${TAG7} op=pair-success cid=${cid} account=${acct} sinceCodeIssuedMs=${sinceCodeIssuedMs ?? "n/a"} hasAccountAfter=${afterCensus.hasAccountSignature}`
|
|
3466
3572
|
);
|
|
3467
3573
|
}
|
|
3468
3574
|
if (update.qr && onQr) {
|
|
@@ -3472,25 +3578,25 @@ async function createWaSocket(opts) {
|
|
|
3472
3578
|
const status = getStatusCode(update.lastDisconnect?.error);
|
|
3473
3579
|
if (!silent) {
|
|
3474
3580
|
console.error(
|
|
3475
|
-
`${
|
|
3581
|
+
`${TAG7} op=close cid=${cid} account=${acct} status=${status ?? "unknown"} reason="${formatError(update.lastDisconnect?.error)}" socketLifeMs=${Date.now() - openTs} kind=${closeKind(status, everOpen)}`
|
|
3476
3582
|
);
|
|
3477
3583
|
}
|
|
3478
3584
|
if (status === DisconnectReason.loggedOut && !silent) {
|
|
3479
|
-
console.error(`${
|
|
3585
|
+
console.error(`${TAG7} session logged out (401) \u2014 re-link required`);
|
|
3480
3586
|
}
|
|
3481
3587
|
}
|
|
3482
3588
|
if (update.connection === "open") {
|
|
3483
3589
|
everOpen = true;
|
|
3484
|
-
if (!silent) console.error(`${
|
|
3590
|
+
if (!silent) console.error(`${TAG7} connected`);
|
|
3485
3591
|
}
|
|
3486
3592
|
onConnectionUpdate?.(update);
|
|
3487
3593
|
} catch (err) {
|
|
3488
|
-
console.error(`${
|
|
3594
|
+
console.error(`${TAG7} connection.update handler error: ${String(err)}`);
|
|
3489
3595
|
}
|
|
3490
3596
|
});
|
|
3491
3597
|
if (sock.ws && typeof sock.ws.on === "function") {
|
|
3492
3598
|
sock.ws.on("error", (err) => {
|
|
3493
|
-
console.error(`${
|
|
3599
|
+
console.error(`${TAG7} WebSocket error: ${String(err)}`);
|
|
3494
3600
|
});
|
|
3495
3601
|
sock.ws.on("CB:message", (node) => {
|
|
3496
3602
|
const attrs = node?.attrs ?? {};
|
|
@@ -3643,7 +3749,7 @@ function withTimeout(label, promise, timeoutMs) {
|
|
|
3643
3749
|
}
|
|
3644
3750
|
|
|
3645
3751
|
// app/lib/whatsapp/init-queries.ts
|
|
3646
|
-
var
|
|
3752
|
+
var TAG8 = "[whatsapp:init]";
|
|
3647
3753
|
var DEFAULT_QUERY_TIMEOUT_MS = 15e3;
|
|
3648
3754
|
var INSPECT_OPTS2 = {
|
|
3649
3755
|
depth: null,
|
|
@@ -3671,20 +3777,20 @@ async function runInitQueries(sock, ctx) {
|
|
|
3671
3777
|
elapsedMs: 0
|
|
3672
3778
|
};
|
|
3673
3779
|
console.error(
|
|
3674
|
-
`${
|
|
3780
|
+
`${TAG8} runInitQueries start account=${ctx.accountId} attempt=${ctx.attempt} selfPhone=${ctx.selfPhone ?? "unknown"} authDir=${ctx.authDir} queryTimeoutMs=${queryTimeoutMs}`
|
|
3675
3781
|
);
|
|
3676
3782
|
console.error(
|
|
3677
|
-
`${
|
|
3783
|
+
`${TAG8} fetchProps SKIPPED account=${ctx.accountId} reason=no-platform-consumer`
|
|
3678
3784
|
);
|
|
3679
3785
|
if (ctx.signal?.aborted) {
|
|
3680
3786
|
result.aborted = true;
|
|
3681
3787
|
result.elapsedMs = Date.now() - startedAt;
|
|
3682
3788
|
console.error(
|
|
3683
|
-
`${
|
|
3789
|
+
`${TAG8} runInitQueries aborted before start account=${ctx.accountId}`
|
|
3684
3790
|
);
|
|
3685
3791
|
return result;
|
|
3686
3792
|
}
|
|
3687
|
-
console.error(`${
|
|
3793
|
+
console.error(`${TAG8} fetchBlocklist start account=${ctx.accountId}`);
|
|
3688
3794
|
try {
|
|
3689
3795
|
const blocklist = await withTimeout(
|
|
3690
3796
|
"fetchBlocklist",
|
|
@@ -3694,24 +3800,24 @@ async function runInitQueries(sock, ctx) {
|
|
|
3694
3800
|
const count = Array.isArray(blocklist) ? blocklist.length : 0;
|
|
3695
3801
|
result.blocklistOk = true;
|
|
3696
3802
|
console.error(
|
|
3697
|
-
`${
|
|
3803
|
+
`${TAG8} fetchBlocklist ok account=${ctx.accountId} count=${count}`
|
|
3698
3804
|
);
|
|
3699
3805
|
} catch (err) {
|
|
3700
3806
|
const formatted = formatErr(err);
|
|
3701
3807
|
result.errors.blocklist = formatted;
|
|
3702
3808
|
console.error(
|
|
3703
|
-
`${
|
|
3809
|
+
`${TAG8} fetchBlocklist FAILED account=${ctx.accountId}: ${formatted}`
|
|
3704
3810
|
);
|
|
3705
3811
|
}
|
|
3706
3812
|
if (ctx.signal?.aborted) {
|
|
3707
3813
|
result.aborted = true;
|
|
3708
3814
|
result.elapsedMs = Date.now() - startedAt;
|
|
3709
3815
|
console.error(
|
|
3710
|
-
`${
|
|
3816
|
+
`${TAG8} runInitQueries aborted mid-sequence account=${ctx.accountId} after=fetchBlocklist`
|
|
3711
3817
|
);
|
|
3712
3818
|
return result;
|
|
3713
3819
|
}
|
|
3714
|
-
console.error(`${
|
|
3820
|
+
console.error(`${TAG8} fetchPrivacySettings start account=${ctx.accountId}`);
|
|
3715
3821
|
try {
|
|
3716
3822
|
const settings = await withTimeout(
|
|
3717
3823
|
"fetchPrivacySettings",
|
|
@@ -3721,18 +3827,18 @@ async function runInitQueries(sock, ctx) {
|
|
|
3721
3827
|
);
|
|
3722
3828
|
result.privacySettingsOk = true;
|
|
3723
3829
|
console.error(
|
|
3724
|
-
`${
|
|
3830
|
+
`${TAG8} fetchPrivacySettings ok account=${ctx.accountId} keys=${settings ? Object.keys(settings).join(",") : ""}`
|
|
3725
3831
|
);
|
|
3726
3832
|
} catch (err) {
|
|
3727
3833
|
const formatted = formatErr(err);
|
|
3728
3834
|
result.errors.privacySettings = formatted;
|
|
3729
3835
|
console.error(
|
|
3730
|
-
`${
|
|
3836
|
+
`${TAG8} fetchPrivacySettings FAILED account=${ctx.accountId}: ${formatted}`
|
|
3731
3837
|
);
|
|
3732
3838
|
}
|
|
3733
3839
|
result.elapsedMs = Date.now() - startedAt;
|
|
3734
3840
|
console.error(
|
|
3735
|
-
`${
|
|
3841
|
+
`${TAG8} runInitQueries done account=${ctx.accountId} blocklistOk=${result.blocklistOk} privacySettingsOk=${result.privacySettingsOk} elapsedMs=${result.elapsedMs}`
|
|
3736
3842
|
);
|
|
3737
3843
|
return result;
|
|
3738
3844
|
}
|
|
@@ -4474,7 +4580,7 @@ function isDuplicateInbound(key) {
|
|
|
4474
4580
|
}
|
|
4475
4581
|
|
|
4476
4582
|
// app/lib/whatsapp/inbound/self-chat.ts
|
|
4477
|
-
var
|
|
4583
|
+
var TAG9 = "[whatsapp:self-chat]";
|
|
4478
4584
|
function normalizeJid(jid) {
|
|
4479
4585
|
const m = jid.match(/^(\d+)(?::\d+)?@(s\.whatsapp\.net|lid)$/i);
|
|
4480
4586
|
return m ? `${m[1]}@${m[2].toLowerCase()}` : jid;
|
|
@@ -4494,10 +4600,10 @@ function evaluateSelfChatCommand(input) {
|
|
|
4494
4600
|
const connAtMs = input.lastConnectedAtMs ?? 0;
|
|
4495
4601
|
const msgId = input.msgId ?? "?";
|
|
4496
4602
|
console.error(
|
|
4497
|
-
`${
|
|
4603
|
+
`${TAG9} op=seen account=${input.accountId} msgId=${msgId} tsMs=${tsMs} connAtMs=${connAtMs} fromMe=true`
|
|
4498
4604
|
);
|
|
4499
4605
|
const skip = (reason) => {
|
|
4500
|
-
console.error(`${
|
|
4606
|
+
console.error(`${TAG9} op=skip account=${input.accountId} msgId=${msgId} reason=${reason}`);
|
|
4501
4607
|
return { kind: "skip", reason };
|
|
4502
4608
|
};
|
|
4503
4609
|
if (input.msgId && isAgentSentMessage(input.msgId)) return skip("echo");
|
|
@@ -4505,13 +4611,13 @@ function evaluateSelfChatCommand(input) {
|
|
|
4505
4611
|
if (isDuplicateInbound(`${input.accountId}:${input.remoteJid}:${input.msgId}`)) return skip("duplicate");
|
|
4506
4612
|
if (!input.text) return skip("no-text");
|
|
4507
4613
|
console.error(
|
|
4508
|
-
`${
|
|
4614
|
+
`${TAG9} op=dispatch account=${input.accountId} msgId=${msgId} bytes=${Buffer.byteLength(input.text, "utf8")} agentType=admin`
|
|
4509
4615
|
);
|
|
4510
4616
|
return { kind: "dispatch", text: input.text };
|
|
4511
4617
|
}
|
|
4512
4618
|
|
|
4513
4619
|
// app/lib/whatsapp/activity.ts
|
|
4514
|
-
var
|
|
4620
|
+
var TAG10 = "[whatsapp:activity]";
|
|
4515
4621
|
var RECENT_EVENTS_MAX = 200;
|
|
4516
4622
|
var counters = /* @__PURE__ */ new Map();
|
|
4517
4623
|
var recentEvents = [];
|
|
@@ -4539,10 +4645,10 @@ function recordActivity(event) {
|
|
|
4539
4645
|
recentEvents.splice(0, recentEvents.length - RECENT_EVENTS_MAX);
|
|
4540
4646
|
}
|
|
4541
4647
|
console.error(
|
|
4542
|
-
`${
|
|
4648
|
+
`${TAG10} channel-activity: direction=${direction} account=${accountId} jid=${jid}` + (messageType ? ` type=${messageType}` : "")
|
|
4543
4649
|
);
|
|
4544
4650
|
} catch (err) {
|
|
4545
|
-
console.error(`${
|
|
4651
|
+
console.error(`${TAG10} recording failed: ${String(err)}`);
|
|
4546
4652
|
}
|
|
4547
4653
|
}
|
|
4548
4654
|
function getChannelActivity(accountId) {
|
|
@@ -4563,7 +4669,7 @@ function getChannelActivity(accountId) {
|
|
|
4563
4669
|
}
|
|
4564
4670
|
|
|
4565
4671
|
// app/lib/whatsapp/outbound/send.ts
|
|
4566
|
-
var
|
|
4672
|
+
var TAG11 = "[whatsapp:outbound]";
|
|
4567
4673
|
function buildQuoted(q2) {
|
|
4568
4674
|
return {
|
|
4569
4675
|
key: {
|
|
@@ -4584,14 +4690,14 @@ async function sendTextMessage(sock, to, text, opts) {
|
|
|
4584
4690
|
const messageId = result?.key?.id;
|
|
4585
4691
|
if (messageId) {
|
|
4586
4692
|
trackAgentSentMessage(messageId);
|
|
4587
|
-
console.error(`${
|
|
4693
|
+
console.error(`${TAG11} sent text to=${jid} id=${messageId}`);
|
|
4588
4694
|
}
|
|
4589
4695
|
if (opts?.accountId) {
|
|
4590
4696
|
recordActivity({ accountId: opts.accountId, direction: "outbound", jid, messageType: "text" });
|
|
4591
4697
|
}
|
|
4592
4698
|
return { success: true, messageId: messageId ?? void 0 };
|
|
4593
4699
|
} catch (err) {
|
|
4594
|
-
console.error(`${
|
|
4700
|
+
console.error(`${TAG11} send failed to=${to}: ${String(err)}`);
|
|
4595
4701
|
return { success: false, error: String(err) };
|
|
4596
4702
|
}
|
|
4597
4703
|
}
|
|
@@ -4668,20 +4774,20 @@ async function sendMediaMessage(sock, to, media, opts) {
|
|
|
4668
4774
|
const messageId = result?.key?.id;
|
|
4669
4775
|
if (messageId) {
|
|
4670
4776
|
trackAgentSentMessage(messageId);
|
|
4671
|
-
console.error(`${
|
|
4777
|
+
console.error(`${TAG11} sent ${media.type} to=${jid} id=${messageId}`);
|
|
4672
4778
|
}
|
|
4673
4779
|
if (opts?.accountId) {
|
|
4674
4780
|
recordActivity({ accountId: opts.accountId, direction: "outbound", jid, messageType: media.type });
|
|
4675
4781
|
}
|
|
4676
4782
|
return { success: true, messageId: messageId ?? void 0 };
|
|
4677
4783
|
} catch (err) {
|
|
4678
|
-
console.error(`${
|
|
4784
|
+
console.error(`${TAG11} send media failed to=${to}: ${String(err)}`);
|
|
4679
4785
|
return { success: false, error: String(err) };
|
|
4680
4786
|
}
|
|
4681
4787
|
}
|
|
4682
4788
|
|
|
4683
4789
|
// app/lib/whatsapp/persist-message.ts
|
|
4684
|
-
var
|
|
4790
|
+
var TAG12 = "[whatsapp-persist]";
|
|
4685
4791
|
var sessionWriteLocks = /* @__PURE__ */ new Map();
|
|
4686
4792
|
async function persistWhatsAppMessage(input) {
|
|
4687
4793
|
if (!input.body && !input.attachmentId || !input.msgKeyId) return null;
|
|
@@ -4732,14 +4838,14 @@ async function persistWhatsAppMessage(input) {
|
|
|
4732
4838
|
};
|
|
4733
4839
|
const result = await session.run(cypher, params);
|
|
4734
4840
|
if (result.records.length === 0) {
|
|
4735
|
-
console.error(`${
|
|
4841
|
+
console.error(`${TAG12} skip reason=conversation-not-found accountId=${input.platformAccountId} cacheKey=${input.cacheKey} messageId=${messageId}`);
|
|
4736
4842
|
return null;
|
|
4737
4843
|
}
|
|
4738
4844
|
const rec = result.records[0];
|
|
4739
4845
|
const senderElementId = rec.get("senderElementId");
|
|
4740
4846
|
const senderReused = rec.get("senderReused") === true;
|
|
4741
4847
|
console.error(
|
|
4742
|
-
`${
|
|
4848
|
+
`${TAG12} sender-resolved telephone=${senderTelephone} nodeKind=Person elementId=${senderElementId} reused=${senderReused}`
|
|
4743
4849
|
);
|
|
4744
4850
|
const diskRecord = {
|
|
4745
4851
|
messageId,
|
|
@@ -4761,18 +4867,18 @@ async function persistWhatsAppMessage(input) {
|
|
|
4761
4867
|
const { created } = appendMessage(input.platformAccountId, diskRecord, altDedupeRemoteJids);
|
|
4762
4868
|
const ms = Date.now() - t0;
|
|
4763
4869
|
if (!created) {
|
|
4764
|
-
console.error(`${
|
|
4870
|
+
console.error(`${TAG12} noop reason=existing-message messageId=${messageId}`);
|
|
4765
4871
|
return { messageId, created: false, senderElementId };
|
|
4766
4872
|
}
|
|
4767
4873
|
console.error(
|
|
4768
|
-
`${
|
|
4874
|
+
`${TAG12} 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}`
|
|
4769
4875
|
);
|
|
4770
4876
|
return { messageId, created: true, senderElementId };
|
|
4771
4877
|
} catch (err) {
|
|
4772
4878
|
const ms = Date.now() - t0;
|
|
4773
4879
|
const reason = sanitizeReason(err);
|
|
4774
4880
|
console.error(
|
|
4775
|
-
`${
|
|
4881
|
+
`${TAG12} FAIL accountId=${input.platformAccountId} waname=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
|
|
4776
4882
|
);
|
|
4777
4883
|
return null;
|
|
4778
4884
|
} finally {
|
|
@@ -4805,7 +4911,7 @@ function sanitizeReason(err) {
|
|
|
4805
4911
|
}
|
|
4806
4912
|
|
|
4807
4913
|
// app/lib/whatsapp/ensure-conversation.ts
|
|
4808
|
-
var
|
|
4914
|
+
var TAG13 = "[whatsapp-persist]";
|
|
4809
4915
|
async function ensureWhatsAppConversation(input) {
|
|
4810
4916
|
const t0 = Date.now();
|
|
4811
4917
|
try {
|
|
@@ -4817,19 +4923,19 @@ async function ensureWhatsAppConversation(input) {
|
|
|
4817
4923
|
const ms = Date.now() - t0;
|
|
4818
4924
|
if (!result.sessionId) {
|
|
4819
4925
|
console.error(
|
|
4820
|
-
`${
|
|
4926
|
+
`${TAG13} conversation-merged FAIL cacheKey=${input.cacheKey} reason=null-sessionId ms=${ms}`
|
|
4821
4927
|
);
|
|
4822
4928
|
return null;
|
|
4823
4929
|
}
|
|
4824
4930
|
console.error(
|
|
4825
|
-
`${
|
|
4931
|
+
`${TAG13} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp accountId=${input.platformAccountId} connectionAccountId=${input.connectionAccountId ?? input.platformAccountId} created=${result.created} ms=${ms}`
|
|
4826
4932
|
);
|
|
4827
4933
|
return { sessionId: result.sessionId, created: result.created };
|
|
4828
4934
|
} catch (err) {
|
|
4829
4935
|
const ms = Date.now() - t0;
|
|
4830
4936
|
const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
|
|
4831
4937
|
console.error(
|
|
4832
|
-
`${
|
|
4938
|
+
`${TAG13} conversation-merged FAIL cacheKey=${input.cacheKey} reason=${reason} ms=${ms}`
|
|
4833
4939
|
);
|
|
4834
4940
|
return null;
|
|
4835
4941
|
}
|
|
@@ -5391,7 +5497,7 @@ async function storeComponentArtefact(accountId, attachmentId, mimeType, content
|
|
|
5391
5497
|
}
|
|
5392
5498
|
|
|
5393
5499
|
// app/lib/whatsapp/inbound/media.ts
|
|
5394
|
-
var
|
|
5500
|
+
var TAG14 = "[whatsapp:media]";
|
|
5395
5501
|
var MEDIA_DIR = "/tmp/maxy-media";
|
|
5396
5502
|
var DOWNLOAD_TIMEOUT_MS = 45e3;
|
|
5397
5503
|
var DETACHED_GRACE_MS = 3e5;
|
|
@@ -5481,25 +5587,25 @@ async function downloadInboundMediaBuffer(msg, sock, opts) {
|
|
|
5481
5587
|
}
|
|
5482
5588
|
);
|
|
5483
5589
|
if (!buffer || buffer.length === 0) {
|
|
5484
|
-
console.error(`${
|
|
5590
|
+
console.error(`${TAG14} primary download returned empty, trying direct fallback`);
|
|
5485
5591
|
const downloadable = getDownloadableContent(content);
|
|
5486
5592
|
if (downloadable) {
|
|
5487
5593
|
try {
|
|
5488
5594
|
const stream2 = await downloadContentFromMessage(downloadable.downloadable, downloadable.mediaType);
|
|
5489
5595
|
buffer = await streamToBuffer(stream2);
|
|
5490
5596
|
} catch (fallbackErr) {
|
|
5491
|
-
console.error(`${
|
|
5597
|
+
console.error(`${TAG14} direct download fallback failed: ${String(fallbackErr)}`);
|
|
5492
5598
|
}
|
|
5493
5599
|
}
|
|
5494
5600
|
}
|
|
5495
5601
|
if (!buffer || buffer.length === 0) {
|
|
5496
|
-
console.error(`${
|
|
5602
|
+
console.error(`${TAG14} download failed: empty buffer for ${mimetype ?? "unknown"}`);
|
|
5497
5603
|
return void 0;
|
|
5498
5604
|
}
|
|
5499
5605
|
if (buffer.length > maxBytes) {
|
|
5500
5606
|
const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
|
|
5501
5607
|
const limitMB = (maxBytes / (1024 * 1024)).toFixed(0);
|
|
5502
|
-
console.error(`${
|
|
5608
|
+
console.error(`${TAG14} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
|
|
5503
5609
|
return void 0;
|
|
5504
5610
|
}
|
|
5505
5611
|
return {
|
|
@@ -5513,7 +5619,7 @@ async function downloadInboundMediaBuffer(msg, sock, opts) {
|
|
|
5513
5619
|
endMediaDownload();
|
|
5514
5620
|
return settled;
|
|
5515
5621
|
} catch (err) {
|
|
5516
|
-
console.error(`${
|
|
5622
|
+
console.error(`${TAG14} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
|
|
5517
5623
|
if (isTimeoutError(err)) {
|
|
5518
5624
|
const t = setTimeout(endMediaDownload, DETACHED_GRACE_MS);
|
|
5519
5625
|
t.unref?.();
|
|
@@ -5531,7 +5637,7 @@ async function downloadInboundMedia(msg, sock, opts) {
|
|
|
5531
5637
|
const filePath = join8(MEDIA_DIR, `${randomUUID4()}.${ext}`);
|
|
5532
5638
|
await writeFile2(filePath, res.buffer);
|
|
5533
5639
|
const sizeKB = (res.buffer.length / 1024).toFixed(0);
|
|
5534
|
-
console.error(`${
|
|
5640
|
+
console.error(`${TAG14} media downloaded type=${res.mimetype} size=${sizeKB}KB path=${filePath}`);
|
|
5535
5641
|
return { path: filePath, mimetype: res.mimetype, size: res.buffer.length };
|
|
5536
5642
|
}
|
|
5537
5643
|
async function storeInboundMediaServable(msg, sock, accountId, seed, opts) {
|
|
@@ -5540,15 +5646,15 @@ async function storeInboundMediaServable(msg, sock, accountId, seed, opts) {
|
|
|
5540
5646
|
if (attachmentExists(accountId, attachmentId)) return attachmentId;
|
|
5541
5647
|
const res = await downloadInboundMediaBufferCached(msg, sock, opts);
|
|
5542
5648
|
if (!res) {
|
|
5543
|
-
console.error(`${
|
|
5649
|
+
console.error(`${TAG14} servable-store skip reason=no-buffer id=${attachmentId}`);
|
|
5544
5650
|
return null;
|
|
5545
5651
|
}
|
|
5546
5652
|
const filename = res.filename ?? `${attachmentId}.${mimeToExt(res.mimetype)}`;
|
|
5547
5653
|
await storeMediaBuffer(accountId, attachmentId, filename, res.mimetype, res.buffer);
|
|
5548
|
-
console.error(`${
|
|
5654
|
+
console.error(`${TAG14} servable-store ok id=${attachmentId} mime=${res.mimetype} bytes=${res.buffer.length}`);
|
|
5549
5655
|
return attachmentId;
|
|
5550
5656
|
} catch (err) {
|
|
5551
|
-
console.error(`${
|
|
5657
|
+
console.error(`${TAG14} servable-store FAIL id=${attachmentId} reason=${String(err).slice(0, 120)}`);
|
|
5552
5658
|
return null;
|
|
5553
5659
|
}
|
|
5554
5660
|
}
|
|
@@ -5596,7 +5702,7 @@ function createBoundedQueue(limit, opts) {
|
|
|
5596
5702
|
}
|
|
5597
5703
|
|
|
5598
5704
|
// app/lib/whatsapp/inbound/debounce.ts
|
|
5599
|
-
var
|
|
5705
|
+
var TAG15 = "[whatsapp:debounce]";
|
|
5600
5706
|
var STT_TAG = "[whatsapp:stt-await]";
|
|
5601
5707
|
function createInboundDebouncer(opts) {
|
|
5602
5708
|
const { debounceMs, buildKey, onFlush, onError } = opts;
|
|
@@ -5627,7 +5733,7 @@ function createInboundDebouncer(opts) {
|
|
|
5627
5733
|
pending.delete(key);
|
|
5628
5734
|
const batchSize = batch.entries.length;
|
|
5629
5735
|
try {
|
|
5630
|
-
console.error(`${
|
|
5736
|
+
console.error(`${TAG15} debounce flush key=${key} batchSize=${batchSize}`);
|
|
5631
5737
|
const result = onFlush(batch.entries);
|
|
5632
5738
|
if (result && typeof result.catch === "function") {
|
|
5633
5739
|
result.catch(onError);
|
|
@@ -5707,7 +5813,7 @@ function combineInboundBatch(entries2) {
|
|
|
5707
5813
|
}
|
|
5708
5814
|
|
|
5709
5815
|
// app/lib/whatsapp/opening-hours.ts
|
|
5710
|
-
var
|
|
5816
|
+
var TAG16 = "[whatsapp:hours]";
|
|
5711
5817
|
async function isBusinessOpen(accountId) {
|
|
5712
5818
|
try {
|
|
5713
5819
|
const timezone = await resolveTimezone(accountId);
|
|
@@ -5725,7 +5831,7 @@ async function isBusinessOpen(accountId) {
|
|
|
5725
5831
|
{ accountId, dayOfWeek, previousDayOfWeek }
|
|
5726
5832
|
);
|
|
5727
5833
|
if (result.records.length === 0) {
|
|
5728
|
-
console.error(`${
|
|
5834
|
+
console.error(`${TAG16} [${accountId}] business hours check: no opening hours configured, treating as open`);
|
|
5729
5835
|
return { open: true, reason: "no opening hours configured" };
|
|
5730
5836
|
}
|
|
5731
5837
|
const specs = result.records.filter((r) => r.get("opens") != null && r.get("closes") != null).map((r) => ({
|
|
@@ -5734,7 +5840,7 @@ async function isBusinessOpen(accountId) {
|
|
|
5734
5840
|
closes: String(r.get("closes")).trim()
|
|
5735
5841
|
}));
|
|
5736
5842
|
if (specs.length === 0) {
|
|
5737
|
-
console.error(`${
|
|
5843
|
+
console.error(`${TAG16} [${accountId}] business hours check: no opening hours configured, treating as open`);
|
|
5738
5844
|
return { open: true, reason: "no opening hours configured" };
|
|
5739
5845
|
}
|
|
5740
5846
|
for (const spec of specs) {
|
|
@@ -5744,7 +5850,7 @@ async function isBusinessOpen(accountId) {
|
|
|
5744
5850
|
if (spec.opens > spec.closes && currentTime < spec.closes) {
|
|
5745
5851
|
const hoursStr = `${spec.opens}-${spec.closes} (${spec.day})`;
|
|
5746
5852
|
console.error(
|
|
5747
|
-
`${
|
|
5853
|
+
`${TAG16} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
|
|
5748
5854
|
);
|
|
5749
5855
|
return {
|
|
5750
5856
|
open: true,
|
|
@@ -5757,7 +5863,7 @@ async function isBusinessOpen(accountId) {
|
|
|
5757
5863
|
} else if (isTimeInRange(currentTime, spec.opens, spec.closes)) {
|
|
5758
5864
|
const hoursStr = `${spec.opens}-${spec.closes}`;
|
|
5759
5865
|
console.error(
|
|
5760
|
-
`${
|
|
5866
|
+
`${TAG16} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
|
|
5761
5867
|
);
|
|
5762
5868
|
return {
|
|
5763
5869
|
open: true,
|
|
@@ -5771,7 +5877,7 @@ async function isBusinessOpen(accountId) {
|
|
|
5771
5877
|
const todaySpecs = specs.filter((s) => s.day === dayOfWeek);
|
|
5772
5878
|
const allHours = todaySpecs.map((s) => `${s.opens}-${s.closes}`).join(", ") || "none today";
|
|
5773
5879
|
console.error(
|
|
5774
|
-
`${
|
|
5880
|
+
`${TAG16} [${accountId}] business hours check: closed (day=${dayOfWeek}, time=${currentTime}, hours=${allHours})`
|
|
5775
5881
|
);
|
|
5776
5882
|
return {
|
|
5777
5883
|
open: false,
|
|
@@ -5785,7 +5891,7 @@ async function isBusinessOpen(accountId) {
|
|
|
5785
5891
|
}
|
|
5786
5892
|
} catch (err) {
|
|
5787
5893
|
console.error(
|
|
5788
|
-
`${
|
|
5894
|
+
`${TAG16} [${accountId}] business hours check failed, treating as open: ${err instanceof Error ? err.message : String(err)}`
|
|
5789
5895
|
);
|
|
5790
5896
|
return { open: true, reason: "hours check failed (treating as open)" };
|
|
5791
5897
|
}
|
|
@@ -5831,7 +5937,7 @@ import { execFile } from "child_process";
|
|
|
5831
5937
|
import { unlink, stat } from "fs/promises";
|
|
5832
5938
|
import { promisify } from "util";
|
|
5833
5939
|
var execFileAsync = promisify(execFile);
|
|
5834
|
-
var
|
|
5940
|
+
var TAG17 = "[stt]";
|
|
5835
5941
|
var WHISPER_BINARY = process.env.WHISPER_BINARY ?? "/opt/whisper.cpp/build/bin/whisper-cli";
|
|
5836
5942
|
var WHISPER_MODEL = process.env.WHISPER_MODEL ?? "/opt/whisper.cpp/models/ggml-base.bin";
|
|
5837
5943
|
var WHISPER_TIMEOUT_MS = 20 * 60 * 1e3;
|
|
@@ -5850,11 +5956,11 @@ async function transcribe(audioPath, mimetype) {
|
|
|
5850
5956
|
const s = await stat(audioPath);
|
|
5851
5957
|
audioBytes = s.size;
|
|
5852
5958
|
} catch {
|
|
5853
|
-
console.error(`${
|
|
5959
|
+
console.error(`${TAG17} failed: file not readable path=${audioPath}`);
|
|
5854
5960
|
return void 0;
|
|
5855
5961
|
}
|
|
5856
5962
|
console.error(
|
|
5857
|
-
`${
|
|
5963
|
+
`${TAG17} start provider=whisper-local audio_bytes=${audioBytes} mimetype=${mimetype} path=${audioPath}`
|
|
5858
5964
|
);
|
|
5859
5965
|
const wavPath = audioPath.replace(/\.[^.]+$/, "") + ".wav";
|
|
5860
5966
|
try {
|
|
@@ -5873,7 +5979,7 @@ async function transcribe(audioPath, mimetype) {
|
|
|
5873
5979
|
], { timeout: 3e4 });
|
|
5874
5980
|
} catch (err) {
|
|
5875
5981
|
const reason = err instanceof Error ? err.message : String(err);
|
|
5876
|
-
console.error(`${
|
|
5982
|
+
console.error(`${TAG17} failed: ffmpeg conversion error=${reason}`);
|
|
5877
5983
|
return void 0;
|
|
5878
5984
|
}
|
|
5879
5985
|
try {
|
|
@@ -5889,20 +5995,20 @@ async function transcribe(audioPath, mimetype) {
|
|
|
5889
5995
|
const durationMs = Date.now() - startMs;
|
|
5890
5996
|
const { text, count: stripped } = stripSpecialTokens(stdout.trim());
|
|
5891
5997
|
if (!text) {
|
|
5892
|
-
console.error(`${
|
|
5998
|
+
console.error(`${TAG17} failed: whisper returned empty output duration_ms=${durationMs}`);
|
|
5893
5999
|
return void 0;
|
|
5894
6000
|
}
|
|
5895
6001
|
const langMatch = stderr.match(/auto-detected language:\s*(\w+)/);
|
|
5896
6002
|
const language = langMatch?.[1] ?? "unknown";
|
|
5897
6003
|
const words = text.split(/\s+/).filter(Boolean).length;
|
|
5898
6004
|
console.error(
|
|
5899
|
-
`${
|
|
6005
|
+
`${TAG17} done provider=whisper-local duration_ms=${durationMs} words=${words} stripped=${stripped} lang=${language}`
|
|
5900
6006
|
);
|
|
5901
6007
|
return { text, language, durationMs };
|
|
5902
6008
|
} catch (err) {
|
|
5903
6009
|
const durationMs = Date.now() - startMs;
|
|
5904
6010
|
const reason = err instanceof Error ? err.message : String(err);
|
|
5905
|
-
console.error(`${
|
|
6011
|
+
console.error(`${TAG17} failed provider=whisper-local duration_ms=${durationMs} error=${reason}`);
|
|
5906
6012
|
return void 0;
|
|
5907
6013
|
} finally {
|
|
5908
6014
|
unlink(wavPath).catch(() => {
|
|
@@ -5921,7 +6027,7 @@ var WaReplyError = class extends Error {
|
|
|
5921
6027
|
};
|
|
5922
6028
|
|
|
5923
6029
|
// app/lib/whatsapp/manager.ts
|
|
5924
|
-
var
|
|
6030
|
+
var TAG18 = "[whatsapp:manager]";
|
|
5925
6031
|
var MAX_RECONNECT_ATTEMPTS = 10;
|
|
5926
6032
|
var MESSAGE_STORE_MAX = 500;
|
|
5927
6033
|
var APPEND_MEDIA_CONCURRENCY = 3;
|
|
@@ -5929,7 +6035,7 @@ var APPEND_MEDIA_MAX_PENDING = 24;
|
|
|
5929
6035
|
var appendMediaQueue = createBoundedQueue(APPEND_MEDIA_CONCURRENCY, {
|
|
5930
6036
|
maxPending: APPEND_MEDIA_MAX_PENDING
|
|
5931
6037
|
});
|
|
5932
|
-
var
|
|
6038
|
+
var connections2 = /* @__PURE__ */ new Map();
|
|
5933
6039
|
var configDir = null;
|
|
5934
6040
|
var whatsAppConfig = {};
|
|
5935
6041
|
var onInboundMessage = null;
|
|
@@ -5958,7 +6064,7 @@ function storeMessage(storeKey, entry) {
|
|
|
5958
6064
|
if (entries2.length > MESSAGE_STORE_MAX) {
|
|
5959
6065
|
const trimmed = entries2.length - MESSAGE_STORE_MAX;
|
|
5960
6066
|
entries2.splice(0, trimmed);
|
|
5961
|
-
console.error(`${
|
|
6067
|
+
console.error(`${TAG18} message store trimmed for ${storeKey}: ${trimmed} oldest messages removed`);
|
|
5962
6068
|
}
|
|
5963
6069
|
}
|
|
5964
6070
|
function deriveCacheKey(input) {
|
|
@@ -6001,7 +6107,7 @@ function buildSelfChatDispatch(input) {
|
|
|
6001
6107
|
}
|
|
6002
6108
|
async function init(opts) {
|
|
6003
6109
|
if (initialized) {
|
|
6004
|
-
console.error(`${
|
|
6110
|
+
console.error(`${TAG18} already initialized`);
|
|
6005
6111
|
return;
|
|
6006
6112
|
}
|
|
6007
6113
|
configDir = opts.configDir;
|
|
@@ -6009,20 +6115,20 @@ async function init(opts) {
|
|
|
6009
6115
|
loadConfig(opts.accountConfig);
|
|
6010
6116
|
const accountIds = listCredentialAccountIds(configDir);
|
|
6011
6117
|
if (accountIds.length === 0) {
|
|
6012
|
-
console.error(`${
|
|
6118
|
+
console.error(`${TAG18} init: no stored WhatsApp credentials found`);
|
|
6013
6119
|
initialized = true;
|
|
6014
6120
|
return;
|
|
6015
6121
|
}
|
|
6016
|
-
console.error(`${
|
|
6122
|
+
console.error(`${TAG18} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
|
|
6017
6123
|
initialized = true;
|
|
6018
6124
|
for (const accountId of accountIds) {
|
|
6019
6125
|
const accountCfg = whatsAppConfig.accounts?.[accountId];
|
|
6020
6126
|
if (accountCfg?.enabled === false) {
|
|
6021
|
-
console.error(`${
|
|
6127
|
+
console.error(`${TAG18} skipping disabled account=${accountId}`);
|
|
6022
6128
|
continue;
|
|
6023
6129
|
}
|
|
6024
6130
|
startConnection(accountId).catch((err) => {
|
|
6025
|
-
console.error(`${
|
|
6131
|
+
console.error(`${TAG18} failed to auto-start account=${accountId}: ${formatError(err)}`);
|
|
6026
6132
|
});
|
|
6027
6133
|
}
|
|
6028
6134
|
}
|
|
@@ -6031,7 +6137,7 @@ async function startConnection(accountId) {
|
|
|
6031
6137
|
const authDir = resolveAuthDir(configDir, accountId);
|
|
6032
6138
|
const hasAuth = await authExists(authDir);
|
|
6033
6139
|
if (!hasAuth) {
|
|
6034
|
-
console.error(`${
|
|
6140
|
+
console.error(`${TAG18} no credentials for account=${accountId}`);
|
|
6035
6141
|
return;
|
|
6036
6142
|
}
|
|
6037
6143
|
let platformAccountId;
|
|
@@ -6063,11 +6169,11 @@ async function startConnection(accountId) {
|
|
|
6063
6169
|
debouncer: null,
|
|
6064
6170
|
lidMapping: null
|
|
6065
6171
|
};
|
|
6066
|
-
|
|
6172
|
+
connections2.set(accountId, conn);
|
|
6067
6173
|
await connectWithReconnect(conn);
|
|
6068
6174
|
}
|
|
6069
6175
|
async function stopConnection(accountId) {
|
|
6070
|
-
const conn =
|
|
6176
|
+
const conn = connections2.get(accountId);
|
|
6071
6177
|
if (!conn) return;
|
|
6072
6178
|
conn.abortController.abort();
|
|
6073
6179
|
if (conn.debouncer) {
|
|
@@ -6082,14 +6188,14 @@ async function stopConnection(accountId) {
|
|
|
6082
6188
|
conn.sock.ev.removeAllListeners("creds.update");
|
|
6083
6189
|
conn.sock.ws?.close?.();
|
|
6084
6190
|
} catch (err) {
|
|
6085
|
-
console.warn(`${
|
|
6191
|
+
console.warn(`${TAG18} socket cleanup error during stop account=${accountId}: ${String(err)}`);
|
|
6086
6192
|
}
|
|
6087
6193
|
}
|
|
6088
|
-
|
|
6089
|
-
console.error(`${
|
|
6194
|
+
connections2.delete(accountId);
|
|
6195
|
+
console.error(`${TAG18} stopped account=${accountId}`);
|
|
6090
6196
|
}
|
|
6091
6197
|
function getStatus() {
|
|
6092
|
-
return Array.from(
|
|
6198
|
+
return Array.from(connections2.values()).map((conn) => ({
|
|
6093
6199
|
accountId: conn.accountId,
|
|
6094
6200
|
accountName: conn.accountName,
|
|
6095
6201
|
connected: conn.connected,
|
|
@@ -6113,14 +6219,14 @@ function selectConnection(conns, accountId) {
|
|
|
6113
6219
|
return { ok: true, sock: conn.sock, accountId: conn.accountId };
|
|
6114
6220
|
}
|
|
6115
6221
|
function resolveSocket(accountId) {
|
|
6116
|
-
return selectConnection(Array.from(
|
|
6222
|
+
return selectConnection(Array.from(connections2.values()), accountId);
|
|
6117
6223
|
}
|
|
6118
6224
|
function selectStoreAccountKey(conns, accountId) {
|
|
6119
6225
|
const conn = findConnectionByKey(conns, accountId);
|
|
6120
6226
|
return conn ? conn.platformAccountId : accountId;
|
|
6121
6227
|
}
|
|
6122
6228
|
function resolveReadStoreKey(accountId) {
|
|
6123
|
-
return selectStoreAccountKey(Array.from(
|
|
6229
|
+
return selectStoreAccountKey(Array.from(connections2.values()), accountId);
|
|
6124
6230
|
}
|
|
6125
6231
|
function resolveInboundPersistAccount(args) {
|
|
6126
6232
|
const result = args.accessResult;
|
|
@@ -6130,7 +6236,7 @@ function resolveInboundPersistAccount(args) {
|
|
|
6130
6236
|
return args.connPlatformAccountId;
|
|
6131
6237
|
}
|
|
6132
6238
|
function resolveConversationPersistAccount(sendAccountId, counterpartyPhone) {
|
|
6133
|
-
const conn = findConnectionByKey(Array.from(
|
|
6239
|
+
const conn = findConnectionByKey(Array.from(connections2.values()), sendAccountId);
|
|
6134
6240
|
if (!conn) return sendAccountId;
|
|
6135
6241
|
const access = checkDmAccess({
|
|
6136
6242
|
senderPhone: counterpartyPhone,
|
|
@@ -6146,7 +6252,7 @@ function resolveConversationPersistAccount(sendAccountId, counterpartyPhone) {
|
|
|
6146
6252
|
});
|
|
6147
6253
|
}
|
|
6148
6254
|
function listConnectionPlatformAccountIds() {
|
|
6149
|
-
return [...
|
|
6255
|
+
return [...connections2.values()].map((conn) => conn.platformAccountId);
|
|
6150
6256
|
}
|
|
6151
6257
|
async function registerLoginSocket(accountId, sock, authDir) {
|
|
6152
6258
|
if (!configDir) throw new Error("WhatsApp manager not initialized");
|
|
@@ -6181,13 +6287,13 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
6181
6287
|
lidMapping: null
|
|
6182
6288
|
};
|
|
6183
6289
|
applySelfIdentity(conn, sock, "register");
|
|
6184
|
-
|
|
6290
|
+
connections2.set(accountId, conn);
|
|
6185
6291
|
monitorInbound(conn);
|
|
6186
6292
|
try {
|
|
6187
6293
|
await sock.sendPresenceUpdate("available");
|
|
6188
|
-
console.error(`${
|
|
6294
|
+
console.error(`${TAG18} presence set to available account=${accountId}`);
|
|
6189
6295
|
} catch (err) {
|
|
6190
|
-
console.error(`${
|
|
6296
|
+
console.error(`${TAG18} presence update failed account=${accountId}: ${String(err)}`);
|
|
6191
6297
|
}
|
|
6192
6298
|
await runInitQueries(sock, {
|
|
6193
6299
|
accountId,
|
|
@@ -6198,7 +6304,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
6198
6304
|
});
|
|
6199
6305
|
watchForDisconnect(conn);
|
|
6200
6306
|
attachSelfIdRefreshOnCredsUpdate(conn);
|
|
6201
|
-
console.error(`${
|
|
6307
|
+
console.error(`${TAG18} registered login socket for account=${accountId} phone=${conn.selfPhone ?? "unknown"}`);
|
|
6202
6308
|
}
|
|
6203
6309
|
function attachSelfIdRefreshOnCredsUpdate(conn) {
|
|
6204
6310
|
if (!conn.sock) return;
|
|
@@ -6240,10 +6346,10 @@ function listStoreAccountKeys() {
|
|
|
6240
6346
|
}
|
|
6241
6347
|
async function shutdown() {
|
|
6242
6348
|
await drainCredsSaveQueue();
|
|
6243
|
-
const ids = Array.from(
|
|
6349
|
+
const ids = Array.from(connections2.keys());
|
|
6244
6350
|
await Promise.all(ids.map((id) => stopConnection(id)));
|
|
6245
6351
|
initialized = false;
|
|
6246
|
-
console.error(`${
|
|
6352
|
+
console.error(`${TAG18} shutdown complete`);
|
|
6247
6353
|
}
|
|
6248
6354
|
function loadConfig(accountConfig) {
|
|
6249
6355
|
try {
|
|
@@ -6254,19 +6360,19 @@ function loadConfig(accountConfig) {
|
|
|
6254
6360
|
if (result.ok) {
|
|
6255
6361
|
whatsAppConfig = result.data;
|
|
6256
6362
|
if (result.droppedKeys.length > 0) {
|
|
6257
|
-
console.error(`${
|
|
6363
|
+
console.error(`${TAG18} config: dropped unknown keys=[${result.droppedKeys.join(",")}] preserved known config`);
|
|
6258
6364
|
}
|
|
6259
6365
|
} else {
|
|
6260
|
-
console.error(`${
|
|
6366
|
+
console.error(`${TAG18} config validation failed: ${result.error.message}`);
|
|
6261
6367
|
whatsAppConfig = {};
|
|
6262
6368
|
}
|
|
6263
6369
|
const diskAdmin = Array.isArray(wa.adminPhones) ? wa.adminPhones : [];
|
|
6264
6370
|
if (diskAdmin.length > 0 && (whatsAppConfig.adminPhones?.length ?? 0) === 0) {
|
|
6265
|
-
console.error(`${
|
|
6371
|
+
console.error(`${TAG18} WARN adminPhones present on disk but empty after load`);
|
|
6266
6372
|
}
|
|
6267
6373
|
}
|
|
6268
6374
|
} catch (err) {
|
|
6269
|
-
console.error(`${
|
|
6375
|
+
console.error(`${TAG18} config load error: ${String(err)}`);
|
|
6270
6376
|
whatsAppConfig = {};
|
|
6271
6377
|
}
|
|
6272
6378
|
}
|
|
@@ -6277,7 +6383,7 @@ async function connectWithReconnect(conn) {
|
|
|
6277
6383
|
let cycleError = null;
|
|
6278
6384
|
let connectedAt;
|
|
6279
6385
|
try {
|
|
6280
|
-
console.error(`${
|
|
6386
|
+
console.error(`${TAG18} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
|
|
6281
6387
|
if (conn.debouncer) {
|
|
6282
6388
|
await conn.debouncer.destroy();
|
|
6283
6389
|
conn.debouncer = null;
|
|
@@ -6290,7 +6396,7 @@ async function connectWithReconnect(conn) {
|
|
|
6290
6396
|
conn.sock = sock;
|
|
6291
6397
|
applySelfIdentity(conn, sock, "pre-listener");
|
|
6292
6398
|
monitorInbound(conn);
|
|
6293
|
-
console.error(`${
|
|
6399
|
+
console.error(`${TAG18} socket created account=${conn.accountId} \u2014 waiting for connection`);
|
|
6294
6400
|
await waitForConnection(sock);
|
|
6295
6401
|
connectedAt = Date.now();
|
|
6296
6402
|
conn.connected = true;
|
|
@@ -6298,12 +6404,12 @@ async function connectWithReconnect(conn) {
|
|
|
6298
6404
|
conn.lastError = void 0;
|
|
6299
6405
|
conn.terminalReason = void 0;
|
|
6300
6406
|
applySelfIdentity(conn, sock, "post-connect");
|
|
6301
|
-
console.error(`${
|
|
6407
|
+
console.error(`${TAG18} connected account=${conn.accountId} phone=${conn.selfPhone ?? "unknown"}`);
|
|
6302
6408
|
try {
|
|
6303
6409
|
await sock.sendPresenceUpdate("available");
|
|
6304
|
-
console.error(`${
|
|
6410
|
+
console.error(`${TAG18} presence set to available account=${conn.accountId}`);
|
|
6305
6411
|
} catch (err) {
|
|
6306
|
-
console.error(`${
|
|
6412
|
+
console.error(`${TAG18} presence update failed account=${conn.accountId}: ${String(err)}`);
|
|
6307
6413
|
}
|
|
6308
6414
|
await runInitQueries(sock, {
|
|
6309
6415
|
accountId: conn.accountId,
|
|
@@ -6326,10 +6432,10 @@ async function connectWithReconnect(conn) {
|
|
|
6326
6432
|
}
|
|
6327
6433
|
const classification = classifyDisconnect(err);
|
|
6328
6434
|
conn.lastError = classification.message;
|
|
6329
|
-
console.error(`${
|
|
6435
|
+
console.error(`${TAG18} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
|
|
6330
6436
|
if (!classification.shouldRetry) {
|
|
6331
6437
|
conn.terminalReason = classification.message;
|
|
6332
|
-
console.error(`${
|
|
6438
|
+
console.error(`${TAG18} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
|
|
6333
6439
|
return;
|
|
6334
6440
|
}
|
|
6335
6441
|
}
|
|
@@ -6342,7 +6448,7 @@ async function connectWithReconnect(conn) {
|
|
|
6342
6448
|
if (decision.action === "abort") {
|
|
6343
6449
|
const stuckReason = `GIVING UP account=${conn.accountId} attempts=${decision.finalAttempts}/${maxAttempts} uptimeMsLast=${uptimeMs} stableThresholdMs=${STABLE_UPTIME_MS} lastError=${conn.lastError ?? "(none)"}`;
|
|
6344
6450
|
console.error(
|
|
6345
|
-
`${
|
|
6451
|
+
`${TAG18} ${stuckReason} \u2014 re-pair via QR or restart required; WhatsApp will not reconnect automatically`
|
|
6346
6452
|
);
|
|
6347
6453
|
conn.sessionStuckReason = stuckReason;
|
|
6348
6454
|
conn.lastError = stuckReason;
|
|
@@ -6351,17 +6457,17 @@ async function connectWithReconnect(conn) {
|
|
|
6351
6457
|
conn.reconnectAttempts = decision.nextAttempts;
|
|
6352
6458
|
if (decision.reason === "short-lived") {
|
|
6353
6459
|
console.error(
|
|
6354
|
-
`${
|
|
6460
|
+
`${TAG18} short-lived session account=${conn.accountId} uptimeMs=${uptimeMs} attempt=${decision.nextAttempts}/${maxAttempts} lastError=${conn.lastError ?? "(clean disconnect)"}`
|
|
6355
6461
|
);
|
|
6356
6462
|
} else {
|
|
6357
6463
|
console.error(
|
|
6358
|
-
`${
|
|
6464
|
+
`${TAG18} session stable account=${conn.accountId} uptimeMs=${uptimeMs} \u2014 reconnect counter reset`
|
|
6359
6465
|
);
|
|
6360
6466
|
}
|
|
6361
6467
|
if (decision.reason === "short-lived" || cycleError) {
|
|
6362
6468
|
const delay = computeBackoff(Math.max(1, decision.nextAttempts));
|
|
6363
6469
|
console.error(
|
|
6364
|
-
`${
|
|
6470
|
+
`${TAG18} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
6365
6471
|
);
|
|
6366
6472
|
await new Promise((resolve46) => {
|
|
6367
6473
|
const timer = setTimeout(resolve46, delay);
|
|
@@ -6394,12 +6500,12 @@ function watchForDisconnect(conn) {
|
|
|
6394
6500
|
if (!conn.sock) return;
|
|
6395
6501
|
conn.sock.ev.on("connection.update", (update) => {
|
|
6396
6502
|
if (update.connection === "close") {
|
|
6397
|
-
if (
|
|
6398
|
-
console.error(`${
|
|
6503
|
+
if (connections2.get(conn.accountId) !== conn) return;
|
|
6504
|
+
console.error(`${TAG18} socket disconnected for account=${conn.accountId}`);
|
|
6399
6505
|
conn.connected = false;
|
|
6400
6506
|
conn.sock = null;
|
|
6401
6507
|
connectWithReconnect(conn).catch((err) => {
|
|
6402
|
-
console.error(`${
|
|
6508
|
+
console.error(`${TAG18} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
|
|
6403
6509
|
});
|
|
6404
6510
|
}
|
|
6405
6511
|
});
|
|
@@ -6425,7 +6531,7 @@ function monitorInbound(conn) {
|
|
|
6425
6531
|
if (!conn.sock || !onInboundMessage) return;
|
|
6426
6532
|
const sock = conn.sock;
|
|
6427
6533
|
const debounceMs = whatsAppConfig.accounts?.[conn.accountId]?.debounceMs ?? whatsAppConfig.debounceMs ?? 0;
|
|
6428
|
-
console.error(`${
|
|
6534
|
+
console.error(`${TAG18} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
|
|
6429
6535
|
conn.debouncer = createInboundDebouncer({
|
|
6430
6536
|
debounceMs,
|
|
6431
6537
|
buildKey: (payload) => {
|
|
@@ -6435,12 +6541,12 @@ function monitorInbound(conn) {
|
|
|
6435
6541
|
onFlush: (entries2) => {
|
|
6436
6542
|
if (!onInboundMessage) return;
|
|
6437
6543
|
if (entries2.length > 1) {
|
|
6438
|
-
console.error(`${
|
|
6544
|
+
console.error(`${TAG18} debounce: combining ${entries2.length} messages account=${conn.accountId} from=${entries2[0].senderPhone}`);
|
|
6439
6545
|
}
|
|
6440
6546
|
dispatchInboundPayload(conn, combineInboundBatch(entries2));
|
|
6441
6547
|
},
|
|
6442
6548
|
onError: (err) => {
|
|
6443
|
-
console.error(`${
|
|
6549
|
+
console.error(`${TAG18} debounce flush error account=${conn.accountId}: ${String(err)}`);
|
|
6444
6550
|
}
|
|
6445
6551
|
});
|
|
6446
6552
|
sock.ev.on("messages.upsert", async (upsert) => {
|
|
@@ -6494,7 +6600,7 @@ function monitorInbound(conn) {
|
|
|
6494
6600
|
}
|
|
6495
6601
|
const entries2 = messageStore.get(storeKey);
|
|
6496
6602
|
console.error(
|
|
6497
|
-
`${
|
|
6603
|
+
`${TAG18} stored message ${msg.key.id ?? "?"} for ${remoteJid} (type: ${upsert.type}, store size: ${entries2?.length ?? 0}/${MESSAGE_STORE_MAX}) storeAccount=${persistAccountId} socketOwner=${conn.platformAccountId}`
|
|
6498
6604
|
);
|
|
6499
6605
|
recordActivity({
|
|
6500
6606
|
accountId: conn.accountId,
|
|
@@ -6548,7 +6654,7 @@ function monitorInbound(conn) {
|
|
|
6548
6654
|
const wantsServableMedia = mediaServable && !!conn.sock && (!fromMe || !alreadyRecorded);
|
|
6549
6655
|
if (fromMe && mediaServable && !!conn.sock) {
|
|
6550
6656
|
console.error(
|
|
6551
|
-
`${
|
|
6657
|
+
`${TAG18} fromMe-media-echo msgKey=${msg.key.id} decision=${alreadyRecorded ? "skip" : "store"} reason=${alreadyRecorded ? "already-recorded" : "no-prior-record"}`
|
|
6552
6658
|
);
|
|
6553
6659
|
}
|
|
6554
6660
|
const mediaSeed = `whatsapp-live:${conn.accountId}:${canonicalRemoteJid}:${msg.key.id}`;
|
|
@@ -6600,7 +6706,7 @@ function monitorInbound(conn) {
|
|
|
6600
6706
|
receiptDeferredToWorker = true;
|
|
6601
6707
|
if (!accepted) {
|
|
6602
6708
|
console.error(
|
|
6603
|
-
`${
|
|
6709
|
+
`${TAG18} append-media overflow account=${conn.platformAccountId} msgKey=${msgKeyId ?? "?"} \u2014 pending cap ${APPEND_MEDIA_MAX_PENDING} reached, text-fallback persist, inline media shed`
|
|
6604
6710
|
);
|
|
6605
6711
|
const persisted = await persistWhatsAppMessage({
|
|
6606
6712
|
...basePersist,
|
|
@@ -6676,13 +6782,13 @@ function monitorInbound(conn) {
|
|
|
6676
6782
|
const skippedSenderJid = isGroupJid(remoteJid) ? msg.key.participant ?? remoteJid : remoteJid;
|
|
6677
6783
|
const skippedSenderPhone = await resolveJidToE164(skippedSenderJid, conn.lidMapping) ?? skippedSenderJid;
|
|
6678
6784
|
console.error(
|
|
6679
|
-
`${
|
|
6785
|
+
`${TAG18} append-type message ${msg.key.id ?? "?"} dispatch skipped from=${skippedSenderPhone}${msg.pushName ? ` name=${msg.pushName}` : ""} account=${conn.accountId} (persistence not confirmed here)`
|
|
6680
6786
|
);
|
|
6681
6787
|
continue;
|
|
6682
6788
|
}
|
|
6683
6789
|
await handleInboundMessage(conn, msg);
|
|
6684
6790
|
} catch (err) {
|
|
6685
|
-
console.error(`${
|
|
6791
|
+
console.error(`${TAG18} inbound handler error account=${conn.accountId}: ${String(err)}`);
|
|
6686
6792
|
}
|
|
6687
6793
|
}
|
|
6688
6794
|
});
|
|
@@ -6738,31 +6844,31 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6738
6844
|
const remoteJid = msg.key.remoteJid;
|
|
6739
6845
|
if (!remoteJid) return;
|
|
6740
6846
|
if (remoteJid === "status@broadcast") {
|
|
6741
|
-
console.error(`${
|
|
6847
|
+
console.error(`${TAG18} drop: status broadcast account=${conn.accountId}`);
|
|
6742
6848
|
return;
|
|
6743
6849
|
}
|
|
6744
6850
|
if (!msg.message) {
|
|
6745
|
-
console.error(`${
|
|
6851
|
+
console.error(`${TAG18} drop: empty message account=${conn.accountId} from=${remoteJid}`);
|
|
6746
6852
|
return;
|
|
6747
6853
|
}
|
|
6748
6854
|
const dedupKey = `${conn.accountId}:${remoteJid}:${msg.key.id}`;
|
|
6749
6855
|
if (isDuplicateInbound(dedupKey)) {
|
|
6750
|
-
console.error(`${
|
|
6856
|
+
console.error(`${TAG18} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
|
|
6751
6857
|
return;
|
|
6752
6858
|
}
|
|
6753
6859
|
if (msg.key.fromMe) {
|
|
6754
6860
|
if (msg.key.id && isAgentSentMessage(msg.key.id)) {
|
|
6755
|
-
console.error(`${
|
|
6861
|
+
console.error(`${TAG18} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
|
|
6756
6862
|
return;
|
|
6757
6863
|
}
|
|
6758
6864
|
const extracted2 = extractMessage(msg);
|
|
6759
6865
|
if (!extracted2.text) {
|
|
6760
|
-
console.error(`${
|
|
6866
|
+
console.error(`${TAG18} owner reply skipped \u2014 no text content account=${conn.accountId}`);
|
|
6761
6867
|
return;
|
|
6762
6868
|
}
|
|
6763
6869
|
const isGroup2 = isGroupJid(remoteJid);
|
|
6764
6870
|
const senderPhone2 = conn.selfPhone ?? "owner";
|
|
6765
|
-
console.error(`${
|
|
6871
|
+
console.error(`${TAG18} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
|
|
6766
6872
|
const reply2 = async (text) => {
|
|
6767
6873
|
const currentSock = conn.sock;
|
|
6768
6874
|
if (!currentSock) throw new Error("WhatsApp disconnected \u2014 cannot reply");
|
|
@@ -6796,7 +6902,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6796
6902
|
}
|
|
6797
6903
|
const extracted = extractMessage(msg);
|
|
6798
6904
|
if (!extracted.text && !extracted.mediaType) {
|
|
6799
|
-
console.error(`${
|
|
6905
|
+
console.error(`${TAG18} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
|
|
6800
6906
|
return;
|
|
6801
6907
|
}
|
|
6802
6908
|
const isGroup = isGroupJid(remoteJid);
|
|
@@ -6820,7 +6926,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6820
6926
|
});
|
|
6821
6927
|
}
|
|
6822
6928
|
console.error(
|
|
6823
|
-
`${
|
|
6929
|
+
`${TAG18} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
|
|
6824
6930
|
);
|
|
6825
6931
|
if (!accessResult.allowed && accessResult.reason === "account-manager-unresolved") {
|
|
6826
6932
|
console.error(
|
|
@@ -6851,7 +6957,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6851
6957
|
maxBytes: maxMb * 1024 * 1024
|
|
6852
6958
|
});
|
|
6853
6959
|
if (!mediaResult) {
|
|
6854
|
-
console.error(`${
|
|
6960
|
+
console.error(`${TAG18} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
|
|
6855
6961
|
}
|
|
6856
6962
|
}
|
|
6857
6963
|
if (extracted.mediaType === "audio" && mediaResult?.path) {
|
|
@@ -6873,7 +6979,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6873
6979
|
}
|
|
6874
6980
|
const reply = async (text) => {
|
|
6875
6981
|
if (isGroupJid(remoteJid)) {
|
|
6876
|
-
console.error(`${
|
|
6982
|
+
console.error(`${TAG18} op=group-reply-blocked jid=${remoteJid}`);
|
|
6877
6983
|
throw new WaReplyError("group reply blocked \u2014 observe-only", false);
|
|
6878
6984
|
}
|
|
6879
6985
|
const currentSock = conn.sock;
|
|
@@ -6920,15 +7026,15 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6920
7026
|
if (accessResult.agentType === "public") {
|
|
6921
7027
|
const hoursResult = await isBusinessOpen(conn.accountId);
|
|
6922
7028
|
if (!hoursResult.open) {
|
|
6923
|
-
console.error(`${
|
|
7029
|
+
console.error(`${TAG18} [${conn.accountId}] dispatch skipped: business closed`);
|
|
6924
7030
|
const afterHoursMessage = whatsAppConfig.accounts?.[conn.accountId]?.afterHoursMessage ?? whatsAppConfig.afterHoursMessage;
|
|
6925
7031
|
if (afterHoursMessage) {
|
|
6926
7032
|
try {
|
|
6927
7033
|
await reply(afterHoursMessage);
|
|
6928
|
-
console.error(`${
|
|
7034
|
+
console.error(`${TAG18} [${conn.accountId}] after-hours auto-reply sent to ${remoteJid}`);
|
|
6929
7035
|
} catch (err) {
|
|
6930
7036
|
console.error(
|
|
6931
|
-
`${
|
|
7037
|
+
`${TAG18} [${conn.accountId}] after-hours auto-reply failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6932
7038
|
);
|
|
6933
7039
|
}
|
|
6934
7040
|
}
|
|
@@ -7373,13 +7479,13 @@ function audioExtension(mimeType) {
|
|
|
7373
7479
|
}
|
|
7374
7480
|
|
|
7375
7481
|
// app/lib/stt/voice-note.ts
|
|
7376
|
-
var
|
|
7482
|
+
var TAG19 = "[voice]";
|
|
7377
7483
|
async function transcribeVoiceNote(file, source) {
|
|
7378
7484
|
const startMs = Date.now();
|
|
7379
7485
|
const bytes = file.size;
|
|
7380
7486
|
const mimeType = file.type;
|
|
7381
7487
|
console.error(
|
|
7382
|
-
`${
|
|
7488
|
+
`${TAG19} recording send source=${source} duration_ms=unknown bytes=${bytes} format=${mimeType}`
|
|
7383
7489
|
);
|
|
7384
7490
|
let tempDir;
|
|
7385
7491
|
let tempPath;
|
|
@@ -7391,7 +7497,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
7391
7497
|
await writeFile3(tempPath, buffer);
|
|
7392
7498
|
} catch (err) {
|
|
7393
7499
|
const reason = err instanceof Error ? err.message : String(err);
|
|
7394
|
-
console.error(`${
|
|
7500
|
+
console.error(`${TAG19} failed source=${source} error=temp-file-write: ${reason}`);
|
|
7395
7501
|
return { ok: false, error: "Could not process voice note" };
|
|
7396
7502
|
}
|
|
7397
7503
|
try {
|
|
@@ -7399,7 +7505,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
7399
7505
|
if (!sttResult) {
|
|
7400
7506
|
const elapsed2 = Date.now() - startMs;
|
|
7401
7507
|
console.error(
|
|
7402
|
-
`${
|
|
7508
|
+
`${TAG19} failed source=${source} error=transcription-failed duration_ms=${elapsed2}`
|
|
7403
7509
|
);
|
|
7404
7510
|
return { ok: false, error: "Could not transcribe voice note. Please try again or type your message." };
|
|
7405
7511
|
}
|
|
@@ -7407,7 +7513,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
7407
7513
|
const elapsed = Date.now() - startMs;
|
|
7408
7514
|
const words = rawText.split(/\s+/).filter(Boolean).length;
|
|
7409
7515
|
console.error(
|
|
7410
|
-
`${
|
|
7516
|
+
`${TAG19} transcribed source=${source} duration_ms=${elapsed} stt_ms=${sttResult.durationMs} words=${words}`
|
|
7411
7517
|
);
|
|
7412
7518
|
return {
|
|
7413
7519
|
ok: true,
|
|
@@ -7417,7 +7523,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
7417
7523
|
const elapsed = Date.now() - startMs;
|
|
7418
7524
|
const reason = err instanceof Error ? err.message : String(err);
|
|
7419
7525
|
console.error(
|
|
7420
|
-
`${
|
|
7526
|
+
`${TAG19} failed source=${source} error=${reason} duration_ms=${elapsed}`
|
|
7421
7527
|
);
|
|
7422
7528
|
return { ok: false, error: "Voice note transcription failed. Please try again or type your message." };
|
|
7423
7529
|
} finally {
|
|
@@ -7464,7 +7570,7 @@ function composePublicSessionTitle(input) {
|
|
|
7464
7570
|
}
|
|
7465
7571
|
|
|
7466
7572
|
// app/lib/webchat/persist-customer-inbound.ts
|
|
7467
|
-
var
|
|
7573
|
+
var TAG20 = "[channel-house]";
|
|
7468
7574
|
async function persistCustomerWebchatInbound(input) {
|
|
7469
7575
|
if (!input.text) return null;
|
|
7470
7576
|
try {
|
|
@@ -7480,7 +7586,7 @@ async function persistCustomerWebchatInbound(input) {
|
|
|
7480
7586
|
);
|
|
7481
7587
|
if (!conv.sessionId) {
|
|
7482
7588
|
console.error(
|
|
7483
|
-
`${
|
|
7589
|
+
`${TAG20} op=persist-skip reason=conversation-merge-failed accountId=${input.houseAccountId} key=${input.sessionKey}`
|
|
7484
7590
|
);
|
|
7485
7591
|
return null;
|
|
7486
7592
|
}
|
|
@@ -7492,17 +7598,17 @@ async function persistCustomerWebchatInbound(input) {
|
|
|
7492
7598
|
);
|
|
7493
7599
|
if (!messageId) {
|
|
7494
7600
|
console.error(
|
|
7495
|
-
`${
|
|
7601
|
+
`${TAG20} op=persist-skip reason=message-write-failed accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)}`
|
|
7496
7602
|
);
|
|
7497
7603
|
return null;
|
|
7498
7604
|
}
|
|
7499
7605
|
console.error(
|
|
7500
|
-
`${
|
|
7606
|
+
`${TAG20} op=persist accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)} messageId=${messageId.slice(0, 8)} bytes=${Buffer.byteLength(input.text, "utf8")}`
|
|
7501
7607
|
);
|
|
7502
7608
|
return { sessionId: conv.sessionId, messageId };
|
|
7503
7609
|
} catch (err) {
|
|
7504
7610
|
console.error(
|
|
7505
|
-
`${
|
|
7611
|
+
`${TAG20} op=persist-fail accountId=${input.houseAccountId} key=${input.sessionKey} reason=${err instanceof Error ? err.message : String(err)}`
|
|
7506
7612
|
);
|
|
7507
7613
|
return null;
|
|
7508
7614
|
}
|
|
@@ -7810,7 +7916,7 @@ function parseScheduleProvenance(v) {
|
|
|
7810
7916
|
}
|
|
7811
7917
|
|
|
7812
7918
|
// server/routes/channel/schedule-inject.ts
|
|
7813
|
-
var
|
|
7919
|
+
var TAG21 = "[schedule-inject]";
|
|
7814
7920
|
function isLoopbackAddr(addr) {
|
|
7815
7921
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
7816
7922
|
}
|
|
@@ -7820,7 +7926,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7820
7926
|
const env = c.env;
|
|
7821
7927
|
const remoteAddr = env?.incoming?.socket?.remoteAddress ?? "";
|
|
7822
7928
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
7823
|
-
console.error(`${
|
|
7929
|
+
console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
7824
7930
|
return c.json({ ok: false, error: "schedule-inject-loopback-only" }, 403);
|
|
7825
7931
|
}
|
|
7826
7932
|
const xffHeader = env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -7828,7 +7934,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7828
7934
|
if (xffRaw.length > 0) {
|
|
7829
7935
|
const offender = xffRaw.split(",").map((t) => t.trim()).filter(Boolean).find((t) => !isLoopbackAddr(t));
|
|
7830
7936
|
if (offender !== void 0) {
|
|
7831
|
-
console.error(`${
|
|
7937
|
+
console.error(`${TAG21} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
7832
7938
|
return c.json({ ok: false, error: "schedule-inject-loopback-only" }, 403);
|
|
7833
7939
|
}
|
|
7834
7940
|
}
|
|
@@ -7844,23 +7950,23 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7844
7950
|
const eventId = typeof body.eventId === "string" ? body.eventId : "";
|
|
7845
7951
|
const scheduleProvenance = parseScheduleProvenance(body.scheduleProvenance);
|
|
7846
7952
|
if (channel !== "whatsapp" && channel !== "telegram" || !destination || !prompt) {
|
|
7847
|
-
console.error(`${
|
|
7953
|
+
console.error(`${TAG21} reject reason=bad-request channel=${String(channel)} hasDestination=${Boolean(destination)} hasPrompt=${Boolean(prompt)}`);
|
|
7848
7954
|
return c.json({ ok: false, error: "schedule-inject-bad-request" }, 400);
|
|
7849
7955
|
}
|
|
7850
7956
|
const account = deps.resolveAccount();
|
|
7851
7957
|
if (!account) {
|
|
7852
|
-
console.error(`${
|
|
7958
|
+
console.error(`${TAG21} reject reason=no-account eventId=${eventId}`);
|
|
7853
7959
|
return c.json({ ok: false, error: "no-account" }, 500);
|
|
7854
7960
|
}
|
|
7855
7961
|
const houseAccountId = account.accountId;
|
|
7856
|
-
console.error(`${
|
|
7962
|
+
console.error(`${TAG21} op=received eventId=${eventId} channel=${channel} destination=${destination} account=${houseAccountId}`);
|
|
7857
7963
|
if (channel === "whatsapp") {
|
|
7858
7964
|
const effectiveAccount2 = deps.effectiveAccountFor(houseAccountId, account.accountDir, destination);
|
|
7859
7965
|
if (effectiveAccount2 === null) {
|
|
7860
|
-
console.error(`${
|
|
7966
|
+
console.error(`${TAG21} op=schedule-account-manager-reject eventId=${eventId} destination=${destination} reason=unresolved-effective-account`);
|
|
7861
7967
|
return c.json({ ok: false, error: "account-manager-unresolved" }, 403);
|
|
7862
7968
|
}
|
|
7863
|
-
console.error(`${
|
|
7969
|
+
console.error(`${TAG21} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount2}`);
|
|
7864
7970
|
const sessionId2 = adminSessionIdFor(effectiveAccount2, normalizeTarget(destination) ?? destination);
|
|
7865
7971
|
const reply2 = (text) => deps.sendWhatsAppText(houseAccountId, destination, text);
|
|
7866
7972
|
try {
|
|
@@ -7882,17 +7988,17 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7882
7988
|
reply: reply2
|
|
7883
7989
|
});
|
|
7884
7990
|
} catch (err) {
|
|
7885
|
-
console.error(`${
|
|
7991
|
+
console.error(`${TAG21} op=inject-spawn eventId=${eventId} sessionId=${sessionId2} result=inject-error status=${err instanceof Error ? err.message : String(err)}`);
|
|
7886
7992
|
return c.json({ ok: false, error: "inject-error" }, 502);
|
|
7887
7993
|
}
|
|
7888
|
-
console.error(`${
|
|
7994
|
+
console.error(`${TAG21} op=inject-spawn eventId=${eventId} sessionId=${sessionId2} result=ok status=-`);
|
|
7889
7995
|
return c.json({ ok: true }, 200);
|
|
7890
7996
|
}
|
|
7891
7997
|
const effectiveAccount = houseAccountId;
|
|
7892
|
-
console.error(`${
|
|
7998
|
+
console.error(`${TAG21} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount}`);
|
|
7893
7999
|
const botToken = account.config.telegram?.adminBotToken;
|
|
7894
8000
|
if (!botToken) {
|
|
7895
|
-
console.error(`${
|
|
8001
|
+
console.error(`${TAG21} reject reason=no-admin-bot-token eventId=${eventId}`);
|
|
7896
8002
|
return c.json({ ok: false, error: "no-admin-bot-token" }, 400);
|
|
7897
8003
|
}
|
|
7898
8004
|
const chatId = Number(destination);
|
|
@@ -7911,10 +8017,10 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7911
8017
|
reply
|
|
7912
8018
|
});
|
|
7913
8019
|
} catch (err) {
|
|
7914
|
-
console.error(`${
|
|
8020
|
+
console.error(`${TAG21} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=inject-error status=${err instanceof Error ? err.message : String(err)}`);
|
|
7915
8021
|
return c.json({ ok: false, error: "inject-error" }, 502);
|
|
7916
8022
|
}
|
|
7917
|
-
console.error(`${
|
|
8023
|
+
console.error(`${TAG21} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
|
|
7918
8024
|
return c.json({ ok: true }, 200);
|
|
7919
8025
|
});
|
|
7920
8026
|
return app70;
|
|
@@ -7971,7 +8077,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
7971
8077
|
// app/lib/whatsapp/config-persist.ts
|
|
7972
8078
|
import { readFileSync as readFileSync13, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "fs";
|
|
7973
8079
|
import { resolve as resolve9, join as join10, dirname as dirname2, basename as basename2 } from "path";
|
|
7974
|
-
var
|
|
8080
|
+
var TAG22 = "[whatsapp:config]";
|
|
7975
8081
|
function configPath(accountDir) {
|
|
7976
8082
|
return resolve9(accountDir, "account.json");
|
|
7977
8083
|
}
|
|
@@ -7988,9 +8094,9 @@ function reloadManagerConfig(accountDir) {
|
|
|
7988
8094
|
try {
|
|
7989
8095
|
const config = readConfig(accountDir);
|
|
7990
8096
|
reloadConfig(config);
|
|
7991
|
-
console.error(`${
|
|
8097
|
+
console.error(`${TAG22} reloaded manager config`);
|
|
7992
8098
|
} catch (err) {
|
|
7993
|
-
console.error(`${
|
|
8099
|
+
console.error(`${TAG22} manager config reload failed: ${String(err)}`);
|
|
7994
8100
|
}
|
|
7995
8101
|
}
|
|
7996
8102
|
var E164_PATTERN = /^\+\d{7,15}$/;
|
|
@@ -8016,25 +8122,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
|
|
|
8016
8122
|
const adminPhones = wa.adminPhones;
|
|
8017
8123
|
if (!adminPhones.includes(normalized)) {
|
|
8018
8124
|
adminPhones.push(normalized);
|
|
8019
|
-
console.error(`${
|
|
8125
|
+
console.error(`${TAG22} added selfPhone=${normalized} to adminPhones`);
|
|
8020
8126
|
}
|
|
8021
8127
|
} else {
|
|
8022
|
-
console.error(`${
|
|
8128
|
+
console.error(`${TAG22} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
|
|
8023
8129
|
}
|
|
8024
8130
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
8025
8131
|
if (!parsed.success) {
|
|
8026
8132
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
8027
|
-
console.error(`${
|
|
8133
|
+
console.error(`${TAG22} validation failed after pairing: ${msg}`);
|
|
8028
8134
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
8029
8135
|
}
|
|
8030
8136
|
config.whatsapp = parsed.data;
|
|
8031
8137
|
writeConfig(accountDir, config);
|
|
8032
|
-
console.error(`${
|
|
8138
|
+
console.error(`${TAG22} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
|
|
8033
8139
|
reloadManagerConfig(accountDir);
|
|
8034
8140
|
return { ok: true };
|
|
8035
8141
|
} catch (err) {
|
|
8036
8142
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8037
|
-
console.error(`${
|
|
8143
|
+
console.error(`${TAG22} persist failed account=${accountId}: ${msg}`);
|
|
8038
8144
|
return { ok: false, error: msg };
|
|
8039
8145
|
}
|
|
8040
8146
|
}
|
|
@@ -8048,7 +8154,7 @@ function addAdminPhone(accountDir, phone) {
|
|
|
8048
8154
|
const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename2(resolve9(accountDir));
|
|
8049
8155
|
const socketOwner = resolveHouseOrSoleAccountId(dirname2(resolve9(accountDir)));
|
|
8050
8156
|
if (targetId !== socketOwner) {
|
|
8051
|
-
console.error(`${
|
|
8157
|
+
console.error(`${TAG22} add-admin-phone rejected accountId=${targetId} reason=not-socket-account`);
|
|
8052
8158
|
return {
|
|
8053
8159
|
ok: false,
|
|
8054
8160
|
reason: "not-socket-account",
|
|
@@ -8077,12 +8183,12 @@ function addAdminPhone(accountDir, phone) {
|
|
|
8077
8183
|
}
|
|
8078
8184
|
config.whatsapp = parsed.data;
|
|
8079
8185
|
writeConfig(accountDir, config);
|
|
8080
|
-
console.error(`${
|
|
8186
|
+
console.error(`${TAG22} added admin phone=${normalized}`);
|
|
8081
8187
|
reloadManagerConfig(accountDir);
|
|
8082
8188
|
return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
|
|
8083
8189
|
} catch (err) {
|
|
8084
8190
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8085
|
-
console.error(`${
|
|
8191
|
+
console.error(`${TAG22} addAdminPhone failed: ${msg}`);
|
|
8086
8192
|
return { ok: false, error: msg };
|
|
8087
8193
|
}
|
|
8088
8194
|
}
|
|
@@ -8110,12 +8216,12 @@ function removeAdminPhone(accountDir, phone) {
|
|
|
8110
8216
|
}
|
|
8111
8217
|
config.whatsapp = parsed.data;
|
|
8112
8218
|
writeConfig(accountDir, config);
|
|
8113
|
-
console.error(`${
|
|
8219
|
+
console.error(`${TAG22} removed admin phone=${normalized}`);
|
|
8114
8220
|
reloadManagerConfig(accountDir);
|
|
8115
8221
|
return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
|
|
8116
8222
|
} catch (err) {
|
|
8117
8223
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8118
|
-
console.error(`${
|
|
8224
|
+
console.error(`${TAG22} removeAdminPhone failed: ${msg}`);
|
|
8119
8225
|
return { ok: false, error: msg };
|
|
8120
8226
|
}
|
|
8121
8227
|
}
|
|
@@ -8210,13 +8316,13 @@ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
|
|
|
8210
8316
|
}
|
|
8211
8317
|
config.whatsapp = parsed.data;
|
|
8212
8318
|
writeConfig(accountDir, config);
|
|
8213
|
-
console.error(`${
|
|
8319
|
+
console.error(`${TAG22} bound account manager phone=${normalized} managesAccount=${sub} mode=${mode}`);
|
|
8214
8320
|
reloadManagerConfig(accountDir);
|
|
8215
8321
|
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.`;
|
|
8216
8322
|
return { ok: true, message: `Bound ${normalized} as ${mode} account manager for sub-account ${sub}. ${behaviour}` };
|
|
8217
8323
|
} catch (err) {
|
|
8218
8324
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8219
|
-
console.error(`${
|
|
8325
|
+
console.error(`${TAG22} setAccountManager failed: ${msg}`);
|
|
8220
8326
|
return { ok: false, error: msg };
|
|
8221
8327
|
}
|
|
8222
8328
|
}
|
|
@@ -8253,12 +8359,12 @@ function setDispatchInbound(accountDir, accountId, value) {
|
|
|
8253
8359
|
}
|
|
8254
8360
|
config.whatsapp = parsed.data;
|
|
8255
8361
|
writeConfig(accountDir, config);
|
|
8256
|
-
console.error(`${
|
|
8362
|
+
console.error(`${TAG22} set dispatchInbound account=${id} value=${value}`);
|
|
8257
8363
|
reloadManagerConfig(accountDir);
|
|
8258
8364
|
return { ok: true };
|
|
8259
8365
|
} catch (err) {
|
|
8260
8366
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8261
|
-
console.error(`${
|
|
8367
|
+
console.error(`${TAG22} setDispatchInbound failed: ${msg}`);
|
|
8262
8368
|
return { ok: false, error: msg };
|
|
8263
8369
|
}
|
|
8264
8370
|
}
|
|
@@ -8283,12 +8389,12 @@ function clearAccountManager(accountDir, phone) {
|
|
|
8283
8389
|
}
|
|
8284
8390
|
config.whatsapp = parsed.data;
|
|
8285
8391
|
writeConfig(accountDir, config);
|
|
8286
|
-
console.error(`${
|
|
8392
|
+
console.error(`${TAG22} cleared account manager phone=${normalized}`);
|
|
8287
8393
|
reloadManagerConfig(accountDir);
|
|
8288
8394
|
return { ok: true, message: `Cleared the account-manager binding for ${normalized}.` };
|
|
8289
8395
|
} catch (err) {
|
|
8290
8396
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8291
|
-
console.error(`${
|
|
8397
|
+
console.error(`${TAG22} clearAccountManager failed: ${msg}`);
|
|
8292
8398
|
return { ok: false, error: msg };
|
|
8293
8399
|
}
|
|
8294
8400
|
}
|
|
@@ -8335,12 +8441,12 @@ function setPublicAgent(accountDir, slug) {
|
|
|
8335
8441
|
}
|
|
8336
8442
|
config.whatsapp = parsed.data;
|
|
8337
8443
|
writeConfig(accountDir, config);
|
|
8338
|
-
console.error(`${
|
|
8444
|
+
console.error(`${TAG22} publicAgent set to ${trimmed}`);
|
|
8339
8445
|
reloadManagerConfig(accountDir);
|
|
8340
8446
|
return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
|
|
8341
8447
|
} catch (err) {
|
|
8342
8448
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8343
|
-
console.error(`${
|
|
8449
|
+
console.error(`${TAG22} setPublicAgent failed: ${msg}`);
|
|
8344
8450
|
return { ok: false, error: msg };
|
|
8345
8451
|
}
|
|
8346
8452
|
}
|
|
@@ -8381,17 +8487,17 @@ function updateConfig(accountDir, fields) {
|
|
|
8381
8487
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
8382
8488
|
if (!parsed.success) {
|
|
8383
8489
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
8384
|
-
console.error(`${
|
|
8490
|
+
console.error(`${TAG22} update validation failed: ${msg}`);
|
|
8385
8491
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
8386
8492
|
}
|
|
8387
8493
|
config.whatsapp = parsed.data;
|
|
8388
8494
|
writeConfig(accountDir, config);
|
|
8389
|
-
console.error(`${
|
|
8495
|
+
console.error(`${TAG22} updated fields=[${fieldNames.join(",")}]`);
|
|
8390
8496
|
reloadManagerConfig(accountDir);
|
|
8391
8497
|
return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
|
|
8392
8498
|
} catch (err) {
|
|
8393
8499
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8394
|
-
console.error(`${
|
|
8500
|
+
console.error(`${TAG22} updateConfig failed: ${msg}`);
|
|
8395
8501
|
return { ok: false, error: msg };
|
|
8396
8502
|
}
|
|
8397
8503
|
}
|
|
@@ -8428,17 +8534,17 @@ function migrateRemovedConfigKeys(accountDir) {
|
|
|
8428
8534
|
}
|
|
8429
8535
|
writeConfig(accountDir, config);
|
|
8430
8536
|
console.error(
|
|
8431
|
-
`${
|
|
8537
|
+
`${TAG22} migration: stripped unknown keys=[${result.droppedKeys.join(",")}] from account.json`
|
|
8432
8538
|
);
|
|
8433
8539
|
return { dropped: result.droppedKeys };
|
|
8434
8540
|
} catch (err) {
|
|
8435
|
-
console.error(`${
|
|
8541
|
+
console.error(`${TAG22} migration failed: ${String(err)}`);
|
|
8436
8542
|
return { dropped: [] };
|
|
8437
8543
|
}
|
|
8438
8544
|
}
|
|
8439
8545
|
|
|
8440
8546
|
// app/lib/whatsapp/login.ts
|
|
8441
|
-
var
|
|
8547
|
+
var TAG23 = "[whatsapp:login]";
|
|
8442
8548
|
function maskPhone(digits) {
|
|
8443
8549
|
if (digits.length <= 4) return "*".repeat(digits.length);
|
|
8444
8550
|
return digits.slice(0, 2) + "*".repeat(digits.length - 4) + digits.slice(-2);
|
|
@@ -8449,7 +8555,7 @@ function closeSocket(sock) {
|
|
|
8449
8555
|
try {
|
|
8450
8556
|
sock.ws?.close?.();
|
|
8451
8557
|
} catch (err) {
|
|
8452
|
-
console.warn(`${
|
|
8558
|
+
console.warn(`${TAG23} socket close error during cleanup: ${String(err)}`);
|
|
8453
8559
|
}
|
|
8454
8560
|
}
|
|
8455
8561
|
function resetActiveLogin(accountId) {
|
|
@@ -8476,18 +8582,18 @@ async function runPostPairing(login) {
|
|
|
8476
8582
|
const masked = selfPhone ? `${selfPhone.slice(0, 4)}***` : "null";
|
|
8477
8583
|
console.error(`[whatsapp-persist] wa-persist-on-connect account=${login.accountId} phone=${masked}`);
|
|
8478
8584
|
} else {
|
|
8479
|
-
console.error(`${
|
|
8585
|
+
console.error(`${TAG23} wa-persist-on-connect FAILED account=${login.accountId}: ${result.error}`);
|
|
8480
8586
|
}
|
|
8481
8587
|
} catch (err) {
|
|
8482
|
-
console.error(`${
|
|
8588
|
+
console.error(`${TAG23} wa-persist-on-connect error account=${login.accountId}: ${String(err)}`);
|
|
8483
8589
|
}
|
|
8484
8590
|
} else {
|
|
8485
|
-
console.error(`${
|
|
8591
|
+
console.error(`${TAG23} wa-persist-on-connect skipped \u2014 no accountDir account=${login.accountId}`);
|
|
8486
8592
|
}
|
|
8487
8593
|
try {
|
|
8488
8594
|
await registerLoginSocket(login.accountId, login.sock, login.authDir);
|
|
8489
8595
|
} catch (err) {
|
|
8490
|
-
console.error(`${
|
|
8596
|
+
console.error(`${TAG23} registerLoginSocket failed account=${login.accountId}: ${String(err)}`);
|
|
8491
8597
|
}
|
|
8492
8598
|
}
|
|
8493
8599
|
async function loginConnectionLoop(accountId, login) {
|
|
@@ -8496,7 +8602,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
8496
8602
|
const logTerminal = (outcome, errMsg) => {
|
|
8497
8603
|
const fc = credsCensus(login.authDir);
|
|
8498
8604
|
console.error(
|
|
8499
|
-
`${
|
|
8605
|
+
`${TAG23} op=terminal cid=${cidShort} outcome=${outcome} credsFinal={registered=${fc.registered},account=${fc.hasAccountSignature},me=${fc.e164 ?? "null"}}` + (errMsg ? ` error="${errMsg}"` : "")
|
|
8500
8606
|
);
|
|
8501
8607
|
};
|
|
8502
8608
|
while (true) {
|
|
@@ -8506,7 +8612,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
8506
8612
|
if (current?.id === login.id) {
|
|
8507
8613
|
await runPostPairing(current);
|
|
8508
8614
|
current.connected = true;
|
|
8509
|
-
console.error(`${
|
|
8615
|
+
console.error(`${TAG23} loginConnectionLoop: connected account=${accountId} attempt=${attempt} configPersisted=${current.configPersisted}`);
|
|
8510
8616
|
logTerminal("connected");
|
|
8511
8617
|
}
|
|
8512
8618
|
return;
|
|
@@ -8518,7 +8624,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
8518
8624
|
if (!preRetryCensus.hasAccountSignature) {
|
|
8519
8625
|
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.";
|
|
8520
8626
|
console.error(
|
|
8521
|
-
`${
|
|
8627
|
+
`${TAG23} op=pairing-incomplete cid=${cidShort} closeKind=${closeKind(getStatusCode(err), false)} reason=no-account-signature`
|
|
8522
8628
|
);
|
|
8523
8629
|
return;
|
|
8524
8630
|
}
|
|
@@ -8526,7 +8632,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
8526
8632
|
if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
|
|
8527
8633
|
if (attempt >= LOGIN_MAX_RECONNECTS) {
|
|
8528
8634
|
console.error(
|
|
8529
|
-
`${
|
|
8635
|
+
`${TAG23} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
|
|
8530
8636
|
);
|
|
8531
8637
|
current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
|
|
8532
8638
|
} else {
|
|
@@ -8539,7 +8645,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
8539
8645
|
attempt++;
|
|
8540
8646
|
const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
|
|
8541
8647
|
console.error(
|
|
8542
|
-
`${
|
|
8648
|
+
`${TAG23} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
|
|
8543
8649
|
);
|
|
8544
8650
|
closeSocket(current.sock);
|
|
8545
8651
|
await new Promise((r) => setTimeout(r, delay));
|
|
@@ -8547,14 +8653,14 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
8547
8653
|
if (afterDelay?.id !== login.id) return;
|
|
8548
8654
|
const rc = credsCensus(login.authDir);
|
|
8549
8655
|
console.error(
|
|
8550
|
-
`${
|
|
8656
|
+
`${TAG23} op=reconnect cid=${cidShort} attempt=${attempt}/${LOGIN_MAX_RECONNECTS} withRegistered=${rc.registered} withAccount=${rc.hasAccountSignature}`
|
|
8551
8657
|
);
|
|
8552
8658
|
try {
|
|
8553
8659
|
const newSock = await createWaSocket({ authDir: login.authDir, correlationId: cidShort, account: accountId });
|
|
8554
8660
|
current.sock = newSock;
|
|
8555
8661
|
} catch (sockErr) {
|
|
8556
8662
|
console.error(
|
|
8557
|
-
`${
|
|
8663
|
+
`${TAG23} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
|
|
8558
8664
|
);
|
|
8559
8665
|
current.error = `Reconnection failed: ${String(sockErr)}`;
|
|
8560
8666
|
logTerminal("error", current.error);
|
|
@@ -8571,7 +8677,7 @@ async function startLogin(opts) {
|
|
|
8571
8677
|
const hasAuth = await authExists(authDir);
|
|
8572
8678
|
const selfId = readSelfId(authDir);
|
|
8573
8679
|
console.error(
|
|
8574
|
-
`${
|
|
8680
|
+
`${TAG23} 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")
|
|
8575
8681
|
);
|
|
8576
8682
|
if (hasAuth && !force) {
|
|
8577
8683
|
const who = selfId.e164 ?? selfId.jid ?? "unknown";
|
|
@@ -8586,7 +8692,7 @@ async function startLogin(opts) {
|
|
|
8586
8692
|
}
|
|
8587
8693
|
const existing = activeLogins.get(accountId);
|
|
8588
8694
|
if (existing && isLoginFresh(existing) && existing.pairingCode && !force) {
|
|
8589
|
-
console.error(`${
|
|
8695
|
+
console.error(`${TAG23} startLogin account=${accountId} guard: returning existing pairing code (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
|
|
8590
8696
|
return {
|
|
8591
8697
|
pairingCode: existing.pairingCode,
|
|
8592
8698
|
phone: existing.phone,
|
|
@@ -8594,7 +8700,7 @@ async function startLogin(opts) {
|
|
|
8594
8700
|
};
|
|
8595
8701
|
}
|
|
8596
8702
|
if (existing) {
|
|
8597
|
-
console.error(`${
|
|
8703
|
+
console.error(`${TAG23} startLogin account=${accountId} ${force ? "force override" : "stale/no-code"}, resetting active login`);
|
|
8598
8704
|
}
|
|
8599
8705
|
resetActiveLogin(accountId);
|
|
8600
8706
|
await clearAuth(authDir);
|
|
@@ -8625,7 +8731,7 @@ async function startLogin(opts) {
|
|
|
8625
8731
|
if (requested) return;
|
|
8626
8732
|
requested = true;
|
|
8627
8733
|
console.error(
|
|
8628
|
-
`${
|
|
8734
|
+
`${TAG23} op=pairing-request cid=${cid} account=${accountId} qrRef=#1 sinceOpenMs=${socketOpenTs ? Date.now() - socketOpenTs : 0}`
|
|
8629
8735
|
);
|
|
8630
8736
|
void sock.requestPairingCode(digits).then((code) => {
|
|
8631
8737
|
clearTimeout(codeTimer);
|
|
@@ -8634,7 +8740,7 @@ async function startLogin(opts) {
|
|
|
8634
8740
|
if (current?.id !== login.id) return;
|
|
8635
8741
|
if (!current.pairingCode) current.pairingCode = code;
|
|
8636
8742
|
console.error(
|
|
8637
|
-
`${
|
|
8743
|
+
`${TAG23} op=code-issued cid=${cid} account=${accountId} phone=${maskPhone(digits)} codeLen=${code.length} sinceOpenMs=${socketOpenTs ? codeIssuedTs - socketOpenTs : 0}`
|
|
8638
8744
|
);
|
|
8639
8745
|
resolveCode?.(code);
|
|
8640
8746
|
}).catch((err) => {
|
|
@@ -8665,7 +8771,7 @@ async function startLogin(opts) {
|
|
|
8665
8771
|
const ttlTimer = setTimeout(() => {
|
|
8666
8772
|
const current = activeLogins.get(accountId);
|
|
8667
8773
|
if (current?.id === login.id && !current.connected) {
|
|
8668
|
-
console.error(`${
|
|
8774
|
+
console.error(`${TAG23} pairing-window-elapsed account=${accountId} (ttl sweep)`);
|
|
8669
8775
|
resetActiveLogin(accountId);
|
|
8670
8776
|
}
|
|
8671
8777
|
}, ACTIVE_LOGIN_TTL_MS);
|
|
@@ -8673,7 +8779,7 @@ async function startLogin(opts) {
|
|
|
8673
8779
|
ttlTimer.unref();
|
|
8674
8780
|
}
|
|
8675
8781
|
loginConnectionLoop(accountId, login).catch((err) => {
|
|
8676
|
-
console.error(`${
|
|
8782
|
+
console.error(`${TAG23} loginConnectionLoop unexpected error: ${String(err)}`);
|
|
8677
8783
|
const current = activeLogins.get(accountId);
|
|
8678
8784
|
if (current?.id === login.id) {
|
|
8679
8785
|
current.error = `Unexpected login error: ${String(err)}`;
|
|
@@ -8698,13 +8804,13 @@ async function waitForLogin(opts) {
|
|
|
8698
8804
|
const { accountId, timeoutMs = 6e4 } = opts;
|
|
8699
8805
|
const login = activeLogins.get(accountId);
|
|
8700
8806
|
console.error(
|
|
8701
|
-
`${
|
|
8807
|
+
`${TAG23} 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")
|
|
8702
8808
|
);
|
|
8703
8809
|
if (!login) {
|
|
8704
8810
|
return { connected: false, message: "No active WhatsApp login in progress.", configPersisted: false };
|
|
8705
8811
|
}
|
|
8706
8812
|
if (!isLoginFresh(login)) {
|
|
8707
|
-
console.error(`${
|
|
8813
|
+
console.error(`${TAG23} pairing-window-elapsed account=${accountId}`);
|
|
8708
8814
|
resetActiveLogin(accountId);
|
|
8709
8815
|
return { connected: false, message: "The pairing window expired. Ask me to generate a new code.", configPersisted: false };
|
|
8710
8816
|
}
|
|
@@ -8712,8 +8818,8 @@ async function waitForLogin(opts) {
|
|
|
8712
8818
|
while (Date.now() < deadline) {
|
|
8713
8819
|
if (login.connected) {
|
|
8714
8820
|
const selfId = readSelfId(login.authDir);
|
|
8715
|
-
console.error(`${
|
|
8716
|
-
console.error(`${
|
|
8821
|
+
console.error(`${TAG23} pairing-connected account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
8822
|
+
console.error(`${TAG23} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"} configPersisted=${login.configPersisted}`);
|
|
8717
8823
|
const configPersisted = login.configPersisted;
|
|
8718
8824
|
activeLogins.delete(accountId);
|
|
8719
8825
|
return {
|
|
@@ -8731,7 +8837,7 @@ async function waitForLogin(opts) {
|
|
|
8731
8837
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
8732
8838
|
}
|
|
8733
8839
|
const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
|
|
8734
|
-
console.error(`${
|
|
8840
|
+
console.error(`${TAG23} waitForLogin poll timeout account=${accountId} elapsed=${elapsed}s \u2014 login kept alive (pairing code still valid)`);
|
|
8735
8841
|
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 };
|
|
8736
8842
|
}
|
|
8737
8843
|
function listActiveLoginAccountIds() {
|
|
@@ -8753,7 +8859,7 @@ function reconcileMessageStoreAccountKeys(input) {
|
|
|
8753
8859
|
import { realpathSync as realpathSync3 } from "fs";
|
|
8754
8860
|
import { readFile, stat as stat2 } from "fs/promises";
|
|
8755
8861
|
import { resolve as resolve10, basename as basename3 } from "path";
|
|
8756
|
-
var
|
|
8862
|
+
var TAG24 = "[whatsapp:outbound]";
|
|
8757
8863
|
var lastDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
8758
8864
|
var lastRouteDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
8759
8865
|
function normalizeJid2(to) {
|
|
@@ -8786,10 +8892,10 @@ async function sendWhatsAppDocument(input) {
|
|
|
8786
8892
|
} catch (err) {
|
|
8787
8893
|
const code = err.code;
|
|
8788
8894
|
if (code === "ENOENT") {
|
|
8789
|
-
console.error(`${
|
|
8895
|
+
console.error(`${TAG24} document ENOENT scope=account-dir accountDir=${accountDir} maxyAccountId=${maxyAccountId}`);
|
|
8790
8896
|
return { ok: false, status: 500, error: `Account directory not resolvable for ${maxyAccountId}` };
|
|
8791
8897
|
}
|
|
8792
|
-
console.error(`${
|
|
8898
|
+
console.error(`${TAG24} document path error scope=account-dir maxyAccountId=${maxyAccountId}: ${String(err)}`);
|
|
8793
8899
|
return { ok: false, status: 500, error: String(err) };
|
|
8794
8900
|
}
|
|
8795
8901
|
let resolvedPath;
|
|
@@ -8798,15 +8904,15 @@ async function sendWhatsAppDocument(input) {
|
|
|
8798
8904
|
} catch (err) {
|
|
8799
8905
|
const code = err.code;
|
|
8800
8906
|
if (code === "ENOENT") {
|
|
8801
|
-
console.error(`${
|
|
8907
|
+
console.error(`${TAG24} document ENOENT scope=file path=${filePath} maxyAccountId=${maxyAccountId}`);
|
|
8802
8908
|
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
8803
8909
|
}
|
|
8804
|
-
console.error(`${
|
|
8910
|
+
console.error(`${TAG24} document path error scope=file maxyAccountId=${maxyAccountId}: ${String(err)}`);
|
|
8805
8911
|
return { ok: false, status: 500, error: String(err) };
|
|
8806
8912
|
}
|
|
8807
8913
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
8808
8914
|
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
8809
|
-
console.error(`${
|
|
8915
|
+
console.error(`${TAG24} document REJECTED path=${sanitised} reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
|
|
8810
8916
|
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
8811
8917
|
}
|
|
8812
8918
|
const fileStat = await stat2(resolvedPath);
|
|
@@ -8822,7 +8928,7 @@ async function sendWhatsAppDocument(input) {
|
|
|
8822
8928
|
const res = resolveSocket(accountId);
|
|
8823
8929
|
if (!res.ok) {
|
|
8824
8930
|
const reason = formatSocketResolutionReason(res);
|
|
8825
|
-
console.error(`${
|
|
8931
|
+
console.error(`${TAG24} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=false reason=${reason}`);
|
|
8826
8932
|
return { ok: false, status: 503, error: formatSocketResolutionMessage(res, accountId) };
|
|
8827
8933
|
}
|
|
8828
8934
|
const sock = res.sock;
|
|
@@ -8835,7 +8941,7 @@ async function sendWhatsAppDocument(input) {
|
|
|
8835
8941
|
{ accountId: res.accountId }
|
|
8836
8942
|
);
|
|
8837
8943
|
console.error(
|
|
8838
|
-
`${
|
|
8944
|
+
`${TAG24} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
|
|
8839
8945
|
);
|
|
8840
8946
|
if (result.success) {
|
|
8841
8947
|
recordDocumentOutbound(to);
|
|
@@ -8955,7 +9061,7 @@ function serializeWhatsAppSchema() {
|
|
|
8955
9061
|
// app/lib/whatsapp/status-reconcile.ts
|
|
8956
9062
|
import { readdirSync as readdirSync6 } from "fs";
|
|
8957
9063
|
import { join as join11 } from "path";
|
|
8958
|
-
var
|
|
9064
|
+
var TAG25 = "[whatsapp:reconcile]";
|
|
8959
9065
|
var HALF_REGISTERED_MIN_AGE_MS = 5 * 6e4;
|
|
8960
9066
|
function listCredsAccountIds(credsRoot) {
|
|
8961
9067
|
try {
|
|
@@ -8981,14 +9087,14 @@ function reconcileCredsOnDisk(opts) {
|
|
|
8981
9087
|
const ageMs2 = getAuthAgeMs(authDir);
|
|
8982
9088
|
const abandoned = !inFlight3 && ageMs2 !== null && ageMs2 >= HALF_REGISTERED_MIN_AGE_MS;
|
|
8983
9089
|
if (!abandoned) {
|
|
8984
|
-
console.error(`${
|
|
9090
|
+
console.error(`${TAG25} op=half-registered account=${accountId} registered=true hasAccount=false inFlight=${inFlight3} ageMs=${ageMs2 ?? "null"}`);
|
|
8985
9091
|
entries2.push({ accountId, connected: false, linkedUnconfigured: true });
|
|
8986
9092
|
continue;
|
|
8987
9093
|
}
|
|
8988
|
-
console.error(`${
|
|
9094
|
+
console.error(`${TAG25} op=discard-half-registered account=${accountId} registered=true hasAccount=false ageMs=${ageMs2}`);
|
|
8989
9095
|
const removed = discardAuthDirSync(authDir);
|
|
8990
9096
|
if (!removed) {
|
|
8991
|
-
console.error(`${
|
|
9097
|
+
console.error(`${TAG25} op=discard-half-registered-failed account=${accountId} \u2014 residue persists`);
|
|
8992
9098
|
entries2.push({ accountId, connected: false, linkedUnconfigured: true });
|
|
8993
9099
|
}
|
|
8994
9100
|
continue;
|
|
@@ -8996,14 +9102,14 @@ function reconcileCredsOnDisk(opts) {
|
|
|
8996
9102
|
const selfPhone = readSelfId(authDir).e164 ?? void 0;
|
|
8997
9103
|
const configured = accountDir ? isAccountConfigured(accountDir, accountId) : false;
|
|
8998
9104
|
console.error(
|
|
8999
|
-
`${
|
|
9105
|
+
`${TAG25} wa-link-unconfigured account=${accountId} creds=present config=${configured ? "present" : "absent"}`
|
|
9000
9106
|
);
|
|
9001
9107
|
if (!configured && accountDir) {
|
|
9002
9108
|
const result = persistAfterPairing(accountDir, accountId, selfPhone ?? null);
|
|
9003
9109
|
if (result.ok) {
|
|
9004
|
-
console.error(`${
|
|
9110
|
+
console.error(`${TAG25} self-healed account=${accountId}`);
|
|
9005
9111
|
} else {
|
|
9006
|
-
console.error(`${
|
|
9112
|
+
console.error(`${TAG25} self-heal FAILED account=${accountId}: ${result.error}`);
|
|
9007
9113
|
}
|
|
9008
9114
|
}
|
|
9009
9115
|
entries2.push({ accountId, selfPhone, connected: false, linkedUnconfigured: !configured });
|
|
@@ -9146,7 +9252,7 @@ function authorizeRecallRead(input) {
|
|
|
9146
9252
|
}
|
|
9147
9253
|
|
|
9148
9254
|
// server/routes/whatsapp.ts
|
|
9149
|
-
var
|
|
9255
|
+
var TAG26 = "[whatsapp:api]";
|
|
9150
9256
|
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
9151
9257
|
function recallAttachment(storeAccountId, attachmentId) {
|
|
9152
9258
|
if (!attachmentId) return void 0;
|
|
@@ -9191,16 +9297,16 @@ function houseSocketScopeDenial(c, op) {
|
|
|
9191
9297
|
const houseAccountId = resolveAccount()?.accountId ?? "";
|
|
9192
9298
|
const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, houseAccountId);
|
|
9193
9299
|
if (decision.authorized) return null;
|
|
9194
|
-
console.error(`${
|
|
9300
|
+
console.error(`${TAG26} op=authz-deny route=${op} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
|
|
9195
9301
|
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);
|
|
9196
9302
|
}
|
|
9197
9303
|
function selfOrHouseScopeDenial(c, targetAccountId, op) {
|
|
9198
9304
|
const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, targetAccountId);
|
|
9199
9305
|
if (decision.authorized) {
|
|
9200
|
-
console.error(`${
|
|
9306
|
+
console.error(`${TAG26} op=authz-ok route=${op} target=${targetAccountId} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
|
|
9201
9307
|
return null;
|
|
9202
9308
|
}
|
|
9203
|
-
console.error(`${
|
|
9309
|
+
console.error(`${TAG26} op=authz-deny route=${op} target=${targetAccountId} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
|
|
9204
9310
|
return c.json({ ok: false, error: "Not authorized. This session may only pair or manage its own account." }, 403);
|
|
9205
9311
|
}
|
|
9206
9312
|
var app2 = new Hono();
|
|
@@ -9220,7 +9326,7 @@ app2.get("/status", (c) => {
|
|
|
9220
9326
|
dispatchInbound: shouldDispatchInbound(cfg, a.accountId)
|
|
9221
9327
|
}));
|
|
9222
9328
|
const summary = accounts.map((a) => `${a.accountId}:${a.connected ? "up" : a.linkedUnconfigured ? "linked-unconfigured" : "down"}:${a.dispatchInbound ? "dispatch" : "observe"}`).join(", ");
|
|
9223
|
-
console.error(`${
|
|
9329
|
+
console.error(`${TAG26} status accounts=${accounts.length} [${summary}]`);
|
|
9224
9330
|
const reachableAccountIds = [
|
|
9225
9331
|
.../* @__PURE__ */ new Set([...listStoredAccountIds(), ...listConnectionPlatformAccountIds()])
|
|
9226
9332
|
];
|
|
@@ -9229,11 +9335,11 @@ app2.get("/status", (c) => {
|
|
|
9229
9335
|
reachableAccountIds
|
|
9230
9336
|
});
|
|
9231
9337
|
if (!storeKeyReconcile.ok) {
|
|
9232
|
-
console.warn(`${
|
|
9338
|
+
console.warn(`${TAG26} store-key-drift orphanLiveKeys=[${storeKeyReconcile.orphanLiveKeys.join(",")}] \u2014 in-memory cache keyed by a value with no disk account; read tools will miss`);
|
|
9233
9339
|
}
|
|
9234
9340
|
return c.json({ accounts, storeKeyReconcile });
|
|
9235
9341
|
} catch (err) {
|
|
9236
|
-
console.error(`${
|
|
9342
|
+
console.error(`${TAG26} status error: ${String(err)}`);
|
|
9237
9343
|
return c.json({ error: String(err) }, 500);
|
|
9238
9344
|
}
|
|
9239
9345
|
});
|
|
@@ -9251,10 +9357,10 @@ app2.post("/login/start", async (c) => {
|
|
|
9251
9357
|
const authDir = join12(MAXY_DIR, "credentials", "whatsapp", accountId);
|
|
9252
9358
|
const accountDir = resolveAccount()?.accountDir ?? null;
|
|
9253
9359
|
const result = await startLogin({ accountId, authDir, phone, accountDir, force });
|
|
9254
|
-
console.error(`${
|
|
9360
|
+
console.error(`${TAG26} login/start result account=${accountId} hasCode=${!!result.pairingCode}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
|
|
9255
9361
|
return c.json(result);
|
|
9256
9362
|
} catch (err) {
|
|
9257
|
-
console.error(`${
|
|
9363
|
+
console.error(`${TAG26} login/start error: ${String(err)}`);
|
|
9258
9364
|
return c.json({ error: String(err) }, 500);
|
|
9259
9365
|
}
|
|
9260
9366
|
});
|
|
@@ -9266,7 +9372,7 @@ app2.post("/login/wait", async (c) => {
|
|
|
9266
9372
|
if (denial) return denial;
|
|
9267
9373
|
const timeoutMs = body.timeoutMs ?? 6e4;
|
|
9268
9374
|
const result = await waitForLogin({ accountId, timeoutMs });
|
|
9269
|
-
console.error(`${
|
|
9375
|
+
console.error(`${TAG26} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${result.configPersisted}`);
|
|
9270
9376
|
return c.json({
|
|
9271
9377
|
connected: result.connected,
|
|
9272
9378
|
message: result.message,
|
|
@@ -9274,7 +9380,7 @@ app2.post("/login/wait", async (c) => {
|
|
|
9274
9380
|
configPersisted: result.configPersisted
|
|
9275
9381
|
});
|
|
9276
9382
|
} catch (err) {
|
|
9277
|
-
console.error(`${
|
|
9383
|
+
console.error(`${TAG26} login/wait error: ${String(err)}`);
|
|
9278
9384
|
return c.json({ error: String(err) }, 500);
|
|
9279
9385
|
}
|
|
9280
9386
|
});
|
|
@@ -9287,7 +9393,7 @@ app2.post("/disconnect", async (c) => {
|
|
|
9287
9393
|
await stopConnection(accountId);
|
|
9288
9394
|
return c.json({ disconnected: true, accountId });
|
|
9289
9395
|
} catch (err) {
|
|
9290
|
-
console.error(`${
|
|
9396
|
+
console.error(`${TAG26} disconnect error: ${String(err)}`);
|
|
9291
9397
|
return c.json({ error: String(err) }, 500);
|
|
9292
9398
|
}
|
|
9293
9399
|
});
|
|
@@ -9300,7 +9406,7 @@ app2.post("/reconnect", async (c) => {
|
|
|
9300
9406
|
await startConnection(accountId);
|
|
9301
9407
|
return c.json({ reconnecting: true, accountId });
|
|
9302
9408
|
} catch (err) {
|
|
9303
|
-
console.error(`${
|
|
9409
|
+
console.error(`${TAG26} reconnect error: ${String(err)}`);
|
|
9304
9410
|
return c.json({ error: String(err) }, 500);
|
|
9305
9411
|
}
|
|
9306
9412
|
});
|
|
@@ -9324,7 +9430,7 @@ app2.post("/config", async (c) => {
|
|
|
9324
9430
|
const acct = resolveAccount();
|
|
9325
9431
|
if (!acct) return c.json({ ok: false, error: "No account configured." }, 500);
|
|
9326
9432
|
const result = setDispatchInbound(acct.accountDir, target, body.dispatchInbound);
|
|
9327
|
-
console.error(`${
|
|
9433
|
+
console.error(`${TAG26} config action=set-dispatch-inbound account=${target} dispatchInbound=${body.dispatchInbound} ok=${result.ok}`);
|
|
9328
9434
|
return c.json(result, result.ok ? 200 : 400);
|
|
9329
9435
|
}
|
|
9330
9436
|
const denial = houseSocketScopeDenial(c, `config action=${action}`);
|
|
@@ -9340,7 +9446,7 @@ app2.post("/config", async (c) => {
|
|
|
9340
9446
|
}
|
|
9341
9447
|
const result = addAdminPhone(account.accountDir, phone);
|
|
9342
9448
|
const reasonTail = !result.ok && result.reason ? ` reason=${result.reason}` : "";
|
|
9343
|
-
console.error(`${
|
|
9449
|
+
console.error(`${TAG26} config action=add-admin-phone accountId=${account.accountId} phone=${phone} ok=${result.ok}${reasonTail}`);
|
|
9344
9450
|
return c.json(result, result.ok ? 200 : 400);
|
|
9345
9451
|
}
|
|
9346
9452
|
case "remove-admin-phone": {
|
|
@@ -9348,12 +9454,12 @@ app2.post("/config", async (c) => {
|
|
|
9348
9454
|
return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
|
|
9349
9455
|
}
|
|
9350
9456
|
const result = removeAdminPhone(account.accountDir, phone);
|
|
9351
|
-
console.error(`${
|
|
9457
|
+
console.error(`${TAG26} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
|
|
9352
9458
|
return c.json(result, result.ok ? 200 : 400);
|
|
9353
9459
|
}
|
|
9354
9460
|
case "list-admin-phones": {
|
|
9355
9461
|
const phones = readAdminPhones(account.accountDir);
|
|
9356
|
-
console.error(`${
|
|
9462
|
+
console.error(`${TAG26} config action=list-admin-phones count=${phones.length}`);
|
|
9357
9463
|
return c.json({ ok: true, phones });
|
|
9358
9464
|
}
|
|
9359
9465
|
case "add-account-manager": {
|
|
@@ -9365,7 +9471,7 @@ app2.post("/config", async (c) => {
|
|
|
9365
9471
|
}
|
|
9366
9472
|
const managerMode = mode === "passive" ? "passive" : "active";
|
|
9367
9473
|
const result = setAccountManager(account.accountDir, phone, managesAccount, managerMode);
|
|
9368
|
-
console.error(`${
|
|
9474
|
+
console.error(`${TAG26} config action=add-account-manager phone=${phone} managesAccount=${managesAccount} mode=${managerMode} ok=${result.ok}`);
|
|
9369
9475
|
return c.json(result, result.ok ? 200 : 400);
|
|
9370
9476
|
}
|
|
9371
9477
|
case "remove-account-manager": {
|
|
@@ -9373,12 +9479,12 @@ app2.post("/config", async (c) => {
|
|
|
9373
9479
|
return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
|
|
9374
9480
|
}
|
|
9375
9481
|
const result = clearAccountManager(account.accountDir, phone);
|
|
9376
|
-
console.error(`${
|
|
9482
|
+
console.error(`${TAG26} config action=remove-account-manager phone=${phone} ok=${result.ok}`);
|
|
9377
9483
|
return c.json(result, result.ok ? 200 : 400);
|
|
9378
9484
|
}
|
|
9379
9485
|
case "list-account-managers": {
|
|
9380
9486
|
const accountManagers = readAccountManagers(account.accountDir);
|
|
9381
|
-
console.error(`${
|
|
9487
|
+
console.error(`${TAG26} config action=list-account-managers count=${Object.keys(accountManagers).length}`);
|
|
9382
9488
|
return c.json({ ok: true, accountManagers });
|
|
9383
9489
|
}
|
|
9384
9490
|
case "set-public-agent": {
|
|
@@ -9386,13 +9492,13 @@ app2.post("/config", async (c) => {
|
|
|
9386
9492
|
return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
|
|
9387
9493
|
}
|
|
9388
9494
|
const result = setPublicAgent(account.accountDir, slug);
|
|
9389
|
-
console.error(`${
|
|
9495
|
+
console.error(`${TAG26} config action=set-public-agent slug=${slug} ok=${result.ok}`);
|
|
9390
9496
|
return c.json(result, result.ok ? 200 : 400);
|
|
9391
9497
|
}
|
|
9392
9498
|
case "get-public-agent": {
|
|
9393
9499
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
9394
9500
|
const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount });
|
|
9395
|
-
console.error(`${
|
|
9501
|
+
console.error(`${TAG26} config action=get-public-agent accountId=${targetAccount} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
|
|
9396
9502
|
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
9397
9503
|
}
|
|
9398
9504
|
case "list-public-agents": {
|
|
@@ -9409,26 +9515,26 @@ app2.post("/config", async (c) => {
|
|
|
9409
9515
|
const config = JSON.parse(readFileSync14(configPath2, "utf-8"));
|
|
9410
9516
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
9411
9517
|
} catch {
|
|
9412
|
-
console.error(`${
|
|
9518
|
+
console.error(`${TAG26} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
9413
9519
|
}
|
|
9414
9520
|
}
|
|
9415
9521
|
} catch (err) {
|
|
9416
|
-
console.error(`${
|
|
9522
|
+
console.error(`${TAG26} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
|
|
9417
9523
|
}
|
|
9418
9524
|
}
|
|
9419
|
-
console.error(`${
|
|
9525
|
+
console.error(`${TAG26} config action=list-public-agents count=${agents.length}`);
|
|
9420
9526
|
return c.json({ ok: true, agents });
|
|
9421
9527
|
}
|
|
9422
9528
|
case "schema": {
|
|
9423
9529
|
const text = serializeWhatsAppSchema();
|
|
9424
|
-
console.error(`${
|
|
9530
|
+
console.error(`${TAG26} config action=schema`);
|
|
9425
9531
|
return c.json({ ok: true, text });
|
|
9426
9532
|
}
|
|
9427
9533
|
case "list-groups": {
|
|
9428
9534
|
const groupAccountId = accountId ?? "default";
|
|
9429
9535
|
const groupRes = resolveSocket(groupAccountId);
|
|
9430
9536
|
if (!groupRes.ok) {
|
|
9431
|
-
console.error(`${
|
|
9537
|
+
console.error(`${TAG26} config action=list-groups error="${formatSocketResolutionReason(groupRes)}" accountId=${groupAccountId}`);
|
|
9432
9538
|
return c.json({ ok: false, error: formatSocketResolutionMessage(groupRes, groupAccountId) });
|
|
9433
9539
|
}
|
|
9434
9540
|
const sock = groupRes.sock;
|
|
@@ -9439,10 +9545,10 @@ app2.post("/config", async (c) => {
|
|
|
9439
9545
|
name: g.subject ?? g.id,
|
|
9440
9546
|
participantCount: Array.isArray(g.participants) ? g.participants.length : 0
|
|
9441
9547
|
}));
|
|
9442
|
-
console.error(`${
|
|
9548
|
+
console.error(`${TAG26} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
|
|
9443
9549
|
return c.json({ ok: true, groups });
|
|
9444
9550
|
} catch (err) {
|
|
9445
|
-
console.error(`${
|
|
9551
|
+
console.error(`${TAG26} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
|
|
9446
9552
|
return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
|
|
9447
9553
|
}
|
|
9448
9554
|
}
|
|
@@ -9452,12 +9558,12 @@ app2.post("/config", async (c) => {
|
|
|
9452
9558
|
}
|
|
9453
9559
|
const result = updateConfig(account.accountDir, fields);
|
|
9454
9560
|
const fieldNames = Object.keys(fields);
|
|
9455
|
-
console.error(`${
|
|
9561
|
+
console.error(`${TAG26} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
|
|
9456
9562
|
return c.json(result, result.ok ? 200 : 400);
|
|
9457
9563
|
}
|
|
9458
9564
|
case "get-config": {
|
|
9459
9565
|
const waConfig = getConfig(account.accountDir);
|
|
9460
|
-
console.error(`${
|
|
9566
|
+
console.error(`${TAG26} config action=get-config`);
|
|
9461
9567
|
return c.json({ ok: true, config: waConfig });
|
|
9462
9568
|
}
|
|
9463
9569
|
default:
|
|
@@ -9467,7 +9573,7 @@ app2.post("/config", async (c) => {
|
|
|
9467
9573
|
);
|
|
9468
9574
|
}
|
|
9469
9575
|
} catch (err) {
|
|
9470
|
-
console.error(`${
|
|
9576
|
+
console.error(`${TAG26} config error: ${String(err)}`);
|
|
9471
9577
|
return c.json({ ok: false, error: String(err) }, 500);
|
|
9472
9578
|
}
|
|
9473
9579
|
});
|
|
@@ -9488,7 +9594,7 @@ app2.post("/send-document", async (c) => {
|
|
|
9488
9594
|
recordRouteDocumentOutbound(to, filePath);
|
|
9489
9595
|
return c.json({ success: true, messageId: result.messageId });
|
|
9490
9596
|
} catch (err) {
|
|
9491
|
-
console.error(`${
|
|
9597
|
+
console.error(`${TAG26} send-document error: ${String(err)}`);
|
|
9492
9598
|
return c.json({ error: String(err) }, 500);
|
|
9493
9599
|
}
|
|
9494
9600
|
});
|
|
@@ -9533,7 +9639,7 @@ app2.post("/send-admin", async (c) => {
|
|
|
9533
9639
|
console.error(`[whatsapp:outbound] op=send-admin-ok via=${via} to=${phone}`);
|
|
9534
9640
|
return c.json({ success: true, messageId: result.messageId });
|
|
9535
9641
|
} catch (err) {
|
|
9536
|
-
console.error(`${
|
|
9642
|
+
console.error(`${TAG26} send-admin error: ${String(err)}`);
|
|
9537
9643
|
return c.json({ error: String(err) }, 500);
|
|
9538
9644
|
}
|
|
9539
9645
|
});
|
|
@@ -9592,7 +9698,7 @@ app2.post("/reply", async (c) => {
|
|
|
9592
9698
|
console.error(`[whatsapp:observe] op=reply-sent sendId=${sendId} account=${accountId} socket=${res.accountId} waMessageId=${sent.messageId ?? ""}`);
|
|
9593
9699
|
return c.json({ success: true, messageId: sent.messageId });
|
|
9594
9700
|
} catch (err) {
|
|
9595
|
-
console.error(`${
|
|
9701
|
+
console.error(`${TAG26} reply error: ${String(err)}`);
|
|
9596
9702
|
return c.json({ error: String(err) }, 500);
|
|
9597
9703
|
}
|
|
9598
9704
|
});
|
|
@@ -9602,11 +9708,11 @@ app2.get("/activity", (c) => {
|
|
|
9602
9708
|
const result = getChannelActivity(accountId);
|
|
9603
9709
|
const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
|
|
9604
9710
|
console.error(
|
|
9605
|
-
`${
|
|
9711
|
+
`${TAG26} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
|
|
9606
9712
|
);
|
|
9607
9713
|
return c.json(result);
|
|
9608
9714
|
} catch (err) {
|
|
9609
|
-
console.error(`${
|
|
9715
|
+
console.error(`${TAG26} activity error: ${String(err)}`);
|
|
9610
9716
|
return c.json({ error: String(err) }, 500);
|
|
9611
9717
|
}
|
|
9612
9718
|
});
|
|
@@ -9634,10 +9740,10 @@ app2.get("/conversations", (c) => {
|
|
|
9634
9740
|
};
|
|
9635
9741
|
});
|
|
9636
9742
|
conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
|
|
9637
|
-
console.error(`${
|
|
9743
|
+
console.error(`${TAG26} conversations requested=${requestedAccountId} caller=${callerAccountId ?? "none"} account=${storeAccountId} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${allConversations.size > 0} count=${conversations.length}`);
|
|
9638
9744
|
return c.json({ conversations });
|
|
9639
9745
|
} catch (err) {
|
|
9640
|
-
console.error(`${
|
|
9746
|
+
console.error(`${TAG26} conversations error: ${String(err)}`);
|
|
9641
9747
|
return c.json({ error: String(err) }, 500);
|
|
9642
9748
|
}
|
|
9643
9749
|
});
|
|
@@ -9690,10 +9796,10 @@ app2.get("/messages", (c) => {
|
|
|
9690
9796
|
...reactions && reactions.length > 0 ? { reactions } : {}
|
|
9691
9797
|
};
|
|
9692
9798
|
});
|
|
9693
|
-
console.error(`${
|
|
9799
|
+
console.error(`${TAG26} messages requested=${requestedAccountId} caller=${callerAccountId ?? "none"} account=${storeAccountId} jid=${jid} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${storeMessages.length > 0} limit=${effectiveLimit ?? "all"} returned=${messages.length} withAttachments=${withAttachments}`);
|
|
9694
9800
|
return c.json({ messages });
|
|
9695
9801
|
} catch (err) {
|
|
9696
|
-
console.error(`${
|
|
9802
|
+
console.error(`${TAG26} messages error: ${String(err)}`);
|
|
9697
9803
|
return c.json({ error: String(err) }, 500);
|
|
9698
9804
|
}
|
|
9699
9805
|
});
|
|
@@ -9795,7 +9901,7 @@ app2.get("/conversation-graph-state", async (c) => {
|
|
|
9795
9901
|
ms
|
|
9796
9902
|
});
|
|
9797
9903
|
} catch (err) {
|
|
9798
|
-
console.error(`${
|
|
9904
|
+
console.error(`${TAG26} conversation-graph-state error: ${String(err)}`);
|
|
9799
9905
|
return c.json({ error: String(err) }, 500);
|
|
9800
9906
|
}
|
|
9801
9907
|
});
|
|
@@ -9807,12 +9913,12 @@ app2.get("/group-info", async (c) => {
|
|
|
9807
9913
|
return c.json({ error: "Missing required parameter: jid" }, 400);
|
|
9808
9914
|
}
|
|
9809
9915
|
if (!isGroupJid(jid)) {
|
|
9810
|
-
console.error(`${
|
|
9916
|
+
console.error(`${TAG26} group-info error="not a group JID" jid=${jid} account=${accountId}`);
|
|
9811
9917
|
return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
|
|
9812
9918
|
}
|
|
9813
9919
|
const res = resolveSocket(accountId);
|
|
9814
9920
|
if (!res.ok) {
|
|
9815
|
-
console.error(`${
|
|
9921
|
+
console.error(`${TAG26} group-info error="${formatSocketResolutionReason(res)}" account=${accountId}`);
|
|
9816
9922
|
return c.json({ error: formatSocketResolutionMessage(res, accountId) }, 400);
|
|
9817
9923
|
}
|
|
9818
9924
|
const sock = res.sock;
|
|
@@ -9826,10 +9932,10 @@ app2.get("/group-info", async (c) => {
|
|
|
9826
9932
|
participantCount: meta.participants.length,
|
|
9827
9933
|
participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
|
|
9828
9934
|
};
|
|
9829
|
-
console.error(`${
|
|
9935
|
+
console.error(`${TAG26} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
|
|
9830
9936
|
return c.json(result);
|
|
9831
9937
|
} catch (err) {
|
|
9832
|
-
console.error(`${
|
|
9938
|
+
console.error(`${TAG26} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
|
|
9833
9939
|
return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
|
|
9834
9940
|
}
|
|
9835
9941
|
});
|
|
@@ -10033,12 +10139,12 @@ app3.post("/pages/deploy", pagesDeployBodyLimit, async (c) => {
|
|
|
10033
10139
|
console.error(
|
|
10034
10140
|
`[pages-broker] op=project-ensure pub=${pub} project=${project} existed=${existed} created=${!existed}`
|
|
10035
10141
|
);
|
|
10036
|
-
const
|
|
10142
|
+
const started2 = Date.now();
|
|
10037
10143
|
const { url, excluded, uploadDir, uploadDirSource } = await exec.pagesDeploy(dir, project, branch);
|
|
10038
10144
|
console.error(
|
|
10039
10145
|
`[pages-broker] op=stage pub=${pub} upload=${uploadDir} uploadFrom=${uploadDirSource} excluded=${excluded.length > 0 ? excluded.join(",") : "none"}`
|
|
10040
10146
|
);
|
|
10041
|
-
console.error(`[pages-broker] op=deploy pub=${pub} ranMs=${Date.now() -
|
|
10147
|
+
console.error(`[pages-broker] op=deploy pub=${pub} ranMs=${Date.now() - started2} url=${url}`);
|
|
10042
10148
|
const httpStatus = await verifyDeployed(url);
|
|
10043
10149
|
const verified = httpStatus === 200;
|
|
10044
10150
|
console.error(
|
|
@@ -10697,7 +10803,7 @@ function resolveExposedDirs(schemaText, exposeFolders = []) {
|
|
|
10697
10803
|
}
|
|
10698
10804
|
|
|
10699
10805
|
// server/portal-uploads-pull.ts
|
|
10700
|
-
var
|
|
10806
|
+
var TAG27 = "[portal-pull]";
|
|
10701
10807
|
var PORTAL_UPLOADS_PULL_INTERVAL_MS = 6e4;
|
|
10702
10808
|
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10703
10809
|
function safeSegment(s) {
|
|
@@ -10758,17 +10864,17 @@ async function pullAccount(opts) {
|
|
|
10758
10864
|
}
|
|
10759
10865
|
const dest = resolveDestination(accountDir, row, exposed);
|
|
10760
10866
|
const idField = dest.ok || dest.reason !== "bad-fileid" ? ` fileId=${row.fileId}` : "";
|
|
10761
|
-
const head = `${
|
|
10867
|
+
const head = `${TAG27} op=claim account=${accountId}${idField} bytes=${row.size}`;
|
|
10762
10868
|
if (!dest.ok) {
|
|
10763
10869
|
failed++;
|
|
10764
10870
|
log2(
|
|
10765
|
-
`${
|
|
10871
|
+
`${TAG27} op=route account=${accountId}${idField} target="${row.targetPath}" result=refused reason=${dest.reason}`
|
|
10766
10872
|
);
|
|
10767
10873
|
log2(`${head} result=${dest.reason}`);
|
|
10768
10874
|
continue;
|
|
10769
10875
|
}
|
|
10770
10876
|
log2(
|
|
10771
|
-
`${
|
|
10877
|
+
`${TAG27} op=route account=${accountId}${idField} target="${row.targetPath}" result=${row.targetPath ? "exposed" : "default"}`
|
|
10772
10878
|
);
|
|
10773
10879
|
let bytes;
|
|
10774
10880
|
try {
|
|
@@ -10781,7 +10887,7 @@ async function pullAccount(opts) {
|
|
|
10781
10887
|
const finalPath = nextFreePath(dest.final);
|
|
10782
10888
|
if (finalPath !== dest.final) {
|
|
10783
10889
|
log2(
|
|
10784
|
-
`${
|
|
10890
|
+
`${TAG27} op=collision account=${accountId}${idField} suffix="${basename4(finalPath)}" result=suffixed`
|
|
10785
10891
|
);
|
|
10786
10892
|
}
|
|
10787
10893
|
const relPath = relative2(resolve13(accountDir), finalPath).split(sep3).join("/");
|
|
@@ -10825,7 +10931,7 @@ async function pullAccount(opts) {
|
|
|
10825
10931
|
deleteErr = err instanceof Error ? err.message : String(err);
|
|
10826
10932
|
}
|
|
10827
10933
|
log2(
|
|
10828
|
-
`${
|
|
10934
|
+
`${TAG27} op=move account=${accountId}${idField} r2Deleted=${r2Deleted}` + (deleteErr ? ` err="${deleteErr}"` : "")
|
|
10829
10935
|
);
|
|
10830
10936
|
pulled++;
|
|
10831
10937
|
log2(`${head} result=written`);
|
|
@@ -10835,10 +10941,10 @@ async function pullAccount(opts) {
|
|
|
10835
10941
|
async function withdrawRow(opts) {
|
|
10836
10942
|
const { accountDir, accountId, dbName, exposed, row, cf, log: log2 } = opts;
|
|
10837
10943
|
if (!UUID.test(row.fileId)) {
|
|
10838
|
-
log2(`${
|
|
10944
|
+
log2(`${TAG27} op=delete-propagate account=${accountId} result=bad-fileid`);
|
|
10839
10945
|
return false;
|
|
10840
10946
|
}
|
|
10841
|
-
const head = `${
|
|
10947
|
+
const head = `${TAG27} op=delete-propagate account=${accountId} fileId=${row.fileId}`;
|
|
10842
10948
|
const segments = row.devicePath.split("/");
|
|
10843
10949
|
if (!row.devicePath || !segments.every(safeSegment) || !exposedOrOwnUploads(segments, exposed, row.ownerId)) {
|
|
10844
10950
|
log2(`${head} removed=false result=bad-path`);
|
|
@@ -10937,7 +11043,7 @@ async function runPortalUploadsPull(root) {
|
|
|
10937
11043
|
await cf.d1Query(portal.portalDbName, PENDING_SELECT)
|
|
10938
11044
|
);
|
|
10939
11045
|
if (malformed > 0) {
|
|
10940
|
-
console.error(`${
|
|
11046
|
+
console.error(`${TAG27} op=malformed account=${portal.accountId} rows=${malformed}`);
|
|
10941
11047
|
}
|
|
10942
11048
|
if (rows.length === 0) continue;
|
|
10943
11049
|
const accountDir = join14(accountsRoot, portal.accountId);
|
|
@@ -10957,12 +11063,12 @@ async function runPortalUploadsPull(root) {
|
|
|
10957
11063
|
failedTotal += r.failed;
|
|
10958
11064
|
withdrawnTotal += r.withdrawn;
|
|
10959
11065
|
console.error(
|
|
10960
|
-
`${
|
|
11066
|
+
`${TAG27} op=done account=${portal.accountId} pulled=${r.pulled} withdrawn=${r.withdrawn} failed=${r.failed}`
|
|
10961
11067
|
);
|
|
10962
11068
|
} catch (err) {
|
|
10963
11069
|
failedTotal++;
|
|
10964
11070
|
console.error(
|
|
10965
|
-
`${
|
|
11071
|
+
`${TAG27} op=failed account=${portal.accountId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
10966
11072
|
);
|
|
10967
11073
|
}
|
|
10968
11074
|
}
|
|
@@ -11037,7 +11143,7 @@ function createD1Client(opts) {
|
|
|
11037
11143
|
}
|
|
11038
11144
|
|
|
11039
11145
|
// ../plugins/cloudflare/bin/portal-index-push.mjs
|
|
11040
|
-
var
|
|
11146
|
+
var TAG28 = "[portal-index]";
|
|
11041
11147
|
function readEnvFile(path3) {
|
|
11042
11148
|
const out = {};
|
|
11043
11149
|
if (!existsSync11(path3)) return out;
|
|
@@ -11102,17 +11208,17 @@ async function pushAccount(opts) {
|
|
|
11102
11208
|
const exposeFolders = Array.isArray(opts.exposeFolders) ? opts.exposeFolders : [];
|
|
11103
11209
|
const r = resolveExposedDirs(schemaText, exposeFolders);
|
|
11104
11210
|
log2(
|
|
11105
|
-
`${
|
|
11211
|
+
`${TAG28} op=resolve account=${accountId} schemaPresent=${r.schemaPresent} allowed=${r.allowed.length} domainBuckets=${r.domainBuckets.join(",") || "none"} extraFolders=${exposeFolders.length} exposed=${r.exposed.join(",") || "none"}`
|
|
11106
11212
|
);
|
|
11107
11213
|
for (const c of r.collisions) {
|
|
11108
|
-
log2(`${
|
|
11214
|
+
log2(`${TAG28} op=collision account=${accountId} dir=${c} claimedBy=plugin-owned`);
|
|
11109
11215
|
}
|
|
11110
11216
|
if (opts.previousExposed) {
|
|
11111
11217
|
const added = r.exposed.filter((d) => !opts.previousExposed.includes(d));
|
|
11112
11218
|
const removed = opts.previousExposed.filter((d) => !r.exposed.includes(d));
|
|
11113
11219
|
if (added.length || removed.length) {
|
|
11114
11220
|
log2(
|
|
11115
|
-
`${
|
|
11221
|
+
`${TAG28} op=exposed-changed account=${accountId} added=${added.join(",") || "none"} removed=${removed.join(",") || "none"}`
|
|
11116
11222
|
);
|
|
11117
11223
|
}
|
|
11118
11224
|
}
|
|
@@ -11136,7 +11242,7 @@ async function pushAccount(opts) {
|
|
|
11136
11242
|
accountId,
|
|
11137
11243
|
next
|
|
11138
11244
|
]);
|
|
11139
|
-
log2(`${
|
|
11245
|
+
log2(`${TAG28} op=stage-clear account=${accountId} generation=${next} cleared=${orphanCount}`);
|
|
11140
11246
|
}
|
|
11141
11247
|
for (const row of rows) {
|
|
11142
11248
|
await client.query(
|
|
@@ -11144,12 +11250,12 @@ async function pushAccount(opts) {
|
|
|
11144
11250
|
[accountId, row.relPath, row.sizeBytes, row.modifiedAt, nowIso, row.isDir ? 1 : 0, next]
|
|
11145
11251
|
);
|
|
11146
11252
|
}
|
|
11147
|
-
log2(`${
|
|
11253
|
+
log2(`${TAG28} op=stage account=${accountId} generation=${next} inserted=${rows.length}`);
|
|
11148
11254
|
await client.query(
|
|
11149
11255
|
"INSERT INTO directory_state (accountId, currentGeneration) VALUES (?, ?) ON CONFLICT (accountId) DO UPDATE SET currentGeneration = excluded.currentGeneration",
|
|
11150
11256
|
[accountId, next]
|
|
11151
11257
|
);
|
|
11152
|
-
log2(`${
|
|
11258
|
+
log2(`${TAG28} op=flip account=${accountId} generation=${next}`);
|
|
11153
11259
|
try {
|
|
11154
11260
|
await client.query("DELETE FROM directory WHERE accountId = ? AND generation != ?", [
|
|
11155
11261
|
accountId,
|
|
@@ -11157,7 +11263,7 @@ async function pushAccount(opts) {
|
|
|
11157
11263
|
]);
|
|
11158
11264
|
} catch (err) {
|
|
11159
11265
|
log2(
|
|
11160
|
-
`${
|
|
11266
|
+
`${TAG28} op=sweep account=${accountId} generation=${next} result=failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
11161
11267
|
);
|
|
11162
11268
|
}
|
|
11163
11269
|
const check = await client.query(
|
|
@@ -11165,7 +11271,7 @@ async function pushAccount(opts) {
|
|
|
11165
11271
|
[accountId]
|
|
11166
11272
|
);
|
|
11167
11273
|
const after = Number(check[0]?.n ?? -1);
|
|
11168
|
-
log2(`${
|
|
11274
|
+
log2(`${TAG28} op=verify account=${accountId} rowsAfter=${after} expected=${rows.length}`);
|
|
11169
11275
|
return { exposed: r.exposed, rows: rows.length, schemaPresent: true, generation: next };
|
|
11170
11276
|
}
|
|
11171
11277
|
function stateFilePath(platformRoot5) {
|
|
@@ -11186,7 +11292,7 @@ function recordSuccess(platformRoot5, accountId, entry) {
|
|
|
11186
11292
|
writeFileSync6(path3, JSON.stringify(state, null, 1));
|
|
11187
11293
|
} catch (err) {
|
|
11188
11294
|
console.error(
|
|
11189
|
-
`${
|
|
11295
|
+
`${TAG28} op=state-write-failed account=${accountId} path="${path3}" err="${err instanceof Error ? err.message : String(err)}"`
|
|
11190
11296
|
);
|
|
11191
11297
|
}
|
|
11192
11298
|
}
|
|
@@ -11210,7 +11316,7 @@ async function portalAccounts(accountsRoot) {
|
|
|
11210
11316
|
const exposeFolders = Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
|
|
11211
11317
|
out.push({ accountDir, accountId, dbName: cfg.portalDbName, exposeFolders });
|
|
11212
11318
|
} else {
|
|
11213
|
-
console.error(`${
|
|
11319
|
+
console.error(`${TAG28} op=skip account=${accountId} reason=no-portalDbName`);
|
|
11214
11320
|
}
|
|
11215
11321
|
} catch {
|
|
11216
11322
|
}
|
|
@@ -11238,7 +11344,7 @@ function resolveCloudflareCredentials(platformRoot5) {
|
|
|
11238
11344
|
async function pushAllAccounts(platformRoot5) {
|
|
11239
11345
|
const accountsRoot = resolve14(platformRoot5, "..", "data", "accounts");
|
|
11240
11346
|
const targets = await portalAccounts(accountsRoot);
|
|
11241
|
-
console.error(`${
|
|
11347
|
+
console.error(`${TAG28} op=enumerate accountsRoot="${accountsRoot}" accounts=${targets.length}`);
|
|
11242
11348
|
if (targets.length === 0) return { accounts: 0, pushed: 0, failed: 0 };
|
|
11243
11349
|
const cred = resolveCloudflareCredentials(platformRoot5);
|
|
11244
11350
|
if ("error" in cred) {
|
|
@@ -11272,11 +11378,11 @@ async function runTargets(platformRoot5, cred, targets) {
|
|
|
11272
11378
|
});
|
|
11273
11379
|
}
|
|
11274
11380
|
pushed++;
|
|
11275
|
-
console.error(`${
|
|
11381
|
+
console.error(`${TAG28} op=done account=${t.accountId} rows=${r.rows}`);
|
|
11276
11382
|
} catch (err) {
|
|
11277
11383
|
failed++;
|
|
11278
11384
|
console.error(
|
|
11279
|
-
`${
|
|
11385
|
+
`${TAG28} op=failed account=${t.accountId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
11280
11386
|
);
|
|
11281
11387
|
}
|
|
11282
11388
|
}
|
|
@@ -11285,7 +11391,7 @@ async function runTargets(platformRoot5, cred, targets) {
|
|
|
11285
11391
|
async function main() {
|
|
11286
11392
|
const platformRoot5 = process.env.PLATFORM_ROOT;
|
|
11287
11393
|
if (!platformRoot5) {
|
|
11288
|
-
console.error(`${
|
|
11394
|
+
console.error(`${TAG28} op=usage reason=no-platform-root PLATFORM_ROOT is required`);
|
|
11289
11395
|
process.exit(1);
|
|
11290
11396
|
}
|
|
11291
11397
|
if (process.argv.includes("--all-accounts")) {
|
|
@@ -11293,7 +11399,7 @@ async function main() {
|
|
|
11293
11399
|
const r2 = await pushAllAccounts(platformRoot5);
|
|
11294
11400
|
if (r2.failed > 0) process.exit(1);
|
|
11295
11401
|
} catch (err) {
|
|
11296
|
-
console.error(`${
|
|
11402
|
+
console.error(`${TAG28} op=skip reason=${err instanceof Error ? err.message : String(err)}`);
|
|
11297
11403
|
process.exit(1);
|
|
11298
11404
|
}
|
|
11299
11405
|
return;
|
|
@@ -11302,12 +11408,12 @@ async function main() {
|
|
|
11302
11408
|
const accountId = arg("--account");
|
|
11303
11409
|
const dbName = arg("--db");
|
|
11304
11410
|
if (!accountDir || !accountId || !dbName) {
|
|
11305
|
-
console.error(`${
|
|
11411
|
+
console.error(`${TAG28} op=usage --account-dir <dir> --account <id> --db <name>`);
|
|
11306
11412
|
process.exit(1);
|
|
11307
11413
|
}
|
|
11308
11414
|
const cred = resolveCloudflareCredentials(platformRoot5);
|
|
11309
11415
|
if ("error" in cred) {
|
|
11310
|
-
console.error(`${
|
|
11416
|
+
console.error(`${TAG28} op=skip reason=${cred.error}`);
|
|
11311
11417
|
process.exit(1);
|
|
11312
11418
|
}
|
|
11313
11419
|
const r = await runTargets(platformRoot5, cred, [
|
|
@@ -13529,7 +13635,7 @@ function resolveOpenVisitorSession(rows, visitorId, agentSlug) {
|
|
|
13529
13635
|
|
|
13530
13636
|
// server/routes/public-reader.ts
|
|
13531
13637
|
var app6 = new Hono();
|
|
13532
|
-
var
|
|
13638
|
+
var TAG29 = "[public-webchat]";
|
|
13533
13639
|
function parseAccessSessionId(cookieHeader) {
|
|
13534
13640
|
if (!cookieHeader) return null;
|
|
13535
13641
|
const part = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="));
|
|
@@ -13577,7 +13683,7 @@ function enumeratePublicRows() {
|
|
|
13577
13683
|
app6.get("/session", (c) => {
|
|
13578
13684
|
const visitor = resolveVisitor(c);
|
|
13579
13685
|
if (!visitor) {
|
|
13580
|
-
console.error(`${
|
|
13686
|
+
console.error(`${TAG29} op=reader-refused reason=no-anchor path=/session`);
|
|
13581
13687
|
return c.json({ error: "gate-required" }, 401);
|
|
13582
13688
|
}
|
|
13583
13689
|
const rows = enumeratePublicRows();
|
|
@@ -13596,11 +13702,11 @@ app6.get("/session", (c) => {
|
|
|
13596
13702
|
found = effectiveSlug ? resolveOpenVisitorSession(rows, visitor.visitorId, effectiveSlug) : null;
|
|
13597
13703
|
}
|
|
13598
13704
|
if (found) {
|
|
13599
|
-
console.log(`${
|
|
13705
|
+
console.log(`${TAG29} op=session-resume anchor=${visitor.kind} key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
|
|
13600
13706
|
return c.json({ sessionId: found.sessionId, projectDir: found.projectDir, sessionKey: found.senderId });
|
|
13601
13707
|
}
|
|
13602
13708
|
const sessionKey = crypto.randomUUID();
|
|
13603
|
-
console.log(`${
|
|
13709
|
+
console.log(`${TAG29} op=session-new anchor=${visitor.kind} key=${sessionKey.slice(0, 8)}`);
|
|
13604
13710
|
return c.json({ sessionId: null, projectDir: null, sessionKey });
|
|
13605
13711
|
});
|
|
13606
13712
|
function publicUploadsDir(accountId, sessionId) {
|
|
@@ -13629,7 +13735,7 @@ function readFrom2(path3, from) {
|
|
|
13629
13735
|
app6.get("/stream", (c) => {
|
|
13630
13736
|
const visitor = resolveVisitor(c);
|
|
13631
13737
|
if (!visitor) {
|
|
13632
|
-
console.error(`${
|
|
13738
|
+
console.error(`${TAG29} op=reader-refused reason=no-anchor path=/stream`);
|
|
13633
13739
|
return c.json({ error: "gate-required" }, 401);
|
|
13634
13740
|
}
|
|
13635
13741
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
@@ -13647,12 +13753,12 @@ app6.get("/stream", (c) => {
|
|
|
13647
13753
|
const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
|
|
13648
13754
|
const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
|
|
13649
13755
|
if (!(isPublicWebchat && owns)) {
|
|
13650
|
-
console.error(`${
|
|
13756
|
+
console.error(`${TAG29} op=reader-refused reason=not-owner anchor=${visitor.kind} sessionId=${sessionId.slice(0, 8)}`);
|
|
13651
13757
|
return c.json({ error: "forbidden" }, 403);
|
|
13652
13758
|
}
|
|
13653
13759
|
const senderShort = (meta.senderId ?? "").slice(0, 8);
|
|
13654
13760
|
const encoder = new TextEncoder();
|
|
13655
|
-
console.log(`${
|
|
13761
|
+
console.log(`${TAG29} op=reader-open anchor=${visitor.kind} key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
|
|
13656
13762
|
const send = (controller, turn, id) => controller.enqueue(encoder.encode(`id: ${id}
|
|
13657
13763
|
data: ${JSON.stringify(turn)}
|
|
13658
13764
|
|
|
@@ -13667,7 +13773,7 @@ data: ${JSON.stringify(turn)}
|
|
|
13667
13773
|
const { turns, dropped } = filterDeliveredFrames(lines);
|
|
13668
13774
|
if (uploadsDir) enrichPublicAttachments(turns, listSessionAttachmentsInDir(uploadsDir), attachmentCursor);
|
|
13669
13775
|
for (const turn of turns) send(controller, turn, offset);
|
|
13670
|
-
if (dropped > 0) console.log(`${
|
|
13776
|
+
if (dropped > 0) console.log(`${TAG29} op=reader-filtered key=${senderShort} dropped=${dropped}`);
|
|
13671
13777
|
};
|
|
13672
13778
|
try {
|
|
13673
13779
|
const { buf, end } = readFrom2(jsonlPath, 0);
|
|
@@ -13702,7 +13808,7 @@ data: ${JSON.stringify(turn)}
|
|
|
13702
13808
|
watcher?.close();
|
|
13703
13809
|
clearInterval(poll);
|
|
13704
13810
|
clearInterval(heartbeat);
|
|
13705
|
-
console.log(`${
|
|
13811
|
+
console.log(`${TAG29} op=reader-close key=${senderShort}`);
|
|
13706
13812
|
try {
|
|
13707
13813
|
controller.close();
|
|
13708
13814
|
} catch {
|
|
@@ -14684,7 +14790,7 @@ app7.get("/", requireAdminSession, async (c) => {
|
|
|
14684
14790
|
console.error("[webchat:greeting] op=read-failed reason=no-account-for-session");
|
|
14685
14791
|
return c.json({ ok: false });
|
|
14686
14792
|
}
|
|
14687
|
-
const
|
|
14793
|
+
const started2 = Date.now();
|
|
14688
14794
|
let session;
|
|
14689
14795
|
try {
|
|
14690
14796
|
session = getSession();
|
|
@@ -14730,7 +14836,7 @@ app7.get("/", requireAdminSession, async (c) => {
|
|
|
14730
14836
|
console.error(`[webchat:greeting] op=specialists-read-failed reason=${detail}`);
|
|
14731
14837
|
}
|
|
14732
14838
|
console.log(
|
|
14733
|
-
`[webchat:greeting] op=read account=${accountId.slice(0, 8)} events=${events.length} tasks=${tasks.total} specialists=${specialists.length} ms=${Date.now() -
|
|
14839
|
+
`[webchat:greeting] op=read account=${accountId.slice(0, 8)} events=${events.length} tasks=${tasks.total} specialists=${specialists.length} ms=${Date.now() - started2}`
|
|
14734
14840
|
);
|
|
14735
14841
|
return c.json({ ok: true, events, tasks, specialists });
|
|
14736
14842
|
} catch (err) {
|
|
@@ -14809,7 +14915,7 @@ function routeTelegramUpdate(input) {
|
|
|
14809
14915
|
}
|
|
14810
14916
|
|
|
14811
14917
|
// server/routes/telegram.ts
|
|
14812
|
-
var
|
|
14918
|
+
var TAG30 = "[telegram-inbound]";
|
|
14813
14919
|
function configDirName() {
|
|
14814
14920
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
14815
14921
|
const brandPath = join27(platformRoot5, "config", "brand.json");
|
|
@@ -14830,18 +14936,18 @@ app8.post("/", async (c) => {
|
|
|
14830
14936
|
const botParam = c.req.query("bot");
|
|
14831
14937
|
const botType = botParam === "admin" ? "admin" : botParam === "public" ? "public" : null;
|
|
14832
14938
|
if (!botType) {
|
|
14833
|
-
console.error(`${
|
|
14939
|
+
console.error(`${TAG30} op=reject reason=missing-bot-param`);
|
|
14834
14940
|
return c.json({ ok: false }, 400);
|
|
14835
14941
|
}
|
|
14836
14942
|
const sp = secretPath(botType);
|
|
14837
14943
|
if (!existsSync21(sp)) {
|
|
14838
|
-
console.error(`${
|
|
14944
|
+
console.error(`${TAG30} op=reject reason=no-secret-file botType=${botType}`);
|
|
14839
14945
|
return c.json({ ok: false }, 401);
|
|
14840
14946
|
}
|
|
14841
14947
|
const expected = readFileSync27(sp, "utf-8").trim();
|
|
14842
14948
|
const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
|
|
14843
14949
|
if (got !== expected) {
|
|
14844
|
-
console.error(`${
|
|
14950
|
+
console.error(`${TAG30} op=reject reason=bad-secret botType=${botType}`);
|
|
14845
14951
|
return c.json({ ok: false }, 401);
|
|
14846
14952
|
}
|
|
14847
14953
|
let update;
|
|
@@ -14852,40 +14958,40 @@ app8.post("/", async (c) => {
|
|
|
14852
14958
|
}
|
|
14853
14959
|
const account = resolveAccount();
|
|
14854
14960
|
if (!account) {
|
|
14855
|
-
console.error(`${
|
|
14961
|
+
console.error(`${TAG30} op=reject reason=no-account`);
|
|
14856
14962
|
return c.json({ ok: true }, 200);
|
|
14857
14963
|
}
|
|
14858
14964
|
const decision = routeTelegramUpdate({ update, botType, config: account.config.telegram ?? {} });
|
|
14859
14965
|
if (decision.kind === "ignore") {
|
|
14860
|
-
console.error(`${
|
|
14966
|
+
console.error(`${TAG30} op=ignore reason=${decision.reason}`);
|
|
14861
14967
|
return c.json({ ok: true }, 200);
|
|
14862
14968
|
}
|
|
14863
14969
|
if (decision.kind === "denied") {
|
|
14864
|
-
console.error(`${
|
|
14970
|
+
console.error(`${TAG30} op=denied reason=${decision.reason} agentType=${decision.agentType}`);
|
|
14865
14971
|
return c.json({ ok: true }, 200);
|
|
14866
14972
|
}
|
|
14867
14973
|
const role = decision.agentType === "admin" ? "admin" : "public";
|
|
14868
14974
|
const agentSlug = role === "admin" ? "admin" : resolveDefaultAgentSlug(account.accountDir);
|
|
14869
14975
|
if (!agentSlug) {
|
|
14870
|
-
console.error(`${
|
|
14976
|
+
console.error(`${TAG30} op=reject reason=no-default-agent`);
|
|
14871
14977
|
return c.json({ ok: true }, 200);
|
|
14872
14978
|
}
|
|
14873
|
-
console.error(`${
|
|
14979
|
+
console.error(`${TAG30} op=update accountId=${account.accountId} from=${decision.senderId}`);
|
|
14874
14980
|
const botToken = botType === "admin" ? account.config.telegram?.adminBotToken : account.config.telegram?.publicBotToken;
|
|
14875
14981
|
const { senderId, chatId, text } = decision;
|
|
14876
14982
|
const gateway = getTelegramGateway();
|
|
14877
14983
|
if (!gateway) {
|
|
14878
|
-
console.error(`${
|
|
14984
|
+
console.error(`${TAG30} op=reject reason=gateway-not-ready from=${senderId}`);
|
|
14879
14985
|
return c.json({ ok: true }, 200);
|
|
14880
14986
|
}
|
|
14881
14987
|
const reply = async (replyText) => {
|
|
14882
14988
|
if (!botToken) {
|
|
14883
14989
|
const reason = account.config.telegram ? "no-bot-token" : "no-telegram-config";
|
|
14884
|
-
console.error(`${
|
|
14990
|
+
console.error(`${TAG30} op=reply-dropped reason=${reason} botType=${botType} from=${senderId}`);
|
|
14885
14991
|
return;
|
|
14886
14992
|
}
|
|
14887
14993
|
const sent = await sendTelegramText(botToken, chatId, replyText);
|
|
14888
|
-
console.error(`${
|
|
14994
|
+
console.error(`${TAG30} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
|
|
14889
14995
|
};
|
|
14890
14996
|
void gateway.handleInbound({
|
|
14891
14997
|
accountId: account.accountId,
|
|
@@ -14903,7 +15009,7 @@ var telegram_default = app8;
|
|
|
14903
15009
|
// server/routes/quickbooks.ts
|
|
14904
15010
|
import { join as join28 } from "path";
|
|
14905
15011
|
import { existsSync as existsSync22, readFileSync as readFileSync28, writeFileSync as writeFileSync10, mkdirSync as mkdirSync5, rmSync, renameSync as renameSync5 } from "fs";
|
|
14906
|
-
var
|
|
15012
|
+
var TAG31 = "[quickbooks]";
|
|
14907
15013
|
var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
|
|
14908
15014
|
var STATE_RE = /^[A-Za-z0-9_-]+$/;
|
|
14909
15015
|
function pendingPath(accountDir, state) {
|
|
@@ -14916,14 +15022,14 @@ async function completeConsent(args) {
|
|
|
14916
15022
|
const { accountDir, code, realmId } = args;
|
|
14917
15023
|
const state = args.state;
|
|
14918
15024
|
if (!state || !STATE_RE.test(state) || !existsSync22(pendingPath(accountDir, state))) {
|
|
14919
|
-
console.error(`${
|
|
15025
|
+
console.error(`${TAG31} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
|
|
14920
15026
|
return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
|
|
14921
15027
|
}
|
|
14922
15028
|
const claimFile = `${pendingPath(accountDir, state)}.claimed`;
|
|
14923
15029
|
try {
|
|
14924
15030
|
renameSync5(pendingPath(accountDir, state), claimFile);
|
|
14925
15031
|
} catch {
|
|
14926
|
-
console.error(`${
|
|
15032
|
+
console.error(`${TAG31} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
14927
15033
|
return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
|
|
14928
15034
|
}
|
|
14929
15035
|
try {
|
|
@@ -14931,14 +15037,14 @@ async function completeConsent(args) {
|
|
|
14931
15037
|
try {
|
|
14932
15038
|
pending = JSON.parse(readFileSync28(claimFile, "utf-8"));
|
|
14933
15039
|
} catch {
|
|
14934
|
-
console.error(`${
|
|
15040
|
+
console.error(`${TAG31} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
14935
15041
|
return { ok: false, status: 400, message: "Authorization state could not be read.", stateValid: false };
|
|
14936
15042
|
}
|
|
14937
15043
|
if (Date.now() > pending.expiresAt) {
|
|
14938
|
-
console.error(`${
|
|
15044
|
+
console.error(`${TAG31} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
14939
15045
|
return { ok: false, status: 400, message: "Authorization request expired. Generate a new consent link.", stateValid: false };
|
|
14940
15046
|
}
|
|
14941
|
-
console.error(`${
|
|
15047
|
+
console.error(`${TAG31} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
|
|
14942
15048
|
if (!code || !realmId) {
|
|
14943
15049
|
return { ok: false, status: 400, message: "Missing code or realmId in the callback.", stateValid: true };
|
|
14944
15050
|
}
|
|
@@ -14962,12 +15068,12 @@ async function completeConsent(args) {
|
|
|
14962
15068
|
});
|
|
14963
15069
|
const text = await res.text();
|
|
14964
15070
|
if (!res.ok) {
|
|
14965
|
-
console.error(`${
|
|
15071
|
+
console.error(`${TAG31} op=token-exchange realmId=${realmId} persisted=false`);
|
|
14966
15072
|
return { ok: false, status: 502, message: `Token exchange failed (${res.status}). Generate a new consent link to retry.`, stateValid: true };
|
|
14967
15073
|
}
|
|
14968
15074
|
const body = JSON.parse(text);
|
|
14969
15075
|
if (!body.refresh_token) {
|
|
14970
|
-
console.error(`${
|
|
15076
|
+
console.error(`${TAG31} op=token-exchange realmId=${realmId} persisted=false`);
|
|
14971
15077
|
return { ok: false, status: 502, message: "Token exchange returned no refresh token.", stateValid: true };
|
|
14972
15078
|
}
|
|
14973
15079
|
const now = Date.now();
|
|
@@ -14980,7 +15086,7 @@ async function completeConsent(args) {
|
|
|
14980
15086
|
mkdirSync5(join28(accountDir, "secrets"), { recursive: true });
|
|
14981
15087
|
writeFileSync10(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
|
|
14982
15088
|
const persisted = JSON.parse(readFileSync28(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
|
|
14983
|
-
console.error(`${
|
|
15089
|
+
console.error(`${TAG31} op=token-exchange realmId=${realmId} persisted=${persisted}`);
|
|
14984
15090
|
return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
|
|
14985
15091
|
} finally {
|
|
14986
15092
|
rmSync(claimFile, { force: true });
|
|
@@ -14993,7 +15099,7 @@ var app9 = new Hono();
|
|
|
14993
15099
|
app9.get("/callback", async (c) => {
|
|
14994
15100
|
const account = resolveAccount();
|
|
14995
15101
|
if (!account) {
|
|
14996
|
-
console.error(`${
|
|
15102
|
+
console.error(`${TAG31} op=callback stateValid=false realmId= reason=no-account`);
|
|
14997
15103
|
return c.html(page("QuickBooks", "This install has no account configured."), 500);
|
|
14998
15104
|
}
|
|
14999
15105
|
const result = await completeConsent({
|
|
@@ -16769,7 +16875,7 @@ async function purgeAdminConversationJsonls(args) {
|
|
|
16769
16875
|
|
|
16770
16876
|
// app/lib/admin-conversation-cascade.ts
|
|
16771
16877
|
async function cascadeAdminConversationDelete(args) {
|
|
16772
|
-
const
|
|
16878
|
+
const started2 = Date.now();
|
|
16773
16879
|
const fetchImpl = args.fetchImpl ?? fetch;
|
|
16774
16880
|
const log2 = args.logger ?? ((line) => console.log(line));
|
|
16775
16881
|
const id82 = args.sessionId.slice(0, 8);
|
|
@@ -16820,7 +16926,7 @@ async function cascadeAdminConversationDelete(args) {
|
|
|
16820
16926
|
if (!found) {
|
|
16821
16927
|
return { ok: false, reason: "conversation-not-found" };
|
|
16822
16928
|
}
|
|
16823
|
-
const ms = Date.now() -
|
|
16929
|
+
const ms = Date.now() - started2;
|
|
16824
16930
|
const csv = purged.map((s) => s.slice(0, 8)).join(",");
|
|
16825
16931
|
log2(`[admin-conversation-delete] sessionId=${id82} claudeSessionIds=${csv} ms=${ms}`);
|
|
16826
16932
|
return { ok: true, claudeSessionIds: purged, ms };
|
|
@@ -17744,7 +17850,7 @@ function resolveTunnelUrl() {
|
|
|
17744
17850
|
if (!state) return null;
|
|
17745
17851
|
return `https://${hostname2}.${state.domain}`;
|
|
17746
17852
|
}
|
|
17747
|
-
var
|
|
17853
|
+
var TAG32 = "[claude-session-manager:wrapper]";
|
|
17748
17854
|
async function refuseIfClaudeAuthDead(c, route, sessionId) {
|
|
17749
17855
|
const auth = await ensureAuth();
|
|
17750
17856
|
if (auth.status !== "dead" && auth.status !== "missing") return null;
|
|
@@ -17762,12 +17868,12 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
17762
17868
|
const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
|
|
17763
17869
|
const ownerMs = Date.now() - ownerStart;
|
|
17764
17870
|
const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
|
|
17765
|
-
console.log(`${
|
|
17871
|
+
console.log(`${TAG32} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
|
|
17766
17872
|
const dormantPlugins = computeDormantPlugins(args.senderId);
|
|
17767
17873
|
const activePlugins = computeActivePlugins(args.senderId);
|
|
17768
17874
|
const specialistDomains = computeSpecialistDomains(args.senderId);
|
|
17769
17875
|
const tunnelUrl = resolveTunnelUrl();
|
|
17770
|
-
console.log(`${
|
|
17876
|
+
console.log(`${TAG32} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
|
|
17771
17877
|
const upstreamPayload = JSON.stringify({
|
|
17772
17878
|
senderId: args.senderId,
|
|
17773
17879
|
// Task 205 — pass userId through to the manager so it lands as
|
|
@@ -17794,24 +17900,24 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
17794
17900
|
// unshapely values.
|
|
17795
17901
|
conversationNodeId: args.conversationNodeId
|
|
17796
17902
|
});
|
|
17797
|
-
console.log(`${
|
|
17903
|
+
console.log(`${TAG32} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
|
|
17798
17904
|
const forwardStart = Date.now();
|
|
17799
17905
|
const upstream = await fetch(`${managerBase("claude-session-manager:wrapper")}/public-spawn`, {
|
|
17800
17906
|
method: "POST",
|
|
17801
17907
|
headers: { "content-type": "application/json" },
|
|
17802
17908
|
body: upstreamPayload
|
|
17803
17909
|
}).catch((err) => {
|
|
17804
|
-
console.error(`${
|
|
17910
|
+
console.error(`${TAG32} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
|
|
17805
17911
|
return null;
|
|
17806
17912
|
});
|
|
17807
17913
|
if (!upstream) return {
|
|
17808
17914
|
response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
|
|
17809
17915
|
claudeSessionId: null
|
|
17810
17916
|
};
|
|
17811
|
-
console.log(`${
|
|
17917
|
+
console.log(`${TAG32} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
|
|
17812
17918
|
if (args.initialMessage) {
|
|
17813
17919
|
const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
|
|
17814
|
-
console.log(`${
|
|
17920
|
+
console.log(`${TAG32} initial-message-inlined bytes=${inputBytes}`);
|
|
17815
17921
|
}
|
|
17816
17922
|
const bodyText = await upstream.text().catch(() => "");
|
|
17817
17923
|
let claudeSessionId = null;
|
|
@@ -17846,7 +17952,7 @@ app20.post("/", async (c) => {
|
|
|
17846
17952
|
if (refusal) return refusal;
|
|
17847
17953
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
17848
17954
|
if (!senderId) {
|
|
17849
|
-
console.error(`${
|
|
17955
|
+
console.error(`${TAG32} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
|
|
17850
17956
|
return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
17851
17957
|
}
|
|
17852
17958
|
const userId = getUserIdForSession(cacheKey) ?? void 0;
|
|
@@ -17855,7 +17961,7 @@ app20.post("/", async (c) => {
|
|
|
17855
17961
|
const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
|
|
17856
17962
|
const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
|
|
17857
17963
|
const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
|
|
17858
|
-
console.log(`${
|
|
17964
|
+
console.log(`${TAG32} 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"}`);
|
|
17859
17965
|
const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
|
|
17860
17966
|
const { response, claudeSessionId } = await performSpawnWithInitialMessage({
|
|
17861
17967
|
senderId,
|
|
@@ -17874,13 +17980,13 @@ app20.post("/", async (c) => {
|
|
|
17874
17980
|
claudeSessionId,
|
|
17875
17981
|
senderId
|
|
17876
17982
|
);
|
|
17877
|
-
console.log(`${
|
|
17983
|
+
console.log(`${TAG32} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
|
|
17878
17984
|
return response;
|
|
17879
17985
|
});
|
|
17880
17986
|
var claude_sessions_default = app20;
|
|
17881
17987
|
|
|
17882
17988
|
// server/routes/admin/log-ingest.ts
|
|
17883
|
-
var
|
|
17989
|
+
var TAG33 = "[log-ingest]";
|
|
17884
17990
|
var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
|
|
17885
17991
|
var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
17886
17992
|
var MAX_LINE_BYTES = 4096;
|
|
@@ -17891,7 +17997,7 @@ function isLoopbackAddr2(addr) {
|
|
|
17891
17997
|
app21.post("/", async (c) => {
|
|
17892
17998
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17893
17999
|
if (!isLoopbackAddr2(remoteAddr)) {
|
|
17894
|
-
console.error(`${
|
|
18000
|
+
console.error(`${TAG33} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17895
18001
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
17896
18002
|
}
|
|
17897
18003
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17900,7 +18006,7 @@ app21.post("/", async (c) => {
|
|
|
17900
18006
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17901
18007
|
const offender = tokens.find((t) => !isLoopbackAddr2(t));
|
|
17902
18008
|
if (offender !== void 0) {
|
|
17903
|
-
console.error(`${
|
|
18009
|
+
console.error(`${TAG33} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17904
18010
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
17905
18011
|
}
|
|
17906
18012
|
}
|
|
@@ -17942,18 +18048,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
17942
18048
|
]);
|
|
17943
18049
|
var app22 = new Hono();
|
|
17944
18050
|
app22.post("/", async (c) => {
|
|
17945
|
-
const
|
|
18051
|
+
const TAG59 = "[admin:events]";
|
|
17946
18052
|
let body;
|
|
17947
18053
|
try {
|
|
17948
18054
|
body = await c.req.json();
|
|
17949
18055
|
} catch (err) {
|
|
17950
18056
|
const detail = err instanceof Error ? err.message : String(err);
|
|
17951
|
-
console.error(`${
|
|
18057
|
+
console.error(`${TAG59} reject reason=body-not-json detail=${detail}`);
|
|
17952
18058
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
17953
18059
|
}
|
|
17954
18060
|
const event = typeof body.event === "string" ? body.event : "";
|
|
17955
18061
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
17956
|
-
console.error(`${
|
|
18062
|
+
console.error(`${TAG59} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
17957
18063
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
17958
18064
|
}
|
|
17959
18065
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -20800,7 +20906,7 @@ app24.get("/", requireAdminSession, async (c) => {
|
|
|
20800
20906
|
if (labels.includes("FileArtifact")) {
|
|
20801
20907
|
void reconcileFileIndexDebounced(accountId);
|
|
20802
20908
|
}
|
|
20803
|
-
const
|
|
20909
|
+
const started2 = Date.now();
|
|
20804
20910
|
const session = getSession();
|
|
20805
20911
|
try {
|
|
20806
20912
|
const res = await hybrid(session, embed, {
|
|
@@ -20811,7 +20917,7 @@ app24.get("/", requireAdminSession, async (c) => {
|
|
|
20811
20917
|
degradeOnEmbedFailure: true,
|
|
20812
20918
|
vectorThreshold
|
|
20813
20919
|
});
|
|
20814
|
-
const total = Date.now() -
|
|
20920
|
+
const total = Date.now() - started2;
|
|
20815
20921
|
if (res.embedError) {
|
|
20816
20922
|
console.error(`[graph-search] embed-unavailable err="${res.embedError}" \u2014 bm25-only`);
|
|
20817
20923
|
console.error(`[graph-search] embed-degraded query="${q2}" results=${res.results.length}`);
|
|
@@ -20895,7 +21001,7 @@ app24.get("/", requireAdminSession, async (c) => {
|
|
|
20895
21001
|
threshold: res.threshold
|
|
20896
21002
|
});
|
|
20897
21003
|
} catch (err) {
|
|
20898
|
-
const elapsed = Date.now() -
|
|
21004
|
+
const elapsed = Date.now() - started2;
|
|
20899
21005
|
const message = err instanceof Error ? err.message : String(err);
|
|
20900
21006
|
console.error(`[graph-search] neo4j-unreachable query="${q2}" ms=${elapsed} err="${message}"`);
|
|
20901
21007
|
return c.json({ error: `Graph search unavailable: ${message}` }, 503);
|
|
@@ -21212,7 +21318,7 @@ async function handleDefault(c, accountId) {
|
|
|
21212
21318
|
);
|
|
21213
21319
|
}
|
|
21214
21320
|
}
|
|
21215
|
-
const
|
|
21321
|
+
const started2 = Date.now();
|
|
21216
21322
|
const session = getSession();
|
|
21217
21323
|
try {
|
|
21218
21324
|
const countCypher = includeTrashed ? DEFAULT_COUNT_CYPHER_INCLUDE_TRASHED : DEFAULT_COUNT_CYPHER;
|
|
@@ -21231,7 +21337,7 @@ async function handleDefault(c, accountId) {
|
|
|
21231
21337
|
const rawEdges = record?.get("edges") ?? [];
|
|
21232
21338
|
return { overLimit: false, matched, rawNodes, rawEdges };
|
|
21233
21339
|
});
|
|
21234
|
-
const elapsed = Date.now() -
|
|
21340
|
+
const elapsed = Date.now() - started2;
|
|
21235
21341
|
if (result.overLimit) {
|
|
21236
21342
|
console.error(
|
|
21237
21343
|
`[graph-page] load rejected reason=ceiling-exceeded account=${accountId} labels=${labels.join(",")} matched=${result.matched} ceiling=${CEILING} ms=${elapsed}`
|
|
@@ -21254,7 +21360,7 @@ async function handleDefault(c, accountId) {
|
|
|
21254
21360
|
);
|
|
21255
21361
|
return c.json({ nodes, edges });
|
|
21256
21362
|
} catch (err) {
|
|
21257
|
-
const elapsed = Date.now() -
|
|
21363
|
+
const elapsed = Date.now() - started2;
|
|
21258
21364
|
const message = err instanceof Error ? err.message : String(err);
|
|
21259
21365
|
console.error(`[graph-page] error stage=fetch reason="${message}" ms=${elapsed}`);
|
|
21260
21366
|
return c.json({ error: `Graph data unavailable: ${message}` }, 503);
|
|
@@ -21269,7 +21375,7 @@ async function handleDefaultTopLevel(c, accountId, flags) {
|
|
|
21269
21375
|
const { includeTrashed, includeAgentActions } = flags;
|
|
21270
21376
|
const labels = [...getTopLevelLabelAllowlist()].filter((l) => !HIDDEN_BY_DEFAULT_LABELS.has(l));
|
|
21271
21377
|
const agentActionLabels = [...AGENT_ACTION_LABELS];
|
|
21272
|
-
const
|
|
21378
|
+
const started2 = Date.now();
|
|
21273
21379
|
const session = getSession();
|
|
21274
21380
|
try {
|
|
21275
21381
|
const countCypher = includeTrashed ? DEFAULT_COUNT_CYPHER_INCLUDE_TRASHED : DEFAULT_COUNT_CYPHER;
|
|
@@ -21290,7 +21396,7 @@ async function handleDefaultTopLevel(c, accountId, flags) {
|
|
|
21290
21396
|
const rawEdges = record?.get("edges") ?? [];
|
|
21291
21397
|
return { total, rawNodes, rawEdges };
|
|
21292
21398
|
});
|
|
21293
|
-
const elapsed = Date.now() -
|
|
21399
|
+
const elapsed = Date.now() - started2;
|
|
21294
21400
|
const warnedClasses = /* @__PURE__ */ new Set();
|
|
21295
21401
|
const conversationWarnings = { loggedMissingMessageCount: false };
|
|
21296
21402
|
const nodes = result.rawNodes.map((n) => pruneNode(n, warnedClasses, conversationWarnings));
|
|
@@ -21307,7 +21413,7 @@ async function handleDefaultTopLevel(c, accountId, flags) {
|
|
|
21307
21413
|
);
|
|
21308
21414
|
return c.json({ nodes, edges, total: result.total, truncated });
|
|
21309
21415
|
} catch (err) {
|
|
21310
|
-
const elapsed = Date.now() -
|
|
21416
|
+
const elapsed = Date.now() - started2;
|
|
21311
21417
|
const message = err instanceof Error ? err.message : String(err);
|
|
21312
21418
|
console.error(`[graph-page] error stage=fetch-top-level reason="${message}" ms=${elapsed}`);
|
|
21313
21419
|
return c.json({ error: `Graph data unavailable: ${message}` }, 503);
|
|
@@ -21341,7 +21447,7 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
21341
21447
|
const rawLimit = c.req.query("limit");
|
|
21342
21448
|
const parsedLimit = rawLimit ? parseInt(rawLimit, 10) : NEIGHBOURHOOD_SEARCH_DEFAULT_LIMIT;
|
|
21343
21449
|
const searchLimit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, NEIGHBOURHOOD_SEARCH_MAX_LIMIT) : NEIGHBOURHOOD_SEARCH_DEFAULT_LIMIT;
|
|
21344
|
-
const
|
|
21450
|
+
const started2 = Date.now();
|
|
21345
21451
|
const session = getSession();
|
|
21346
21452
|
try {
|
|
21347
21453
|
let allowedIds = null;
|
|
@@ -21393,7 +21499,7 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
21393
21499
|
const record = result.records[0];
|
|
21394
21500
|
const rawNodes = record?.get("nodes") ?? [];
|
|
21395
21501
|
const rawEdges = record?.get("edges") ?? [];
|
|
21396
|
-
const elapsed = Date.now() -
|
|
21502
|
+
const elapsed = Date.now() - started2;
|
|
21397
21503
|
if (rawNodes.length === 0) {
|
|
21398
21504
|
console.error(
|
|
21399
21505
|
`[graph-page] load rejected reason=not-found account=${accountId} elementId=${elementId} ms=${elapsed}`
|
|
@@ -21435,7 +21541,7 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
21435
21541
|
}
|
|
21436
21542
|
return c.json({ nodes, edges });
|
|
21437
21543
|
} catch (err) {
|
|
21438
|
-
const elapsed = Date.now() -
|
|
21544
|
+
const elapsed = Date.now() - started2;
|
|
21439
21545
|
const message = err instanceof Error ? err.message : String(err);
|
|
21440
21546
|
console.error(`[graph-page] error stage=fetch reason="${message}" ms=${elapsed}`);
|
|
21441
21547
|
return c.json({ error: `Graph data unavailable: ${message}` }, 503);
|
|
@@ -21855,7 +21961,7 @@ app26.post("/", requireAdminSession, async (c) => {
|
|
|
21855
21961
|
}
|
|
21856
21962
|
by = body.by;
|
|
21857
21963
|
}
|
|
21858
|
-
const
|
|
21964
|
+
const started2 = Date.now();
|
|
21859
21965
|
const session = getSession();
|
|
21860
21966
|
try {
|
|
21861
21967
|
const lookup = await session.run(
|
|
@@ -21865,7 +21971,7 @@ app26.post("/", requireAdminSession, async (c) => {
|
|
|
21865
21971
|
{ elementId, accountId }
|
|
21866
21972
|
);
|
|
21867
21973
|
if (lookup.records.length === 0) {
|
|
21868
|
-
const elapsed2 = Date.now() -
|
|
21974
|
+
const elapsed2 = Date.now() - started2;
|
|
21869
21975
|
console.error(
|
|
21870
21976
|
`[graph-page] delete account=${accountId} elementId=${elementId} by=${by} labels= route=trash result=not-found ms=${elapsed2}`
|
|
21871
21977
|
);
|
|
@@ -21875,7 +21981,7 @@ app26.post("/", requireAdminSession, async (c) => {
|
|
|
21875
21981
|
const sessionId = lookup.records[0].get("sessionId");
|
|
21876
21982
|
if (preflightLabels.includes("AdminConversation")) {
|
|
21877
21983
|
if (!sessionId) {
|
|
21878
|
-
const elapsed3 = Date.now() -
|
|
21984
|
+
const elapsed3 = Date.now() - started2;
|
|
21879
21985
|
console.error(
|
|
21880
21986
|
`[graph-page] delete account=${accountId} elementId=${elementId} by=${by} labels=${preflightLabels.join(",")} route=cascade result=error ms=${elapsed3} err="AdminConversation node has no sessionId"`
|
|
21881
21987
|
);
|
|
@@ -21891,7 +21997,7 @@ app26.post("/", requireAdminSession, async (c) => {
|
|
|
21891
21997
|
accountId,
|
|
21892
21998
|
managerBase: managerBaseUrl
|
|
21893
21999
|
});
|
|
21894
|
-
const elapsed2 = Date.now() -
|
|
22000
|
+
const elapsed2 = Date.now() - started2;
|
|
21895
22001
|
const labelSummary2 = preflightLabels.join(",");
|
|
21896
22002
|
if (outcome.ok) {
|
|
21897
22003
|
console.error(
|
|
@@ -21968,7 +22074,7 @@ app26.post("/", requireAdminSession, async (c) => {
|
|
|
21968
22074
|
elementId,
|
|
21969
22075
|
by
|
|
21970
22076
|
});
|
|
21971
|
-
const elapsed = Date.now() -
|
|
22077
|
+
const elapsed = Date.now() - started2;
|
|
21972
22078
|
const labelSummary = (result.labels.length > 0 ? result.labels : preflightLabels).join(",");
|
|
21973
22079
|
console.error(
|
|
21974
22080
|
`[graph-page] delete account=${accountId} elementId=${elementId} by=${by} labels=${labelSummary} route=trash result=ok ms=${elapsed}`
|
|
@@ -21982,7 +22088,7 @@ app26.post("/", requireAdminSession, async (c) => {
|
|
|
21982
22088
|
};
|
|
21983
22089
|
return c.json(response);
|
|
21984
22090
|
} catch (err) {
|
|
21985
|
-
const elapsed = Date.now() -
|
|
22091
|
+
const elapsed = Date.now() - started2;
|
|
21986
22092
|
const message = err instanceof Error ? err.message : String(err);
|
|
21987
22093
|
console.error(
|
|
21988
22094
|
`[graph-page] delete account=${accountId} elementId=${elementId} by=${by} labels= result=error ms=${elapsed} err="${message}"`
|
|
@@ -22014,7 +22120,7 @@ app27.post("/", requireAdminSession, async (c) => {
|
|
|
22014
22120
|
if (!elementId) {
|
|
22015
22121
|
return c.json({ error: "elementId required" }, 400);
|
|
22016
22122
|
}
|
|
22017
|
-
const
|
|
22123
|
+
const started2 = Date.now();
|
|
22018
22124
|
const session = getSession();
|
|
22019
22125
|
try {
|
|
22020
22126
|
const lookup = await session.run(
|
|
@@ -22024,7 +22130,7 @@ app27.post("/", requireAdminSession, async (c) => {
|
|
|
22024
22130
|
{ elementId, accountId }
|
|
22025
22131
|
);
|
|
22026
22132
|
if (lookup.records.length === 0) {
|
|
22027
|
-
const elapsed2 = Date.now() -
|
|
22133
|
+
const elapsed2 = Date.now() - started2;
|
|
22028
22134
|
console.error(
|
|
22029
22135
|
`[graph-page] restore account=${accountId} elementId=${elementId} labels= result=not-found ms=${elapsed2}`
|
|
22030
22136
|
);
|
|
@@ -22032,7 +22138,7 @@ app27.post("/", requireAdminSession, async (c) => {
|
|
|
22032
22138
|
}
|
|
22033
22139
|
const preflightLabels = (lookup.records[0].get("labels") ?? []).filter((l) => l !== "Trashed");
|
|
22034
22140
|
const result = await restoreNode({ session, accountId, elementId });
|
|
22035
|
-
const elapsed = Date.now() -
|
|
22141
|
+
const elapsed = Date.now() - started2;
|
|
22036
22142
|
const labelSummary = (result.labels.length > 0 ? result.labels : preflightLabels).join(",");
|
|
22037
22143
|
console.error(
|
|
22038
22144
|
`[graph-page] restore account=${accountId} elementId=${elementId} labels=${labelSummary} result=ok ms=${elapsed}`
|
|
@@ -22044,7 +22150,7 @@ app27.post("/", requireAdminSession, async (c) => {
|
|
|
22044
22150
|
};
|
|
22045
22151
|
return c.json(response);
|
|
22046
22152
|
} catch (err) {
|
|
22047
|
-
const elapsed = Date.now() -
|
|
22153
|
+
const elapsed = Date.now() - started2;
|
|
22048
22154
|
const message = err instanceof Error ? err.message : String(err);
|
|
22049
22155
|
if (message.includes("already holds")) {
|
|
22050
22156
|
console.error(
|
|
@@ -22075,7 +22181,7 @@ app28.get("/", requireAdminSession, async (c) => {
|
|
|
22075
22181
|
return c.json({ error: "Account not found for session" }, 401);
|
|
22076
22182
|
}
|
|
22077
22183
|
const includeAgentActions = c.req.query("includeAgentActions") === "1";
|
|
22078
|
-
const
|
|
22184
|
+
const started2 = Date.now();
|
|
22079
22185
|
const session = getSession();
|
|
22080
22186
|
try {
|
|
22081
22187
|
const allowedSet = new Set(getTopLevelLabelAllowlist());
|
|
@@ -22094,14 +22200,14 @@ app28.get("/", requireAdminSession, async (c) => {
|
|
|
22094
22200
|
if (b.relDegree !== a.relDegree) return b.relDegree - a.relDegree;
|
|
22095
22201
|
return a.label.localeCompare(b.label);
|
|
22096
22202
|
});
|
|
22097
|
-
const elapsed = Date.now() -
|
|
22203
|
+
const elapsed = Date.now() - started2;
|
|
22098
22204
|
const summary = labels.map((l) => `${l.label}:${l.nodeCount}:${l.relDegree}`).join(",");
|
|
22099
22205
|
console.error(
|
|
22100
22206
|
`[graph-page] labels-in-graph account=${accountId} agentActions=${includeAgentActions} labels=${summary} ms=${elapsed}`
|
|
22101
22207
|
);
|
|
22102
22208
|
return c.json({ labels });
|
|
22103
22209
|
} catch (err) {
|
|
22104
|
-
const elapsed = Date.now() -
|
|
22210
|
+
const elapsed = Date.now() - started2;
|
|
22105
22211
|
const message = err instanceof Error ? err.message : String(err);
|
|
22106
22212
|
console.error(
|
|
22107
22213
|
`[graph-page] labels-in-graph-rejected account=${accountId} reason="${message}" ms=${elapsed}`
|
|
@@ -22145,7 +22251,7 @@ app29.get("/", requireAdminSession, async (c) => {
|
|
|
22145
22251
|
console.error('[graph-page] default-view-rejected reason="missing account or user context"');
|
|
22146
22252
|
return c.json({ error: "Account and user context required for default view" }, 401);
|
|
22147
22253
|
}
|
|
22148
|
-
const
|
|
22254
|
+
const started2 = Date.now();
|
|
22149
22255
|
const session = getSession();
|
|
22150
22256
|
try {
|
|
22151
22257
|
const result = await session.executeRead(async (tx) => {
|
|
@@ -22156,13 +22262,13 @@ app29.get("/", requireAdminSession, async (c) => {
|
|
|
22156
22262
|
const rawIncludeTrashed = record?.get("includeTrashed");
|
|
22157
22263
|
const labels = Array.isArray(rawLabels) ? rawLabels : [];
|
|
22158
22264
|
const includeTrashed = rawIncludeTrashed === true;
|
|
22159
|
-
const elapsed = Date.now() -
|
|
22265
|
+
const elapsed = Date.now() - started2;
|
|
22160
22266
|
console.error(
|
|
22161
22267
|
`[graph-page] default-view-load account=${accountId} labels=${labels.join(",")} includeTrashed=${includeTrashed ? 1 : 0} ms=${elapsed}`
|
|
22162
22268
|
);
|
|
22163
22269
|
return c.json({ labels, includeTrashed });
|
|
22164
22270
|
} catch (err) {
|
|
22165
|
-
const elapsed = Date.now() -
|
|
22271
|
+
const elapsed = Date.now() - started2;
|
|
22166
22272
|
const message = err instanceof Error ? err.message : String(err);
|
|
22167
22273
|
console.error(
|
|
22168
22274
|
`[graph-page] default-view-rejected op=load account=${accountId} reason="${message}" ms=${elapsed}`
|
|
@@ -23126,7 +23232,7 @@ async function sample() {
|
|
|
23126
23232
|
}
|
|
23127
23233
|
var app38 = new Hono();
|
|
23128
23234
|
app38.get("/", async (c) => {
|
|
23129
|
-
const
|
|
23235
|
+
const started2 = Date.now();
|
|
23130
23236
|
if (!inflight) {
|
|
23131
23237
|
inflight = sample().finally(() => {
|
|
23132
23238
|
inflight = null;
|
|
@@ -23134,7 +23240,7 @@ app38.get("/", async (c) => {
|
|
|
23134
23240
|
}
|
|
23135
23241
|
try {
|
|
23136
23242
|
const snap = await inflight;
|
|
23137
|
-
const ms = Date.now() -
|
|
23243
|
+
const ms = Date.now() - started2;
|
|
23138
23244
|
const fmt = (n) => n === null ? "null" : n.toFixed(3);
|
|
23139
23245
|
console.log(
|
|
23140
23246
|
`[system-stats] poll cpuPct=${fmt(snap.cpuPct)} memUsedPct=${fmt(snap.memUsedPct)} loadAvg1=${snap.loadAvg1.toFixed(2)} swapUsedPct=${fmt(snap.swapUsedPct)} platform=${snap.platform} ms=${ms}`
|
|
@@ -23347,7 +23453,7 @@ function managerLogFollowUrl(sessionId, opts) {
|
|
|
23347
23453
|
}
|
|
23348
23454
|
|
|
23349
23455
|
// server/routes/admin/linkedin-ingest.ts
|
|
23350
|
-
var
|
|
23456
|
+
var TAG34 = "[linkedin-ingest-route]";
|
|
23351
23457
|
var UUID4 = /^[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}$/;
|
|
23352
23458
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
23353
23459
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
@@ -23451,29 +23557,29 @@ app40.post("/", requireAdminSession, async (c) => {
|
|
|
23451
23557
|
try {
|
|
23452
23558
|
body = await c.req.json();
|
|
23453
23559
|
} catch {
|
|
23454
|
-
console.error(
|
|
23560
|
+
console.error(TAG34 + " rejected status=400 reason=schema:body-not-json");
|
|
23455
23561
|
return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
|
|
23456
23562
|
}
|
|
23457
23563
|
const v = validate(body);
|
|
23458
23564
|
if (!v.ok) {
|
|
23459
|
-
console.error(
|
|
23565
|
+
console.error(TAG34 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
|
|
23460
23566
|
return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
|
|
23461
23567
|
}
|
|
23462
23568
|
const envelope = v.envelope;
|
|
23463
23569
|
const cacheKey = c.var.cacheKey ?? "";
|
|
23464
23570
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
23465
23571
|
if (!senderId) {
|
|
23466
|
-
console.error(
|
|
23572
|
+
console.error(TAG34 + " rejected status=500 reason=admin-account-not-resolved");
|
|
23467
23573
|
return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
|
|
23468
23574
|
}
|
|
23469
23575
|
const payloadBytes = JSON.stringify(envelope).length;
|
|
23470
23576
|
console.log(
|
|
23471
|
-
|
|
23577
|
+
TAG34 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
|
|
23472
23578
|
);
|
|
23473
23579
|
const initialMessage = buildInitialMessage(envelope);
|
|
23474
23580
|
const spawnStart = Date.now();
|
|
23475
23581
|
const sessionId = randomUUID13();
|
|
23476
|
-
console.log(
|
|
23582
|
+
console.log(TAG34 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
|
|
23477
23583
|
const spawned = await managerRcSpawn({
|
|
23478
23584
|
sessionId,
|
|
23479
23585
|
initialMessage,
|
|
@@ -23482,12 +23588,12 @@ app40.post("/", requireAdminSession, async (c) => {
|
|
|
23482
23588
|
});
|
|
23483
23589
|
if ("error" in spawned) {
|
|
23484
23590
|
console.error(
|
|
23485
|
-
|
|
23591
|
+
TAG34 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
|
|
23486
23592
|
);
|
|
23487
23593
|
return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
|
|
23488
23594
|
}
|
|
23489
23595
|
console.log(
|
|
23490
|
-
|
|
23596
|
+
TAG34 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
|
|
23491
23597
|
);
|
|
23492
23598
|
return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
|
|
23493
23599
|
});
|
|
@@ -23495,7 +23601,7 @@ var linkedin_ingest_default = app40;
|
|
|
23495
23601
|
|
|
23496
23602
|
// server/routes/admin/post-turn-context.ts
|
|
23497
23603
|
import neo4j3 from "neo4j-driver";
|
|
23498
|
-
var
|
|
23604
|
+
var TAG35 = "[post-turn-context]";
|
|
23499
23605
|
var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
|
|
23500
23606
|
"embedding",
|
|
23501
23607
|
"passwordHash",
|
|
@@ -23537,7 +23643,7 @@ var app41 = new Hono();
|
|
|
23537
23643
|
app41.get("/", async (c) => {
|
|
23538
23644
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
23539
23645
|
if (!isLoopbackAddr3(remoteAddr)) {
|
|
23540
|
-
console.error(`${
|
|
23646
|
+
console.error(`${TAG35} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
23541
23647
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
23542
23648
|
}
|
|
23543
23649
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -23546,7 +23652,7 @@ app41.get("/", async (c) => {
|
|
|
23546
23652
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
23547
23653
|
const offender = tokens.find((t) => !isLoopbackAddr3(t));
|
|
23548
23654
|
if (offender !== void 0) {
|
|
23549
|
-
console.error(`${
|
|
23655
|
+
console.error(`${TAG35} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
23550
23656
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
23551
23657
|
}
|
|
23552
23658
|
}
|
|
@@ -23555,7 +23661,7 @@ app41.get("/", async (c) => {
|
|
|
23555
23661
|
if (!sessionId) return c.json({ error: "sessionId required" }, 400);
|
|
23556
23662
|
if (!accountId) return c.json({ error: "accountId required" }, 400);
|
|
23557
23663
|
const session = getSession();
|
|
23558
|
-
const
|
|
23664
|
+
const started2 = Date.now();
|
|
23559
23665
|
try {
|
|
23560
23666
|
const cypher = `
|
|
23561
23667
|
MATCH (c:Conversation {sessionId: $sessionId, accountId: $accountId})
|
|
@@ -23576,18 +23682,18 @@ app41.get("/", async (c) => {
|
|
|
23576
23682
|
sortKey: String(r.get("sortKey") ?? "")
|
|
23577
23683
|
}));
|
|
23578
23684
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
23579
|
-
const total = Date.now() -
|
|
23685
|
+
const total = Date.now() - started2;
|
|
23580
23686
|
console.log(
|
|
23581
|
-
`${
|
|
23687
|
+
`${TAG35} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
|
|
23582
23688
|
);
|
|
23583
23689
|
return c.json({
|
|
23584
23690
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
23585
23691
|
});
|
|
23586
23692
|
} catch (err) {
|
|
23587
|
-
const elapsed = Date.now() -
|
|
23693
|
+
const elapsed = Date.now() - started2;
|
|
23588
23694
|
const message = err instanceof Error ? err.message : String(err);
|
|
23589
23695
|
console.error(
|
|
23590
|
-
`${
|
|
23696
|
+
`${TAG35} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
|
|
23591
23697
|
);
|
|
23592
23698
|
return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
|
|
23593
23699
|
} finally {
|
|
@@ -23700,7 +23806,7 @@ function formatPreviousContext(writes) {
|
|
|
23700
23806
|
}
|
|
23701
23807
|
|
|
23702
23808
|
// server/routes/admin/public-session-context.ts
|
|
23703
|
-
var
|
|
23809
|
+
var TAG36 = "[public-session-context]";
|
|
23704
23810
|
function isLoopbackAddr4(addr) {
|
|
23705
23811
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
23706
23812
|
}
|
|
@@ -23708,7 +23814,7 @@ var app42 = new Hono();
|
|
|
23708
23814
|
app42.get("/", async (c) => {
|
|
23709
23815
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
23710
23816
|
if (!isLoopbackAddr4(remoteAddr)) {
|
|
23711
|
-
console.error(`${
|
|
23817
|
+
console.error(`${TAG36} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
23712
23818
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
23713
23819
|
}
|
|
23714
23820
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -23717,7 +23823,7 @@ app42.get("/", async (c) => {
|
|
|
23717
23823
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
23718
23824
|
const offender = tokens.find((t) => !isLoopbackAddr4(t));
|
|
23719
23825
|
if (offender !== void 0) {
|
|
23720
|
-
console.error(`${
|
|
23826
|
+
console.error(`${TAG36} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
23721
23827
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
23722
23828
|
}
|
|
23723
23829
|
}
|
|
@@ -23726,19 +23832,19 @@ app42.get("/", async (c) => {
|
|
|
23726
23832
|
if (!sliceToken) return c.json({ error: "sliceToken required" }, 400);
|
|
23727
23833
|
if (!accountId) return c.json({ error: "accountId required" }, 400);
|
|
23728
23834
|
const session = getSession();
|
|
23729
|
-
const
|
|
23835
|
+
const started2 = Date.now();
|
|
23730
23836
|
try {
|
|
23731
23837
|
const writes = await fetchSliceWrites(session, sliceToken, accountId);
|
|
23732
|
-
const total = Date.now() -
|
|
23838
|
+
const total = Date.now() - started2;
|
|
23733
23839
|
console.log(
|
|
23734
|
-
`${
|
|
23840
|
+
`${TAG36} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
|
|
23735
23841
|
);
|
|
23736
23842
|
return c.json({ writes });
|
|
23737
23843
|
} catch (err) {
|
|
23738
|
-
const elapsed = Date.now() -
|
|
23844
|
+
const elapsed = Date.now() - started2;
|
|
23739
23845
|
const message = err instanceof Error ? err.message : String(err);
|
|
23740
23846
|
console.error(
|
|
23741
|
-
`${
|
|
23847
|
+
`${TAG36} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
|
|
23742
23848
|
);
|
|
23743
23849
|
return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
|
|
23744
23850
|
} finally {
|
|
@@ -23757,7 +23863,7 @@ function getWebchatGateway() {
|
|
|
23757
23863
|
}
|
|
23758
23864
|
|
|
23759
23865
|
// server/routes/admin/public-session-exit.ts
|
|
23760
|
-
var
|
|
23866
|
+
var TAG37 = "[public-session-exit-route]";
|
|
23761
23867
|
function isLoopbackAddr5(addr) {
|
|
23762
23868
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
23763
23869
|
}
|
|
@@ -23765,7 +23871,7 @@ var app43 = new Hono();
|
|
|
23765
23871
|
app43.post("/", async (c) => {
|
|
23766
23872
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
23767
23873
|
if (!isLoopbackAddr5(remoteAddr)) {
|
|
23768
|
-
console.error(`${
|
|
23874
|
+
console.error(`${TAG37} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
23769
23875
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
23770
23876
|
}
|
|
23771
23877
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -23774,7 +23880,7 @@ app43.post("/", async (c) => {
|
|
|
23774
23880
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
23775
23881
|
const offender = tokens.find((t) => !isLoopbackAddr5(t));
|
|
23776
23882
|
if (offender !== void 0) {
|
|
23777
|
-
console.error(`${
|
|
23883
|
+
console.error(`${TAG37} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
23778
23884
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
23779
23885
|
}
|
|
23780
23886
|
}
|
|
@@ -23788,7 +23894,7 @@ app43.post("/", async (c) => {
|
|
|
23788
23894
|
if (!sessionId) return c.json({ error: "sessionId required" }, 400);
|
|
23789
23895
|
const gateway = getWebchatGateway();
|
|
23790
23896
|
if (!gateway) {
|
|
23791
|
-
console.error(`${
|
|
23897
|
+
console.error(`${TAG37} reject reason=gateway-unset sessionId=${sessionId.slice(0, 8)}`);
|
|
23792
23898
|
return c.json({ error: "webchat-gateway-unset" }, 503);
|
|
23793
23899
|
}
|
|
23794
23900
|
gateway.handlePublicSessionExit(sessionId);
|
|
@@ -23797,7 +23903,7 @@ app43.post("/", async (c) => {
|
|
|
23797
23903
|
var public_session_exit_default = app43;
|
|
23798
23904
|
|
|
23799
23905
|
// server/routes/admin/access-session-evict.ts
|
|
23800
|
-
var
|
|
23906
|
+
var TAG38 = "[access-session-evict]";
|
|
23801
23907
|
function isLoopbackAddr6(addr) {
|
|
23802
23908
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
23803
23909
|
}
|
|
@@ -23805,7 +23911,7 @@ var app44 = new Hono();
|
|
|
23805
23911
|
app44.post("/", async (c) => {
|
|
23806
23912
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
23807
23913
|
if (!isLoopbackAddr6(remoteAddr)) {
|
|
23808
|
-
console.error(`${
|
|
23914
|
+
console.error(`${TAG38} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
23809
23915
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
23810
23916
|
}
|
|
23811
23917
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -23814,7 +23920,7 @@ app44.post("/", async (c) => {
|
|
|
23814
23920
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
23815
23921
|
const offender = tokens.find((t) => !isLoopbackAddr6(t));
|
|
23816
23922
|
if (offender !== void 0) {
|
|
23817
|
-
console.error(`${
|
|
23923
|
+
console.error(`${TAG38} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
23818
23924
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
23819
23925
|
}
|
|
23820
23926
|
}
|
|
@@ -23827,13 +23933,13 @@ app44.post("/", async (c) => {
|
|
|
23827
23933
|
const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
|
|
23828
23934
|
if (!grantId) return c.json({ error: "grantId required" }, 400);
|
|
23829
23935
|
const dropped = evictAccessSessionsByGrant(grantId);
|
|
23830
|
-
console.log(`${
|
|
23936
|
+
console.log(`${TAG38} grantId=${grantId} dropped=${dropped}`);
|
|
23831
23937
|
return c.json({ ok: true, dropped });
|
|
23832
23938
|
});
|
|
23833
23939
|
var access_session_evict_default = app44;
|
|
23834
23940
|
|
|
23835
23941
|
// server/routes/admin/enrol-person.ts
|
|
23836
|
-
var
|
|
23942
|
+
var TAG39 = "[enrol]";
|
|
23837
23943
|
function isLoopbackAddr7(addr) {
|
|
23838
23944
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
23839
23945
|
}
|
|
@@ -23842,7 +23948,7 @@ var app45 = new Hono();
|
|
|
23842
23948
|
app45.post("/", async (c) => {
|
|
23843
23949
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
23844
23950
|
if (!isLoopbackAddr7(remoteAddr)) {
|
|
23845
|
-
console.error(`${
|
|
23951
|
+
console.error(`${TAG39} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
23846
23952
|
return c.json({ error: "enrol-person-loopback-only" }, 403);
|
|
23847
23953
|
}
|
|
23848
23954
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -23851,7 +23957,7 @@ app45.post("/", async (c) => {
|
|
|
23851
23957
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
23852
23958
|
const offender = tokens.find((t) => !isLoopbackAddr7(t));
|
|
23853
23959
|
if (offender !== void 0) {
|
|
23854
|
-
console.error(`${
|
|
23960
|
+
console.error(`${TAG39} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
23855
23961
|
return c.json({ error: "enrol-person-loopback-only" }, 403);
|
|
23856
23962
|
}
|
|
23857
23963
|
}
|
|
@@ -23876,7 +23982,7 @@ app45.post("/", async (c) => {
|
|
|
23876
23982
|
}
|
|
23877
23983
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
23878
23984
|
if (!accountId) {
|
|
23879
|
-
console.error(`${
|
|
23985
|
+
console.error(`${TAG39} op=person result=no-account-id`);
|
|
23880
23986
|
return c.json({ error: "no-account-id" }, 500);
|
|
23881
23987
|
}
|
|
23882
23988
|
let personId;
|
|
@@ -23884,7 +23990,7 @@ app45.post("/", async (c) => {
|
|
|
23884
23990
|
personId = await enrolPerson({ accountId, phone, email, agentSlug });
|
|
23885
23991
|
} catch (err) {
|
|
23886
23992
|
console.error(
|
|
23887
|
-
`${
|
|
23993
|
+
`${TAG39} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
23888
23994
|
);
|
|
23889
23995
|
return c.json({ error: "enrol-failed" }, 500);
|
|
23890
23996
|
}
|
|
@@ -23894,11 +24000,11 @@ app45.post("/", async (c) => {
|
|
|
23894
24000
|
if (g) grant = { grantId: g.grantId, status: g.status, contactValue: g.contactValue };
|
|
23895
24001
|
} catch (err) {
|
|
23896
24002
|
console.error(
|
|
23897
|
-
`${
|
|
24003
|
+
`${TAG39} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
23898
24004
|
);
|
|
23899
24005
|
}
|
|
23900
24006
|
console.log(
|
|
23901
|
-
`${
|
|
24007
|
+
`${TAG39} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
|
|
23902
24008
|
);
|
|
23903
24009
|
return c.json({ personId, grant }, 200);
|
|
23904
24010
|
});
|
|
@@ -24760,7 +24866,7 @@ function toNum(v) {
|
|
|
24760
24866
|
}
|
|
24761
24867
|
|
|
24762
24868
|
// server/routes/admin/tasks-list.ts
|
|
24763
|
-
var
|
|
24869
|
+
var TAG40 = "[task-timer]";
|
|
24764
24870
|
var COMPLETED = /* @__PURE__ */ new Set(["completed"]);
|
|
24765
24871
|
var HIDDEN_FROM_OPEN = /* @__PURE__ */ new Set(["completed", "cancelled"]);
|
|
24766
24872
|
var app50 = new Hono();
|
|
@@ -24768,12 +24874,12 @@ app50.get("/", requireAdminSession, async (c) => {
|
|
|
24768
24874
|
const cacheKey = c.var.cacheKey;
|
|
24769
24875
|
const accountId = getAccountIdForSession(cacheKey);
|
|
24770
24876
|
if (!accountId) {
|
|
24771
|
-
console.error(`${
|
|
24877
|
+
console.error(`${TAG40} op=list auth-rejected reason="no account for session"`);
|
|
24772
24878
|
return c.json({ error: "Account not found for session" }, 401);
|
|
24773
24879
|
}
|
|
24774
24880
|
const cacheStart = Date.now();
|
|
24775
24881
|
const cached3 = await getOrCompute("tasks-list", accountId, () => computeTaskList(accountId));
|
|
24776
|
-
console.log(`${
|
|
24882
|
+
console.log(`${TAG40} op=list-cache cache=${cached3.state} account=${accountId} ms=${Date.now() - cacheStart}`);
|
|
24777
24883
|
if (cached3.value === null) {
|
|
24778
24884
|
return c.json({ error: "tasks-list-failed" }, 500);
|
|
24779
24885
|
}
|
|
@@ -24856,15 +24962,15 @@ async function computeTaskList(accountId) {
|
|
|
24856
24962
|
const returned = result.records.length;
|
|
24857
24963
|
if (returned < graphTotal) {
|
|
24858
24964
|
console.error(
|
|
24859
|
-
`${
|
|
24965
|
+
`${TAG40} op=list row-count-shortfall account=${accountId} returned=${returned} graphTotal=${graphTotal} missing=${graphTotal - returned}`
|
|
24860
24966
|
);
|
|
24861
24967
|
}
|
|
24862
24968
|
console.log(
|
|
24863
|
-
`${
|
|
24969
|
+
`${TAG40} op=list account=${accountId} returned=${returned} graphTotal=${graphTotal} open=${open.length} completed=${completed.length} missingTaskId=${missingTaskId} missingCreatedAt=${missingCreatedAt} assigned=${assigned}`
|
|
24864
24970
|
);
|
|
24865
24971
|
return { open, completed };
|
|
24866
24972
|
} catch (err) {
|
|
24867
|
-
console.error(`${
|
|
24973
|
+
console.error(`${TAG40} op=list error="${err instanceof Error ? err.message : String(err)}"`);
|
|
24868
24974
|
return null;
|
|
24869
24975
|
} finally {
|
|
24870
24976
|
await session.close();
|
|
@@ -24874,7 +24980,7 @@ var tasks_list_default = app50;
|
|
|
24874
24980
|
|
|
24875
24981
|
// server/routes/admin/task-timer-start.ts
|
|
24876
24982
|
import { randomUUID as randomUUID14 } from "crypto";
|
|
24877
|
-
var
|
|
24983
|
+
var TAG41 = "[task-timer]";
|
|
24878
24984
|
var app51 = new Hono();
|
|
24879
24985
|
app51.post("/", requireAdminSession, async (c) => {
|
|
24880
24986
|
const cacheKey = c.var.cacheKey;
|
|
@@ -24889,7 +24995,7 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
24889
24995
|
const taskId = typeof body.taskId === "string" ? body.taskId : "";
|
|
24890
24996
|
if (!taskId) {
|
|
24891
24997
|
const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
|
|
24892
|
-
console.error(`${
|
|
24998
|
+
console.error(`${TAG41} op=timer-start rejected reason=${reason} account=${accountId}`);
|
|
24893
24999
|
return c.json({ error: "taskId required" }, 400);
|
|
24894
25000
|
}
|
|
24895
25001
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -24900,7 +25006,7 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
24900
25006
|
{ taskId, accountId }
|
|
24901
25007
|
);
|
|
24902
25008
|
if (target.records.length === 0) {
|
|
24903
|
-
console.error(`${
|
|
25009
|
+
console.error(`${TAG41} op=start taskId=${taskId} accountId=${accountId} result=task-not-found`);
|
|
24904
25010
|
return c.json({ error: "task-not-found" }, 404);
|
|
24905
25011
|
}
|
|
24906
25012
|
const open = await session.run(
|
|
@@ -24912,7 +25018,7 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
24912
25018
|
const priorEntryId = open.records[0]?.get("entryId");
|
|
24913
25019
|
const priorStartedAt = open.records[0]?.get("startedAt");
|
|
24914
25020
|
if (priorEntryId) {
|
|
24915
|
-
console.log(`${
|
|
25021
|
+
console.log(`${TAG41} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
|
|
24916
25022
|
invalidate("tasks-list", accountId);
|
|
24917
25023
|
return c.json({ ok: true, running: true, entryId: priorEntryId, startedAt: priorStartedAt });
|
|
24918
25024
|
}
|
|
@@ -24923,11 +25029,11 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
24923
25029
|
CREATE (te)-[:LOGGED_AGAINST]->(t)`,
|
|
24924
25030
|
{ taskId, accountId, entryId, now }
|
|
24925
25031
|
);
|
|
24926
|
-
console.log(`${
|
|
25032
|
+
console.log(`${TAG41} op=opened taskId=${taskId} entryId=${entryId} startedAt=${now}`);
|
|
24927
25033
|
invalidate("tasks-list", accountId);
|
|
24928
25034
|
return c.json({ ok: true, running: true, entryId, startedAt: now });
|
|
24929
25035
|
} catch (err) {
|
|
24930
|
-
console.error(`${
|
|
25036
|
+
console.error(`${TAG41} op=start error="${err instanceof Error ? err.message : String(err)}"`);
|
|
24931
25037
|
return c.json({ error: "task-timer-start-failed" }, 500);
|
|
24932
25038
|
} finally {
|
|
24933
25039
|
await session.close();
|
|
@@ -24937,7 +25043,7 @@ var task_timer_start_default = app51;
|
|
|
24937
25043
|
|
|
24938
25044
|
// server/routes/admin/task-timer-stop.ts
|
|
24939
25045
|
import neo4j5 from "neo4j-driver";
|
|
24940
|
-
var
|
|
25046
|
+
var TAG42 = "[task-timer]";
|
|
24941
25047
|
var app52 = new Hono();
|
|
24942
25048
|
app52.post("/", requireAdminSession, async (c) => {
|
|
24943
25049
|
const cacheKey = c.var.cacheKey;
|
|
@@ -24952,7 +25058,7 @@ app52.post("/", requireAdminSession, async (c) => {
|
|
|
24952
25058
|
const taskId = typeof body.taskId === "string" ? body.taskId : "";
|
|
24953
25059
|
if (!taskId) {
|
|
24954
25060
|
const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
|
|
24955
|
-
console.error(`${
|
|
25061
|
+
console.error(`${TAG42} op=timer-stop rejected reason=${reason} account=${accountId}`);
|
|
24956
25062
|
return c.json({ error: "taskId required" }, 400);
|
|
24957
25063
|
}
|
|
24958
25064
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -24973,7 +25079,7 @@ app52.post("/", requireAdminSession, async (c) => {
|
|
|
24973
25079
|
{ taskId, accountId }
|
|
24974
25080
|
);
|
|
24975
25081
|
const secondsLogged2 = toNum(cur.records[0]?.get("secondsLogged"));
|
|
24976
|
-
console.log(`${
|
|
25082
|
+
console.log(`${TAG42} op=stop taskId=${taskId} entryId=none seconds=0 totalAfter=${secondsLogged2}`);
|
|
24977
25083
|
invalidate("tasks-list", accountId);
|
|
24978
25084
|
return c.json({ ok: true, running: false, seconds: 0, secondsLogged: secondsLogged2 });
|
|
24979
25085
|
}
|
|
@@ -24997,12 +25103,12 @@ app52.post("/", requireAdminSession, async (c) => {
|
|
|
24997
25103
|
const sumEntries = toNum(verify.records[0]?.get("sumEntries"));
|
|
24998
25104
|
const sumAdjustments = toNum(verify.records[0]?.get("sumAdjustments"));
|
|
24999
25105
|
const secondsLogged = toNum(verify.records[0]?.get("secondsLogged"));
|
|
25000
|
-
console.log(`${
|
|
25001
|
-
console.log(`${
|
|
25106
|
+
console.log(`${TAG42} op=stop taskId=${taskId} entryId=${entryId} seconds=${seconds} totalAfter=${secondsLogged}`);
|
|
25107
|
+
console.log(`${TAG42} op=persisted taskId=${taskId} entryId=${entryId} sumEntries=${sumEntries} sumAdjustments=${sumAdjustments} secondsLogged=${secondsLogged}`);
|
|
25002
25108
|
invalidate("tasks-list", accountId);
|
|
25003
25109
|
return c.json({ ok: true, running: false, seconds, secondsLogged });
|
|
25004
25110
|
} catch (err) {
|
|
25005
|
-
console.error(`${
|
|
25111
|
+
console.error(`${TAG42} op=stop error="${err instanceof Error ? err.message : String(err)}"`);
|
|
25006
25112
|
return c.json({ error: "task-timer-stop-failed" }, 500);
|
|
25007
25113
|
} finally {
|
|
25008
25114
|
await session.close();
|
|
@@ -25011,7 +25117,7 @@ app52.post("/", requireAdminSession, async (c) => {
|
|
|
25011
25117
|
var task_timer_stop_default = app52;
|
|
25012
25118
|
|
|
25013
25119
|
// server/routes/admin/task-complete.ts
|
|
25014
|
-
var
|
|
25120
|
+
var TAG43 = "[task-timer]";
|
|
25015
25121
|
var app53 = new Hono();
|
|
25016
25122
|
app53.post("/", requireAdminSession, async (c) => {
|
|
25017
25123
|
const cacheKey = c.var.cacheKey;
|
|
@@ -25026,7 +25132,7 @@ app53.post("/", requireAdminSession, async (c) => {
|
|
|
25026
25132
|
const taskId = typeof body.taskId === "string" ? body.taskId : "";
|
|
25027
25133
|
if (!taskId) {
|
|
25028
25134
|
const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
|
|
25029
|
-
console.error(`${
|
|
25135
|
+
console.error(`${TAG43} op=complete rejected reason=${reason} account=${accountId}`);
|
|
25030
25136
|
return c.json({ error: "taskId required" }, 400);
|
|
25031
25137
|
}
|
|
25032
25138
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -25042,11 +25148,11 @@ app53.post("/", requireAdminSession, async (c) => {
|
|
|
25042
25148
|
return c.json({ error: "task-not-found" }, 404);
|
|
25043
25149
|
}
|
|
25044
25150
|
const secondsLogged = toNum(result.records[0].get("secondsLogged"));
|
|
25045
|
-
console.log(`${
|
|
25151
|
+
console.log(`${TAG43} op=complete taskId=${taskId} status=completed secondsLogged=${secondsLogged}`);
|
|
25046
25152
|
invalidate("tasks-list", accountId);
|
|
25047
25153
|
return c.json({ ok: true, status: "completed", secondsLogged });
|
|
25048
25154
|
} catch (err) {
|
|
25049
|
-
console.error(`${
|
|
25155
|
+
console.error(`${TAG43} op=complete error="${err instanceof Error ? err.message : String(err)}"`);
|
|
25050
25156
|
return c.json({ error: "task-complete-failed" }, 500);
|
|
25051
25157
|
} finally {
|
|
25052
25158
|
await session.close();
|
|
@@ -25057,7 +25163,7 @@ var task_complete_default = app53;
|
|
|
25057
25163
|
// server/routes/admin/task-time-adjust.ts
|
|
25058
25164
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
25059
25165
|
import neo4j6 from "neo4j-driver";
|
|
25060
|
-
var
|
|
25166
|
+
var TAG44 = "[task-time-adjust]";
|
|
25061
25167
|
var app54 = new Hono();
|
|
25062
25168
|
app54.post("/", requireAdminSession, async (c) => {
|
|
25063
25169
|
const cacheKey = c.var.cacheKey;
|
|
@@ -25067,23 +25173,23 @@ app54.post("/", requireAdminSession, async (c) => {
|
|
|
25067
25173
|
try {
|
|
25068
25174
|
body = await c.req.json();
|
|
25069
25175
|
} catch {
|
|
25070
|
-
console.error(`${
|
|
25176
|
+
console.error(`${TAG44} op=reject reason=bad-json`);
|
|
25071
25177
|
return c.json({ error: "invalid-json" }, 400);
|
|
25072
25178
|
}
|
|
25073
25179
|
const taskId = typeof body.taskId === "string" ? body.taskId : "";
|
|
25074
25180
|
if (!taskId) {
|
|
25075
25181
|
const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
|
|
25076
|
-
console.error(`${
|
|
25182
|
+
console.error(`${TAG44} op=time-adjust rejected reason=${reason} account=${accountId}`);
|
|
25077
25183
|
return c.json({ error: "taskId required" }, 400);
|
|
25078
25184
|
}
|
|
25079
25185
|
const newSeconds = body.newSeconds;
|
|
25080
25186
|
if (typeof newSeconds !== "number" || !Number.isInteger(newSeconds) || newSeconds < 0) {
|
|
25081
|
-
console.error(`${
|
|
25187
|
+
console.error(`${TAG44} op=reject reason=invalid-seconds taskId=${taskId} accountId=${accountId}`);
|
|
25082
25188
|
return c.json({ error: "newSeconds must be a non-negative integer" }, 400);
|
|
25083
25189
|
}
|
|
25084
25190
|
const adjustmentId = randomUUID15();
|
|
25085
25191
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
25086
|
-
console.log(`${
|
|
25192
|
+
console.log(`${TAG44} op=request adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId} newSeconds=${newSeconds}`);
|
|
25087
25193
|
const session = getSession();
|
|
25088
25194
|
try {
|
|
25089
25195
|
const guard = await session.run(
|
|
@@ -25093,12 +25199,12 @@ app54.post("/", requireAdminSession, async (c) => {
|
|
|
25093
25199
|
{ taskId, accountId }
|
|
25094
25200
|
);
|
|
25095
25201
|
if (guard.records.length === 0) {
|
|
25096
|
-
console.error(`${
|
|
25202
|
+
console.error(`${TAG44} op=reject reason=no-task adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
|
|
25097
25203
|
return c.json({ error: "task-not-found" }, 404);
|
|
25098
25204
|
}
|
|
25099
25205
|
const openCount = toNum(guard.records[0].get("openCount"));
|
|
25100
25206
|
if (openCount > 0) {
|
|
25101
|
-
console.error(`${
|
|
25207
|
+
console.error(`${TAG44} op=reject reason=running adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
|
|
25102
25208
|
return c.json({ error: "task-timer-running" }, 409);
|
|
25103
25209
|
}
|
|
25104
25210
|
const previousSeconds = toNum(guard.records[0].get("previousSeconds"));
|
|
@@ -25124,11 +25230,11 @@ app54.post("/", requireAdminSession, async (c) => {
|
|
|
25124
25230
|
}
|
|
25125
25231
|
);
|
|
25126
25232
|
const secondsLoggedAfter = toNum(write.records[0]?.get("secondsLoggedAfter"));
|
|
25127
|
-
console.log(`${
|
|
25233
|
+
console.log(`${TAG44} op=persisted adjustmentId=${adjustmentId} previousSeconds=${previousSeconds} newSeconds=${newSeconds} delta=${delta} secondsLoggedAfter=${secondsLoggedAfter}`);
|
|
25128
25234
|
invalidate("tasks-list", accountId);
|
|
25129
25235
|
return c.json({ ok: true, secondsLogged: secondsLoggedAfter });
|
|
25130
25236
|
} catch (err) {
|
|
25131
|
-
console.error(`${
|
|
25237
|
+
console.error(`${TAG44} op=error adjustmentId=${adjustmentId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
25132
25238
|
return c.json({ error: "task-time-adjust-failed" }, 500);
|
|
25133
25239
|
} finally {
|
|
25134
25240
|
await session.close();
|
|
@@ -25221,7 +25327,7 @@ async function resolveAssignableAdmins(accountId, accountsDir = ACCOUNTS_DIR) {
|
|
|
25221
25327
|
}
|
|
25222
25328
|
|
|
25223
25329
|
// server/routes/admin/task-assign.ts
|
|
25224
|
-
var
|
|
25330
|
+
var TAG45 = "[task-assign]";
|
|
25225
25331
|
var app55 = new Hono();
|
|
25226
25332
|
app55.post("/", requireAdminSession, async (c) => {
|
|
25227
25333
|
const cacheKey = c.var.cacheKey;
|
|
@@ -25236,7 +25342,7 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
25236
25342
|
const taskId = typeof body.taskId === "string" ? body.taskId : "";
|
|
25237
25343
|
if (!taskId) {
|
|
25238
25344
|
const reason = body.taskId === null ? "null-taskId" : "missing-taskId";
|
|
25239
|
-
console.error(`${
|
|
25345
|
+
console.error(`${TAG45} op=assign rejected reason=${reason} account=${accountId}`);
|
|
25240
25346
|
return c.json({ error: "taskId required" }, 400);
|
|
25241
25347
|
}
|
|
25242
25348
|
const rawAssignee = body.assigneeUserId;
|
|
@@ -25248,7 +25354,7 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
25248
25354
|
const { ids } = await resolveAllowedAssigneeIds(accountId);
|
|
25249
25355
|
if (!ids.has(assigneeUserId)) {
|
|
25250
25356
|
console.error(
|
|
25251
|
-
`${
|
|
25357
|
+
`${TAG45} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=false source=route reason=not-in-roster`
|
|
25252
25358
|
);
|
|
25253
25359
|
return c.json({ error: "assignee-not-in-roster" }, 403);
|
|
25254
25360
|
}
|
|
@@ -25270,7 +25376,7 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
25270
25376
|
`MATCH (t:Task {taskId: $taskId, accountId: $accountId}) SET t.updatedAt = $now`,
|
|
25271
25377
|
{ taskId, accountId, now }
|
|
25272
25378
|
);
|
|
25273
|
-
console.log(`${
|
|
25379
|
+
console.log(`${TAG45} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=none assigned=false source=route`);
|
|
25274
25380
|
invalidate("tasks-list", accountId);
|
|
25275
25381
|
return c.json({ ok: true, assigneeUserId: null, assigneeName: null });
|
|
25276
25382
|
}
|
|
@@ -25286,7 +25392,7 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
25286
25392
|
);
|
|
25287
25393
|
if (set.records.length === 0) {
|
|
25288
25394
|
console.error(
|
|
25289
|
-
`${
|
|
25395
|
+
`${TAG45} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=false source=route reason=no-adminuser-node`
|
|
25290
25396
|
);
|
|
25291
25397
|
return c.json({ error: "assignee-no-admin-node" }, 409);
|
|
25292
25398
|
}
|
|
@@ -25294,12 +25400,12 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
25294
25400
|
const family = set.records[0].get("familyName") ?? null;
|
|
25295
25401
|
const assigneeName = given && given.trim().length > 0 ? family && family.trim().length > 0 ? `${given.trim()} ${family.trim()}` : given.trim() : null;
|
|
25296
25402
|
console.log(
|
|
25297
|
-
`${
|
|
25403
|
+
`${TAG45} op=assign taskId=${taskId.slice(0, 8)} assigneeUserId=${assigneeUserId.slice(0, 8)} assigned=true source=route`
|
|
25298
25404
|
);
|
|
25299
25405
|
invalidate("tasks-list", accountId);
|
|
25300
25406
|
return c.json({ ok: true, assigneeUserId, assigneeName });
|
|
25301
25407
|
} catch (err) {
|
|
25302
|
-
console.error(`${
|
|
25408
|
+
console.error(`${TAG45} op=assign error="${err instanceof Error ? err.message : String(err)}"`);
|
|
25303
25409
|
return c.json({ error: "task-assign-failed" }, 500);
|
|
25304
25410
|
} finally {
|
|
25305
25411
|
await session.close();
|
|
@@ -25308,21 +25414,21 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
25308
25414
|
var task_assign_default = app55;
|
|
25309
25415
|
|
|
25310
25416
|
// server/routes/admin/admin-users-list.ts
|
|
25311
|
-
var
|
|
25417
|
+
var TAG46 = "[admin-users]";
|
|
25312
25418
|
var app56 = new Hono();
|
|
25313
25419
|
app56.get("/", requireAdminSession, async (c) => {
|
|
25314
25420
|
const cacheKey = c.var.cacheKey;
|
|
25315
25421
|
const accountId = getAccountIdForSession(cacheKey);
|
|
25316
25422
|
if (!accountId) {
|
|
25317
|
-
console.error(`${
|
|
25423
|
+
console.error(`${TAG46} op=list auth-rejected reason="no account for session"`);
|
|
25318
25424
|
return c.json({ error: "Account not found for session" }, 401);
|
|
25319
25425
|
}
|
|
25320
25426
|
try {
|
|
25321
25427
|
const users = await resolveAssignableAdmins(accountId);
|
|
25322
|
-
console.log(`${
|
|
25428
|
+
console.log(`${TAG46} op=list account=${accountId} count=${users.length}`);
|
|
25323
25429
|
return c.json({ ok: true, users });
|
|
25324
25430
|
} catch (err) {
|
|
25325
|
-
console.error(`${
|
|
25431
|
+
console.error(`${TAG46} op=list error="${err instanceof Error ? err.message : String(err)}"`);
|
|
25326
25432
|
return c.json({ error: "admin-users-list-failed" }, 500);
|
|
25327
25433
|
}
|
|
25328
25434
|
});
|
|
@@ -25331,7 +25437,7 @@ var admin_users_list_default = app56;
|
|
|
25331
25437
|
// server/routes/admin/activity.ts
|
|
25332
25438
|
import { readdirSync as readdirSync26 } from "fs";
|
|
25333
25439
|
var app57 = new Hono();
|
|
25334
|
-
var
|
|
25440
|
+
var TAG47 = "[activity]";
|
|
25335
25441
|
function resolveHouseAccountId(accounts) {
|
|
25336
25442
|
const houses = accounts.filter((a) => a.config.role === "house");
|
|
25337
25443
|
if (houses.length === 1) return { accountId: houses[0].accountId, reason: "house" };
|
|
@@ -25360,19 +25466,19 @@ app57.get("/", requireAdminSession, async (c) => {
|
|
|
25360
25466
|
const house = resolveHouseAccountId(valid);
|
|
25361
25467
|
const isHouse = caller2 !== null && house.accountId !== null && caller2 === house.accountId;
|
|
25362
25468
|
console.log(
|
|
25363
|
-
`${
|
|
25469
|
+
`${TAG47} op=request req=${req} caller=${short(caller2)} house=${isHouse} houseVia=${house.reason}`
|
|
25364
25470
|
);
|
|
25365
25471
|
if (!isHouse) {
|
|
25366
25472
|
const reason = house.accountId === null ? house.reason : "not-house";
|
|
25367
25473
|
console.error(
|
|
25368
|
-
`${
|
|
25474
|
+
`${TAG47} op=gate req=${req} decision=deny reason=${reason} caller=${short(caller2)} houseAcct=${short(house.accountId)} accounts=${valid.length}`
|
|
25369
25475
|
);
|
|
25370
25476
|
return c.json({ error: "house-only" }, 403);
|
|
25371
25477
|
}
|
|
25372
|
-
console.log(`${
|
|
25478
|
+
console.log(`${TAG47} op=gate req=${req} decision=allow caller=${short(caller2)} via=${house.reason}`);
|
|
25373
25479
|
const onDisk = countAccountDirsOnDisk();
|
|
25374
25480
|
console.log(
|
|
25375
|
-
`${
|
|
25481
|
+
`${TAG47} op=accounts req=${req} onDisk=${onDisk ?? "unknown"} valid=${valid.length} ids=${valid.map((a) => short(a.accountId)).join(",")}`
|
|
25376
25482
|
);
|
|
25377
25483
|
const start = Date.now();
|
|
25378
25484
|
let res;
|
|
@@ -25388,17 +25494,17 @@ app57.get("/", requireAdminSession, async (c) => {
|
|
|
25388
25494
|
});
|
|
25389
25495
|
} catch (err) {
|
|
25390
25496
|
console.error(
|
|
25391
|
-
`${
|
|
25497
|
+
`${TAG47} op=manager-fail req=${req} reason=unreachable err=${err instanceof Error ? err.message : String(err)}`
|
|
25392
25498
|
);
|
|
25393
25499
|
return c.json({ error: "manager-unreachable" }, 502);
|
|
25394
25500
|
}
|
|
25395
25501
|
if (res.status !== 200) {
|
|
25396
|
-
console.error(`${
|
|
25502
|
+
console.error(`${TAG47} op=manager-fail req=${req} reason=status status=${res.status}`);
|
|
25397
25503
|
return c.json({ error: `manager-status-${res.status}` }, 502);
|
|
25398
25504
|
}
|
|
25399
25505
|
const body = await res.json().catch(() => null);
|
|
25400
25506
|
if (!body || !Array.isArray(body.rows)) {
|
|
25401
|
-
console.error(`${
|
|
25507
|
+
console.error(`${TAG47} op=manager-fail req=${req} reason=bad-json`);
|
|
25402
25508
|
return c.json({ error: "manager-bad-json" }, 502);
|
|
25403
25509
|
}
|
|
25404
25510
|
const rows = body.rows;
|
|
@@ -25409,10 +25515,10 @@ app57.get("/", requireAdminSession, async (c) => {
|
|
|
25409
25515
|
const ageNull = rows.filter((r) => r.ageMs === null).length;
|
|
25410
25516
|
const sidecarNull = rows.filter((r) => r.sidecarMissing).length;
|
|
25411
25517
|
console.log(
|
|
25412
|
-
`${
|
|
25518
|
+
`${TAG47} op=census req=${req} ageMs=${censusAgeMs ?? "none"} live=${census?.live ?? "none"} cpuNull=${census?.cpuNull ?? "none"} rows=${rows.length} ageNull=${ageNull} sidecarNull=${sidecarNull}`
|
|
25413
25519
|
);
|
|
25414
25520
|
console.log(
|
|
25415
|
-
`${
|
|
25521
|
+
`${TAG47} op=response req=${req} rows=${rows.length} accounts=${valid.length} untagged=${untaggedCount} meteringFailed=${meteringFailed} ms=${Date.now() - start}`
|
|
25416
25522
|
);
|
|
25417
25523
|
const names = {};
|
|
25418
25524
|
await Promise.all(
|
|
@@ -25431,7 +25537,7 @@ app57.get("/", requireAdminSession, async (c) => {
|
|
|
25431
25537
|
return acc;
|
|
25432
25538
|
}, {});
|
|
25433
25539
|
console.log(
|
|
25434
|
-
`${
|
|
25540
|
+
`${TAG47} op=loops req=${req} armed=${armed2} rendered=${loopRows.length} procPct=${cpu.processPct?.toFixed(1) ?? "null"} attributedPct=${cpu.attributedPct?.toFixed(1) ?? "null"} ` + Object.entries(byState).map(([s, n]) => `${s}=${n}`).join(" ")
|
|
25435
25541
|
);
|
|
25436
25542
|
return c.json({
|
|
25437
25543
|
rows,
|
|
@@ -25515,7 +25621,7 @@ app58.route("/activity", activity_default);
|
|
|
25515
25621
|
var admin_default = app58;
|
|
25516
25622
|
|
|
25517
25623
|
// server/routes/access/verify-token.ts
|
|
25518
|
-
var
|
|
25624
|
+
var TAG48 = "[access-verify]";
|
|
25519
25625
|
var MINT_TAG = "[access-session-mint]";
|
|
25520
25626
|
var COOKIE_NAME = "__access_session";
|
|
25521
25627
|
var app59 = new Hono();
|
|
@@ -25534,39 +25640,39 @@ app59.post("/", async (c) => {
|
|
|
25534
25640
|
}
|
|
25535
25641
|
const rateMsg = checkAccessRateLimit(ip, agentSlug);
|
|
25536
25642
|
if (rateMsg) {
|
|
25537
|
-
console.error(`${
|
|
25643
|
+
console.error(`${TAG48} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
|
|
25538
25644
|
return c.json({ error: rateMsg }, 429);
|
|
25539
25645
|
}
|
|
25540
25646
|
const grant = await findGrantByMagicToken(token);
|
|
25541
25647
|
if (!grant) {
|
|
25542
25648
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
25543
|
-
console.error(`${
|
|
25649
|
+
console.error(`${TAG48} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
|
|
25544
25650
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
25545
25651
|
}
|
|
25546
25652
|
if (grant.agentSlug !== agentSlug) {
|
|
25547
25653
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
25548
25654
|
console.error(
|
|
25549
|
-
`${
|
|
25655
|
+
`${TAG48} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
|
|
25550
25656
|
);
|
|
25551
25657
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
25552
25658
|
}
|
|
25553
25659
|
if (grant.status === "expired" || grant.status === "revoked") {
|
|
25554
25660
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
25555
25661
|
console.error(
|
|
25556
|
-
`${
|
|
25662
|
+
`${TAG48} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
|
|
25557
25663
|
);
|
|
25558
25664
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
25559
25665
|
}
|
|
25560
25666
|
if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
|
|
25561
25667
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
25562
25668
|
console.error(
|
|
25563
|
-
`${
|
|
25669
|
+
`${TAG48} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
|
|
25564
25670
|
);
|
|
25565
25671
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
25566
25672
|
}
|
|
25567
25673
|
if (!grant.sliceToken) {
|
|
25568
25674
|
console.error(
|
|
25569
|
-
`${
|
|
25675
|
+
`${TAG48} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
|
|
25570
25676
|
);
|
|
25571
25677
|
return c.json({ error: "grant-misconfigured" }, 500);
|
|
25572
25678
|
}
|
|
@@ -25582,12 +25688,12 @@ app59.post("/", async (c) => {
|
|
|
25582
25688
|
await consumeMagicTokenAndActivate(grant.grantId);
|
|
25583
25689
|
} catch (err) {
|
|
25584
25690
|
console.error(
|
|
25585
|
-
`${
|
|
25691
|
+
`${TAG48} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
25586
25692
|
);
|
|
25587
25693
|
return c.json({ error: "verification-failed" }, 500);
|
|
25588
25694
|
}
|
|
25589
25695
|
clearAccessRateLimit(ip, agentSlug);
|
|
25590
|
-
console.log(`${
|
|
25696
|
+
console.log(`${TAG48} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
|
|
25591
25697
|
console.log(
|
|
25592
25698
|
`${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
|
|
25593
25699
|
);
|
|
@@ -25663,7 +25769,7 @@ async function sendMagicLinkEmail(payload) {
|
|
|
25663
25769
|
}
|
|
25664
25770
|
|
|
25665
25771
|
// server/routes/access/request-magic-link.ts
|
|
25666
|
-
var
|
|
25772
|
+
var TAG49 = "[access-request-link]";
|
|
25667
25773
|
var app60 = new Hono();
|
|
25668
25774
|
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
25669
25775
|
app60.post("/", async (c) => {
|
|
@@ -25680,18 +25786,18 @@ app60.post("/", async (c) => {
|
|
|
25680
25786
|
}
|
|
25681
25787
|
const rateMsg = checkRequestLinkRateLimit(contactValue);
|
|
25682
25788
|
if (rateMsg) {
|
|
25683
|
-
console.error(`${
|
|
25789
|
+
console.error(`${TAG49} contactValue=${maskContact(contactValue)} result=rate-limited`);
|
|
25684
25790
|
return c.json({ error: rateMsg }, 429);
|
|
25685
25791
|
}
|
|
25686
25792
|
recordRequestLinkAttempt(contactValue);
|
|
25687
25793
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
25688
25794
|
if (!accountId) {
|
|
25689
|
-
console.error(`${
|
|
25795
|
+
console.error(`${TAG49} contactValue=${maskContact(contactValue)} result=no-account-id`);
|
|
25690
25796
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
25691
25797
|
}
|
|
25692
25798
|
const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
|
|
25693
25799
|
if (!grant) {
|
|
25694
|
-
console.log(`${
|
|
25800
|
+
console.log(`${TAG49} contactValue=${maskContact(contactValue)} result=notfound`);
|
|
25695
25801
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
25696
25802
|
}
|
|
25697
25803
|
let token;
|
|
@@ -25699,7 +25805,7 @@ app60.post("/", async (c) => {
|
|
|
25699
25805
|
token = await generateNewMagicToken(grant.grantId);
|
|
25700
25806
|
} catch (err) {
|
|
25701
25807
|
console.error(
|
|
25702
|
-
`${
|
|
25808
|
+
`${TAG49} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
25703
25809
|
);
|
|
25704
25810
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
25705
25811
|
}
|
|
@@ -25731,12 +25837,12 @@ app60.post("/", async (c) => {
|
|
|
25731
25837
|
});
|
|
25732
25838
|
if (!sendResult.ok) {
|
|
25733
25839
|
console.error(
|
|
25734
|
-
`${
|
|
25840
|
+
`${TAG49} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
|
|
25735
25841
|
);
|
|
25736
25842
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
25737
25843
|
}
|
|
25738
25844
|
console.log(
|
|
25739
|
-
`${
|
|
25845
|
+
`${TAG49} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
|
|
25740
25846
|
);
|
|
25741
25847
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
25742
25848
|
});
|
|
@@ -26822,7 +26928,7 @@ import { stat as stat8, readFile as readFile9, realpath } from "fs/promises";
|
|
|
26822
26928
|
import { join as join44, resolve as resolve38, sep as sep10 } from "path";
|
|
26823
26929
|
import { Readable as Readable3 } from "stream";
|
|
26824
26930
|
var app68 = new Hono();
|
|
26825
|
-
var
|
|
26931
|
+
var TAG50 = "[portal-fetch]";
|
|
26826
26932
|
async function verifySignature(secret, accountId, relPath, expiresAtMs, signature, nowMs) {
|
|
26827
26933
|
if (!secret || !signature) return false;
|
|
26828
26934
|
if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) return false;
|
|
@@ -26877,14 +26983,14 @@ async function readAccountSecret(accountId) {
|
|
|
26877
26983
|
try {
|
|
26878
26984
|
text = await readFile9(path3, "utf8");
|
|
26879
26985
|
} catch {
|
|
26880
|
-
console.error(`${
|
|
26986
|
+
console.error(`${TAG50} op=no-secret account="${accountId}" path="${path3}"`);
|
|
26881
26987
|
return "";
|
|
26882
26988
|
}
|
|
26883
26989
|
for (const line of text.split("\n")) {
|
|
26884
26990
|
const m = line.match(/^PORTAL_FETCH_SECRET=(.*)$/);
|
|
26885
26991
|
if (m) return m[1].trim();
|
|
26886
26992
|
}
|
|
26887
|
-
console.error(`${
|
|
26993
|
+
console.error(`${TAG50} op=no-secret account="${accountId}" reason=key-absent path="${path3}"`);
|
|
26888
26994
|
return "";
|
|
26889
26995
|
}
|
|
26890
26996
|
function isValidAccountId2(value) {
|
|
@@ -26944,16 +27050,16 @@ async function handle(c, headOnly) {
|
|
|
26944
27050
|
const sig = c.req.query("sig") ?? "";
|
|
26945
27051
|
const owner = c.req.query("owner") ?? "";
|
|
26946
27052
|
console.log(
|
|
26947
|
-
`${
|
|
27053
|
+
`${TAG50} op=request account=${q(accountId)} path=${q(relPath)} owner=${q(owner)} head=${headOnly}`
|
|
26948
27054
|
);
|
|
26949
27055
|
if (!isValidAccountId2(accountId)) {
|
|
26950
|
-
console.error(`${
|
|
27056
|
+
console.error(`${TAG50} op=denied account=${q(accountId)} reason=account-id`);
|
|
26951
27057
|
return c.json({ ok: false, error: "denied" }, 401);
|
|
26952
27058
|
}
|
|
26953
27059
|
const secret = await readAccountSecret(accountId);
|
|
26954
27060
|
const verified = owner ? await verifyOwnUploadSignature(secret, accountId, owner, relPath, exp, sig, Date.now()) : await verifySignature(secret, accountId, relPath, exp, sig, Date.now());
|
|
26955
27061
|
if (!verified) {
|
|
26956
|
-
console.error(`${
|
|
27062
|
+
console.error(`${TAG50} op=denied account=${q(accountId)} reason=secret`);
|
|
26957
27063
|
return c.json({ ok: false, error: "denied" }, 401);
|
|
26958
27064
|
}
|
|
26959
27065
|
const target = await resolveFetchTarget(
|
|
@@ -26963,14 +27069,14 @@ async function handle(c, headOnly) {
|
|
|
26963
27069
|
);
|
|
26964
27070
|
if (!target.ok) {
|
|
26965
27071
|
const op = target.reason === "enoent" ? "miss" : "denied";
|
|
26966
|
-
console.error(`${
|
|
27072
|
+
console.error(`${TAG50} op=${op} account=${accountId} reason=${target.reason}`);
|
|
26967
27073
|
return c.json({ ok: false, error: "not found" }, 404);
|
|
26968
27074
|
}
|
|
26969
27075
|
if (headOnly) {
|
|
26970
|
-
console.log(`${
|
|
27076
|
+
console.log(`${TAG50} op=served account=${accountId} bytes=${target.sizeBytes} head=true`);
|
|
26971
27077
|
return c.body(null, 200);
|
|
26972
27078
|
}
|
|
26973
|
-
console.log(`${
|
|
27079
|
+
console.log(`${TAG50} op=served account=${accountId} bytes=${target.sizeBytes}`);
|
|
26974
27080
|
return new Response(Readable3.toWeb(createReadStream3(target.absolute)), {
|
|
26975
27081
|
status: 200,
|
|
26976
27082
|
headers: {
|
|
@@ -26989,7 +27095,7 @@ app68.on("HEAD", "/fetch", (c) => handle(c, true));
|
|
|
26989
27095
|
var portal_fetch_default = app68;
|
|
26990
27096
|
|
|
26991
27097
|
// app/lib/timeentry-census.ts
|
|
26992
|
-
var
|
|
27098
|
+
var TAG51 = "[timeentry-census]";
|
|
26993
27099
|
function reconcileTimeEntries(rows, now, horizonMs) {
|
|
26994
27100
|
const openEntries = rows.openEntries.length;
|
|
26995
27101
|
let oldestOpenAgeMs = 0;
|
|
@@ -27029,12 +27135,12 @@ async function runTimeEntryCensusOnce(session, now, horizonMs) {
|
|
|
27029
27135
|
sumAdjustments: toNum(r.get("sumAdjustments"))
|
|
27030
27136
|
}));
|
|
27031
27137
|
const finding = reconcileTimeEntries({ openEntries, taskSums }, now, horizonMs);
|
|
27032
|
-
const line = `${
|
|
27138
|
+
const line = `${TAG51} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
|
|
27033
27139
|
if (finding.alarm) console.error(`${line} alarm=true`);
|
|
27034
27140
|
else console.log(line);
|
|
27035
27141
|
return finding;
|
|
27036
27142
|
} catch (err) {
|
|
27037
|
-
console.error(`${
|
|
27143
|
+
console.error(`${TAG51} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
27038
27144
|
return null;
|
|
27039
27145
|
}
|
|
27040
27146
|
}
|
|
@@ -27048,13 +27154,13 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
27048
27154
|
try {
|
|
27049
27155
|
session = openSession();
|
|
27050
27156
|
} catch (err) {
|
|
27051
|
-
console.error(`${
|
|
27157
|
+
console.error(`${TAG51} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
|
|
27052
27158
|
return;
|
|
27053
27159
|
}
|
|
27054
27160
|
try {
|
|
27055
27161
|
await runTimeEntryCensusOnce(session, Date.now(), horizonMs);
|
|
27056
27162
|
} catch (err) {
|
|
27057
|
-
console.error(`${
|
|
27163
|
+
console.error(`${TAG51} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
|
|
27058
27164
|
} finally {
|
|
27059
27165
|
try {
|
|
27060
27166
|
await session.close();
|
|
@@ -27115,7 +27221,7 @@ function formatCensusLine(f) {
|
|
|
27115
27221
|
}
|
|
27116
27222
|
|
|
27117
27223
|
// app/lib/ledger-census.ts
|
|
27118
|
-
var
|
|
27224
|
+
var TAG52 = "[ledger-census]";
|
|
27119
27225
|
var DAY_MS = 864e5;
|
|
27120
27226
|
function countWriteRejects24h(logDir, now) {
|
|
27121
27227
|
let entries2;
|
|
@@ -27191,7 +27297,7 @@ async function runLedgerCensusOnce(session, now) {
|
|
|
27191
27297
|
else console.log(line);
|
|
27192
27298
|
return finding;
|
|
27193
27299
|
} catch (err) {
|
|
27194
|
-
console.error(`${
|
|
27300
|
+
console.error(`${TAG52} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
27195
27301
|
return null;
|
|
27196
27302
|
}
|
|
27197
27303
|
}
|
|
@@ -27204,7 +27310,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
27204
27310
|
session = openSession();
|
|
27205
27311
|
} catch (err) {
|
|
27206
27312
|
console.error(
|
|
27207
|
-
`${
|
|
27313
|
+
`${TAG52} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`
|
|
27208
27314
|
);
|
|
27209
27315
|
return;
|
|
27210
27316
|
}
|
|
@@ -27212,7 +27318,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
27212
27318
|
await runLedgerCensusOnce(session, Date.now());
|
|
27213
27319
|
} catch (err) {
|
|
27214
27320
|
console.error(
|
|
27215
|
-
`${
|
|
27321
|
+
`${TAG52} tick-failed error="${err instanceof Error ? err.message : String(err)}"`
|
|
27216
27322
|
);
|
|
27217
27323
|
} finally {
|
|
27218
27324
|
try {
|
|
@@ -27230,7 +27336,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
27230
27336
|
}
|
|
27231
27337
|
|
|
27232
27338
|
// app/lib/conversation-audit.ts
|
|
27233
|
-
var
|
|
27339
|
+
var TAG53 = "[conversation-audit]";
|
|
27234
27340
|
async function runConversationAudit() {
|
|
27235
27341
|
const session = getSession();
|
|
27236
27342
|
try {
|
|
@@ -27267,12 +27373,12 @@ async function runConversationAudit() {
|
|
|
27267
27373
|
multiAnchored: Number(multiRec?.get("multiAnchored") ?? 0)
|
|
27268
27374
|
};
|
|
27269
27375
|
console.error(
|
|
27270
|
-
`${
|
|
27376
|
+
`${TAG53} channel=whatsapp total=${result.total} senders=${result.senders} multiAnchored=${result.multiAnchored} unreconciled=${result.unreconciled} unpairable=${result.unpairable}`
|
|
27271
27377
|
);
|
|
27272
27378
|
return result;
|
|
27273
27379
|
} catch (err) {
|
|
27274
27380
|
const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
|
|
27275
|
-
console.error(`${
|
|
27381
|
+
console.error(`${TAG53} op=failed reason=${reason}`);
|
|
27276
27382
|
return null;
|
|
27277
27383
|
} finally {
|
|
27278
27384
|
await session.close();
|
|
@@ -28769,10 +28875,12 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
28769
28875
|
resolve: (r) => {
|
|
28770
28876
|
clearTimeout(timer);
|
|
28771
28877
|
this.pendingPrompts.delete(k);
|
|
28878
|
+
this.deps.onPointerChange?.(p.key);
|
|
28772
28879
|
resolve46(r);
|
|
28773
28880
|
}
|
|
28774
28881
|
});
|
|
28775
28882
|
console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
|
|
28883
|
+
this.deps.onPointerChange?.(p.key);
|
|
28776
28884
|
});
|
|
28777
28885
|
}
|
|
28778
28886
|
/** Task 940 — the operator's Allow/Deny answer for an open prompt. Returns
|
|
@@ -29147,7 +29255,7 @@ function toolResultText(content) {
|
|
|
29147
29255
|
).join("");
|
|
29148
29256
|
}
|
|
29149
29257
|
function startFollower(opts) {
|
|
29150
|
-
const
|
|
29258
|
+
const abort2 = new AbortController();
|
|
29151
29259
|
const { entry, tag } = opts;
|
|
29152
29260
|
void (async () => {
|
|
29153
29261
|
try {
|
|
@@ -29159,14 +29267,14 @@ function startFollower(opts) {
|
|
|
29159
29267
|
for (; ; ) {
|
|
29160
29268
|
res = await fetch(
|
|
29161
29269
|
managerLogFollowUrl(entry.sessionId, { boundary: opts.suppressResumeReplay === true }),
|
|
29162
|
-
{ signal:
|
|
29270
|
+
{ signal: abort2.signal }
|
|
29163
29271
|
);
|
|
29164
29272
|
console.error(`${tag} follower-connect sessionId=${sid} status=${res.status}`);
|
|
29165
29273
|
if (res.status === 202) {
|
|
29166
29274
|
attempt += 1;
|
|
29167
29275
|
await res.body?.cancel().catch(() => {
|
|
29168
29276
|
});
|
|
29169
|
-
if (
|
|
29277
|
+
if (abort2.signal.aborted) return;
|
|
29170
29278
|
if (Date.now() >= deadline) {
|
|
29171
29279
|
console.error(`${tag} follower-give-up sessionId=${sid} reason=pending-timeout attempts=${attempt}`);
|
|
29172
29280
|
opts.onError?.("follow-pending-timeout");
|
|
@@ -29174,7 +29282,7 @@ function startFollower(opts) {
|
|
|
29174
29282
|
}
|
|
29175
29283
|
console.error(`${tag} follower-retry sessionId=${sid} attempt=${attempt} reason=pending`);
|
|
29176
29284
|
await new Promise((r) => setTimeout(r, retryMs));
|
|
29177
|
-
if (
|
|
29285
|
+
if (abort2.signal.aborted) return;
|
|
29178
29286
|
continue;
|
|
29179
29287
|
}
|
|
29180
29288
|
break;
|
|
@@ -29194,7 +29302,7 @@ function startFollower(opts) {
|
|
|
29194
29302
|
let suppressing = opts.suppressResumeReplay === true;
|
|
29195
29303
|
let discardedTurns = 0;
|
|
29196
29304
|
let discardedFileTools = 0;
|
|
29197
|
-
while (!
|
|
29305
|
+
while (!abort2.signal.aborted) {
|
|
29198
29306
|
const { value, done } = await reader.read();
|
|
29199
29307
|
if (done) break;
|
|
29200
29308
|
buffered += decoder.decode(value, { stream: true });
|
|
@@ -29310,14 +29418,14 @@ function startFollower(opts) {
|
|
|
29310
29418
|
}
|
|
29311
29419
|
}
|
|
29312
29420
|
} catch (err) {
|
|
29313
|
-
if (!
|
|
29421
|
+
if (!abort2.signal.aborted) {
|
|
29314
29422
|
opts.onError?.(`follower-error: ${err instanceof Error ? err.message : String(err)}`);
|
|
29315
29423
|
}
|
|
29316
29424
|
} finally {
|
|
29317
29425
|
opts.onClose();
|
|
29318
29426
|
}
|
|
29319
29427
|
})();
|
|
29320
|
-
return
|
|
29428
|
+
return abort2;
|
|
29321
29429
|
}
|
|
29322
29430
|
async function fanOut(subscribers, text, onError, tag) {
|
|
29323
29431
|
const callbacks = Array.from(subscribers);
|
|
@@ -29425,14 +29533,14 @@ function makeFileDelivery(opts) {
|
|
|
29425
29533
|
}
|
|
29426
29534
|
|
|
29427
29535
|
// app/lib/webchat/file-delivery.ts
|
|
29428
|
-
var
|
|
29536
|
+
var TAG54 = "[webchat-adaptor]";
|
|
29429
29537
|
function platformRoot2() {
|
|
29430
29538
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
29431
29539
|
}
|
|
29432
29540
|
function makeWebchatSendFile(entry) {
|
|
29433
29541
|
return async (filePath) => {
|
|
29434
29542
|
if (!entry.accountId) {
|
|
29435
|
-
console.error(`${
|
|
29543
|
+
console.error(`${TAG54} file-delivery reject reason=no-account sender=${entry.senderId}`);
|
|
29436
29544
|
return { ok: false, error: "no-account" };
|
|
29437
29545
|
}
|
|
29438
29546
|
const accountDir = resolve41(platformRoot2(), "..", "data/accounts", entry.accountId);
|
|
@@ -29440,14 +29548,14 @@ function makeWebchatSendFile(entry) {
|
|
|
29440
29548
|
const resolved = realpathSync9(filePath);
|
|
29441
29549
|
const accountResolved = realpathSync9(accountDir);
|
|
29442
29550
|
if (!resolved.startsWith(accountResolved + "/")) {
|
|
29443
|
-
console.error(`${
|
|
29551
|
+
console.error(`${TAG54} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
|
|
29444
29552
|
return { ok: false, error: "outside-account" };
|
|
29445
29553
|
}
|
|
29446
29554
|
return { ok: true };
|
|
29447
29555
|
} catch (err) {
|
|
29448
29556
|
const code = err.code;
|
|
29449
29557
|
console.error(
|
|
29450
|
-
`${
|
|
29558
|
+
`${TAG54} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
|
|
29451
29559
|
);
|
|
29452
29560
|
return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
|
|
29453
29561
|
}
|
|
@@ -29456,7 +29564,7 @@ function makeWebchatSendFile(entry) {
|
|
|
29456
29564
|
function makeWebchatFileDelivery(entry) {
|
|
29457
29565
|
return makeFileDelivery({
|
|
29458
29566
|
entry,
|
|
29459
|
-
tag:
|
|
29567
|
+
tag: TAG54,
|
|
29460
29568
|
channel: "webchat",
|
|
29461
29569
|
sendFile: makeWebchatSendFile(entry),
|
|
29462
29570
|
deferUntilVerdict: true
|
|
@@ -29518,7 +29626,7 @@ function buildPublicWebchatSpawnRequest(input) {
|
|
|
29518
29626
|
}
|
|
29519
29627
|
|
|
29520
29628
|
// app/lib/whatsapp/inbound/file-delivery-bridge.ts
|
|
29521
|
-
var
|
|
29629
|
+
var TAG55 = "[whatsapp-adaptor]";
|
|
29522
29630
|
var SEND_USER_FILE2 = "SendUserFile";
|
|
29523
29631
|
var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
|
|
29524
29632
|
function platformRoot3() {
|
|
@@ -29536,7 +29644,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
|
|
|
29536
29644
|
});
|
|
29537
29645
|
if (result.ok) return { ok: true };
|
|
29538
29646
|
console.error(
|
|
29539
|
-
`${
|
|
29647
|
+
`${TAG55} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
|
|
29540
29648
|
);
|
|
29541
29649
|
return { ok: false, error: result.error };
|
|
29542
29650
|
};
|
|
@@ -29544,7 +29652,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
|
|
|
29544
29652
|
function makeWhatsAppFileDelivery(entry, maxyAccountId) {
|
|
29545
29653
|
const shared = makeFileDelivery({
|
|
29546
29654
|
entry,
|
|
29547
|
-
tag:
|
|
29655
|
+
tag: TAG55,
|
|
29548
29656
|
channel: "whatsapp",
|
|
29549
29657
|
sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
|
|
29550
29658
|
});
|
|
@@ -29579,7 +29687,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
|
|
|
29579
29687
|
if (!delivered) {
|
|
29580
29688
|
const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
|
|
29581
29689
|
console.error(
|
|
29582
|
-
`${
|
|
29690
|
+
`${TAG55} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
|
|
29583
29691
|
);
|
|
29584
29692
|
}
|
|
29585
29693
|
}
|
|
@@ -30000,7 +30108,7 @@ function buildTelegramSpawnRequest(input) {
|
|
|
30000
30108
|
import { realpathSync as realpathSync10 } from "fs";
|
|
30001
30109
|
import { readFile as readFile11, stat as stat10 } from "fs/promises";
|
|
30002
30110
|
import { resolve as resolve42, basename as basename13 } from "path";
|
|
30003
|
-
var
|
|
30111
|
+
var TAG56 = "[telegram:outbound]";
|
|
30004
30112
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
30005
30113
|
async function sendTelegramDocument(input) {
|
|
30006
30114
|
const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
|
|
@@ -30017,10 +30125,10 @@ async function sendTelegramDocument(input) {
|
|
|
30017
30125
|
} catch (err) {
|
|
30018
30126
|
const code = err.code;
|
|
30019
30127
|
if (code === "ENOENT") {
|
|
30020
|
-
console.error(`${
|
|
30128
|
+
console.error(`${TAG56} document ENOENT scope=account-dir accountDir=${accountDir} maxyAccountId=${maxyAccountId}`);
|
|
30021
30129
|
return { ok: false, status: 500, error: `Account directory not resolvable for ${maxyAccountId}` };
|
|
30022
30130
|
}
|
|
30023
|
-
console.error(`${
|
|
30131
|
+
console.error(`${TAG56} document path error scope=account-dir maxyAccountId=${maxyAccountId}: ${String(err)}`);
|
|
30024
30132
|
return { ok: false, status: 500, error: String(err) };
|
|
30025
30133
|
}
|
|
30026
30134
|
let resolvedPath;
|
|
@@ -30029,14 +30137,14 @@ async function sendTelegramDocument(input) {
|
|
|
30029
30137
|
} catch (err) {
|
|
30030
30138
|
const code = err.code;
|
|
30031
30139
|
if (code === "ENOENT") {
|
|
30032
|
-
console.error(`${
|
|
30140
|
+
console.error(`${TAG56} document ENOENT scope=file path=${filePath} maxyAccountId=${maxyAccountId}`);
|
|
30033
30141
|
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
30034
30142
|
}
|
|
30035
|
-
console.error(`${
|
|
30143
|
+
console.error(`${TAG56} document path error scope=file maxyAccountId=${maxyAccountId}: ${String(err)}`);
|
|
30036
30144
|
return { ok: false, status: 500, error: String(err) };
|
|
30037
30145
|
}
|
|
30038
30146
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
30039
|
-
console.error(`${
|
|
30147
|
+
console.error(`${TAG56} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
|
|
30040
30148
|
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
30041
30149
|
}
|
|
30042
30150
|
const fileStat = await stat10(resolvedPath);
|
|
@@ -30069,13 +30177,13 @@ async function sendTelegramDocument(input) {
|
|
|
30069
30177
|
error = e instanceof Error ? e.message : String(e);
|
|
30070
30178
|
}
|
|
30071
30179
|
console.error(
|
|
30072
|
-
`${
|
|
30180
|
+
`${TAG56} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
|
|
30073
30181
|
);
|
|
30074
30182
|
return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
|
|
30075
30183
|
}
|
|
30076
30184
|
|
|
30077
30185
|
// app/lib/telegram/outbound/file-delivery.ts
|
|
30078
|
-
var
|
|
30186
|
+
var TAG57 = "[telegram:outbound]";
|
|
30079
30187
|
function platformRoot4() {
|
|
30080
30188
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
30081
30189
|
}
|
|
@@ -30087,11 +30195,11 @@ function makeTelegramSendFile(entry) {
|
|
|
30087
30195
|
botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
|
|
30088
30196
|
}
|
|
30089
30197
|
if (!botToken) {
|
|
30090
|
-
console.error(`${
|
|
30198
|
+
console.error(`${TAG57} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
|
|
30091
30199
|
return { ok: false, error: "no-bot-token" };
|
|
30092
30200
|
}
|
|
30093
30201
|
if (entry.replyTarget == null) {
|
|
30094
|
-
console.error(`${
|
|
30202
|
+
console.error(`${TAG57} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
|
|
30095
30203
|
return { ok: false, error: "no-reply-target" };
|
|
30096
30204
|
}
|
|
30097
30205
|
const result = await sendTelegramDocument({
|
|
@@ -30108,7 +30216,7 @@ function makeTelegramSendFile(entry) {
|
|
|
30108
30216
|
function makeTelegramFileDelivery(entry) {
|
|
30109
30217
|
return makeFileDelivery({
|
|
30110
30218
|
entry,
|
|
30111
|
-
tag:
|
|
30219
|
+
tag: TAG57,
|
|
30112
30220
|
channel: "telegram",
|
|
30113
30221
|
sendFile: makeTelegramSendFile(entry)
|
|
30114
30222
|
});
|
|
@@ -30149,7 +30257,7 @@ function startTelegramNativeFileFollower(input) {
|
|
|
30149
30257
|
|
|
30150
30258
|
// app/lib/channel-pty-bridge/public-session-end-review.ts
|
|
30151
30259
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
30152
|
-
var
|
|
30260
|
+
var TAG58 = "[public-session-review]";
|
|
30153
30261
|
var CONSUMED_CAP = 500;
|
|
30154
30262
|
var consumed = /* @__PURE__ */ new Set();
|
|
30155
30263
|
function consumeOnce(sessionId) {
|
|
@@ -30176,7 +30284,7 @@ async function fetchJsonl(sessionId) {
|
|
|
30176
30284
|
return await res.text();
|
|
30177
30285
|
} catch (err) {
|
|
30178
30286
|
console.error(
|
|
30179
|
-
`${
|
|
30287
|
+
`${TAG58} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
30180
30288
|
);
|
|
30181
30289
|
return null;
|
|
30182
30290
|
}
|
|
@@ -30200,7 +30308,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
30200
30308
|
const res = await fetch(url);
|
|
30201
30309
|
if (!res.ok) {
|
|
30202
30310
|
console.error(
|
|
30203
|
-
`${
|
|
30311
|
+
`${TAG58} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
|
|
30204
30312
|
);
|
|
30205
30313
|
return [];
|
|
30206
30314
|
}
|
|
@@ -30208,7 +30316,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
30208
30316
|
return Array.isArray(body.writes) ? body.writes : [];
|
|
30209
30317
|
} catch (err) {
|
|
30210
30318
|
console.error(
|
|
30211
|
-
`${
|
|
30319
|
+
`${TAG58} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
30212
30320
|
);
|
|
30213
30321
|
return [];
|
|
30214
30322
|
}
|
|
@@ -30237,7 +30345,7 @@ function composeInitialMessage(input) {
|
|
|
30237
30345
|
}
|
|
30238
30346
|
async function dispatchReviewer(input, initialMessage) {
|
|
30239
30347
|
const sessionId = randomUUID16();
|
|
30240
|
-
console.log(`${
|
|
30348
|
+
console.log(`${TAG58} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
|
|
30241
30349
|
const spawned = await managerRcSpawn({
|
|
30242
30350
|
sessionId,
|
|
30243
30351
|
initialMessage,
|
|
@@ -30257,21 +30365,21 @@ async function firePublicSessionEndReview(input) {
|
|
|
30257
30365
|
const dispatchedAt = Date.now();
|
|
30258
30366
|
if (consumed.has(input.sessionId)) {
|
|
30259
30367
|
console.log(
|
|
30260
|
-
`${
|
|
30368
|
+
`${TAG58} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
30261
30369
|
);
|
|
30262
30370
|
return;
|
|
30263
30371
|
}
|
|
30264
30372
|
const jsonl = await fetchJsonl(input.sessionId);
|
|
30265
30373
|
if (!jsonl) {
|
|
30266
30374
|
console.log(
|
|
30267
|
-
`${
|
|
30375
|
+
`${TAG58} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
|
|
30268
30376
|
);
|
|
30269
30377
|
return;
|
|
30270
30378
|
}
|
|
30271
30379
|
const operatorTurns = countOperatorTurns(jsonl);
|
|
30272
30380
|
if (operatorTurns === 0) {
|
|
30273
30381
|
console.log(
|
|
30274
|
-
`${
|
|
30382
|
+
`${TAG58} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
|
|
30275
30383
|
);
|
|
30276
30384
|
return;
|
|
30277
30385
|
}
|
|
@@ -30285,19 +30393,19 @@ async function firePublicSessionEndReview(input) {
|
|
|
30285
30393
|
});
|
|
30286
30394
|
if (!consumeOnce(input.sessionId)) {
|
|
30287
30395
|
console.log(
|
|
30288
|
-
`${
|
|
30396
|
+
`${TAG58} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
30289
30397
|
);
|
|
30290
30398
|
return;
|
|
30291
30399
|
}
|
|
30292
30400
|
const dispatched = await dispatchReviewer(input, initialMessage);
|
|
30293
30401
|
if (!dispatched.ok) {
|
|
30294
30402
|
console.error(
|
|
30295
|
-
`${
|
|
30403
|
+
`${TAG58} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
|
|
30296
30404
|
);
|
|
30297
30405
|
return;
|
|
30298
30406
|
}
|
|
30299
30407
|
console.log(
|
|
30300
|
-
`${
|
|
30408
|
+
`${TAG58} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
|
|
30301
30409
|
);
|
|
30302
30410
|
}
|
|
30303
30411
|
|
|
@@ -30752,6 +30860,7 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
30752
30860
|
console.log(`[alias-domains] reloaded ${updated.size} alias domain(s): ${[...updated].join(", ")}`);
|
|
30753
30861
|
});
|
|
30754
30862
|
attachRouteCacheWatchers();
|
|
30863
|
+
startManagerEventsSubscriber();
|
|
30755
30864
|
function isPublicHost(host) {
|
|
30756
30865
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
30757
30866
|
}
|
|
@@ -30959,6 +31068,13 @@ registerLoop({
|
|
|
30959
31068
|
});
|
|
30960
31069
|
console.log("[1789-sequel] op=cluster-complete items=[1835,1836,1837,1839]");
|
|
30961
31070
|
var webchatGateway = new WebchatGateway({
|
|
31071
|
+
// Task 1997 — a permission prompt opening or closing changes the pointer with
|
|
31072
|
+
// no filesystem event behind it, so it is pushed here. Scoped to the key's own
|
|
31073
|
+
// account; an unresolvable key is skipped rather than broadcast install-wide.
|
|
31074
|
+
onPointerChange: (key) => {
|
|
31075
|
+
const acct = getAccountIdForSession(key);
|
|
31076
|
+
if (acct) notify("webchat-pointer", acct);
|
|
31077
|
+
},
|
|
30962
31078
|
fetchStandingRules: fetchAccountStandingRules,
|
|
30963
31079
|
gatewayUrl: webchatGatewayUrl(),
|
|
30964
31080
|
serverPath: webchatServerPath(),
|