@rubytech/create-maxy-code 0.1.330 → 0.1.332
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +19 -2
- package/package.json +1 -1
- package/payload/platform/config/brand.json +1 -0
- package/payload/platform/lib/account-enumeration/dist/index.d.ts +33 -0
- package/payload/platform/lib/account-enumeration/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/account-enumeration/dist/index.js +76 -0
- package/payload/platform/lib/account-enumeration/dist/index.js.map +1 -1
- package/payload/platform/lib/account-enumeration/src/index.ts +78 -0
- package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts +24 -24
- package/payload/platform/lib/mcp-spawn-tee/dist/index.js +109 -75
- package/payload/platform/lib/mcp-spawn-tee/dist/index.js.map +1 -1
- package/payload/platform/lib/mcp-spawn-tee/src/__tests__/spawn-tee.test.ts +56 -19
- package/payload/platform/lib/mcp-spawn-tee/src/index.ts +112 -77
- package/payload/platform/neo4j/schema.cypher +58 -4
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +13 -1
- package/payload/platform/plugins/admin/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/admin/mcp/dist/index.js +102 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.js +57 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +34 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +78 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -0
- package/payload/platform/plugins/admin/skills/plainly/SKILL.md +7 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
- package/payload/platform/plugins/aeo/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/browser/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/contacts/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/contacts/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.d.ts +2 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js +71 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-create.test.d.ts +2 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-create.test.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-create.test.js +112 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-create.test.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-manage.test.d.ts +2 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-manage.test.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-manage.test.js +102 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-manage.test.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +10 -3
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js +7 -2
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js +5 -4
- package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/package.json +4 -2
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/email/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/memory/mcp/dist/index.js +3 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.js +39 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.js +13 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-estate-agent.md +17 -5
- package/payload/platform/plugins/outlook/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/replicate/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/scheduling/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/telegram/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/url-get/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/whatsapp/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/work/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/plugins/workflows/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +40 -0
- package/payload/platform/scripts/__tests__/sweep-durability.test.sh +103 -0
- package/payload/platform/scripts/lib/provision-account-dir.sh +319 -0
- package/payload/platform/scripts/lib/resolve-account-dir.sh +79 -132
- package/payload/platform/scripts/seed-neo4j.sh +18 -0
- package/payload/platform/scripts/setup-account.sh +28 -361
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +4 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/config.d.ts +18 -0
- package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/config.js +63 -7
- package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +30 -2
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +16 -1
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +7 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +8 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/stuck-turn.d.ts +6 -0
- package/payload/platform/services/claude-session-manager/dist/stuck-turn.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/stuck-turn.js +17 -5
- package/payload/platform/services/claude-session-manager/dist/stuck-turn.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js +25 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.d.ts +17 -0
- package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/notification.js +14 -0
- package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.js +66 -2
- package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts +28 -0
- package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts.map +1 -0
- package/payload/platform/services/webchat-channel/dist/turn-follow.js +188 -0
- package/payload/platform/services/webchat-channel/dist/turn-follow.js.map +1 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +24 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/templates/account.json +2 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-spawn-tee/index.js +109 -75
- package/payload/server/public/assets/AdminLoginScreens-DexSvtjD.js +1 -0
- package/payload/server/public/assets/AdminShell-Bf9tndNg.js +1 -0
- package/payload/server/public/assets/{Checkbox-1flpB9H0.js → Checkbox-D58GsKoQ.js} +1 -1
- package/payload/server/public/assets/{OperatorConversations-DrPqB9Ym.css → OperatorConversations-CDdp2nVn.css} +1 -1
- package/payload/server/public/assets/OperatorConversations-RmqANYz8.js +9 -0
- package/payload/server/public/assets/{admin-BWJTvagN.js → admin-CH5Vkkt4.js} +1 -1
- package/payload/server/public/assets/{browser-XM5e-fn1.js → browser-BwDtzSlr.js} +1 -1
- package/payload/server/public/assets/chat-3GY0LmYH.js +1 -0
- package/payload/server/public/assets/data-CttrzhfL.js +1 -0
- package/payload/server/public/assets/{graph-CIIz9Dmh.js → graph-CZSdF2jv.js} +3 -3
- package/payload/server/public/assets/{graph-labels-BRKvhE0e.js → graph-labels-Bi0fu8Ns.js} +1 -1
- package/payload/server/public/assets/operator-BEivxmRy.js +1 -0
- package/payload/server/public/assets/page-DUvT07SB.js +30 -0
- package/payload/server/public/assets/{public-BPlZnopr.js → public-B3eIlVbd.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/chat.html +5 -5
- package/payload/server/public/data.html +4 -4
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +6 -6
- package/payload/server/public/operator.html +7 -7
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +605 -435
- package/payload/server/public/assets/AdminLoginScreens-BnuaXvxN.js +0 -1
- package/payload/server/public/assets/AdminShell-Br0DJQNu.js +0 -1
- package/payload/server/public/assets/OperatorConversations-C8sITz2_.js +0 -9
- package/payload/server/public/assets/chat-COFMxaoC.js +0 -1
- package/payload/server/public/assets/data-BHtJozT2.js +0 -1
- package/payload/server/public/assets/operator-9hbxrXKm.js +0 -1
- package/payload/server/public/assets/page-CXkBfOhG.js +0 -30
package/payload/server/server.js
CHANGED
|
@@ -1293,8 +1293,8 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1293
1293
|
};
|
|
1294
1294
|
|
|
1295
1295
|
// server/index.ts
|
|
1296
|
-
import { readFileSync as
|
|
1297
|
-
import { resolve as resolve33, join as
|
|
1296
|
+
import { readFileSync as readFileSync32, existsSync as existsSync31, watchFile } from "fs";
|
|
1297
|
+
import { resolve as resolve33, join as join29, basename as basename10 } from "path";
|
|
1298
1298
|
import { homedir as homedir3 } from "os";
|
|
1299
1299
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
1300
1300
|
|
|
@@ -1650,7 +1650,7 @@ async function ensureAuth() {
|
|
|
1650
1650
|
}
|
|
1651
1651
|
|
|
1652
1652
|
// server/routes/health.ts
|
|
1653
|
-
import { existsSync as existsSync3, readFileSync as
|
|
1653
|
+
import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
|
|
1654
1654
|
import { createConnection as createConnection2 } from "net";
|
|
1655
1655
|
|
|
1656
1656
|
// app/lib/network.ts
|
|
@@ -3297,6 +3297,23 @@ function enumerateValidAccountIds(accountsDir) {
|
|
|
3297
3297
|
cache.set(accountsDir, valid);
|
|
3298
3298
|
return valid;
|
|
3299
3299
|
}
|
|
3300
|
+
function resolveHouseOrSoleAccountId(accountsDir) {
|
|
3301
|
+
const valid = enumerateValidAccountIds(accountsDir);
|
|
3302
|
+
const houses = [];
|
|
3303
|
+
for (const id of valid) {
|
|
3304
|
+
const configPath2 = resolve3(accountsDir, id, "account.json");
|
|
3305
|
+
try {
|
|
3306
|
+
const cfg = JSON.parse(readFileSync4(configPath2, "utf-8"));
|
|
3307
|
+
if (cfg.role === "house") houses.push(id);
|
|
3308
|
+
} catch {
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
if (houses.length === 1) return houses[0];
|
|
3312
|
+
if (houses.length === 0 && valid.length === 1) return valid[0];
|
|
3313
|
+
throw new Error(
|
|
3314
|
+
`[account-registry] resolveHouseOrSoleAccountId: houses=${houses.length} total=${valid.length} under ${accountsDir} \u2014 expected exactly one role:"house" (or a single unlabelled account). Loud-fail rather than picking one silently.`
|
|
3315
|
+
);
|
|
3316
|
+
}
|
|
3300
3317
|
function getAccountsDirFromEnv() {
|
|
3301
3318
|
const root = process.env.MAXY_PLATFORM_ROOT;
|
|
3302
3319
|
if (!root) {
|
|
@@ -3324,26 +3341,39 @@ var cached = null;
|
|
|
3324
3341
|
var cachedAccountsDir = null;
|
|
3325
3342
|
function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
|
|
3326
3343
|
if (cached !== null && cachedAccountsDir === accountsDir) return cached;
|
|
3327
|
-
const
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3344
|
+
const resolved = resolveHouseOrSoleAccountId(accountsDir);
|
|
3345
|
+
cached = resolved;
|
|
3346
|
+
cachedAccountsDir = accountsDir;
|
|
3347
|
+
return cached;
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
// app/lib/managed-service.ts
|
|
3351
|
+
import { join as join3 } from "path";
|
|
3352
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3353
|
+
function isHouseManagedService(accountsDir = ACCOUNTS_DIR) {
|
|
3354
|
+
let houseId;
|
|
3355
|
+
try {
|
|
3356
|
+
houseId = resolvePlatformAccountId(accountsDir);
|
|
3357
|
+
} catch {
|
|
3358
|
+
return false;
|
|
3332
3359
|
}
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3360
|
+
try {
|
|
3361
|
+
const cfg = JSON.parse(
|
|
3362
|
+
readFileSync5(join3(accountsDir, houseId, "account.json"), "utf-8")
|
|
3336
3363
|
);
|
|
3364
|
+
return cfg.managedService === true;
|
|
3365
|
+
} catch {
|
|
3366
|
+
return false;
|
|
3337
3367
|
}
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
return
|
|
3368
|
+
}
|
|
3369
|
+
function isCustomerTurnSuppressed(role, managedService) {
|
|
3370
|
+
return managedService === true && role !== "admin";
|
|
3341
3371
|
}
|
|
3342
3372
|
|
|
3343
3373
|
// app/lib/whatsapp/inbound/media.ts
|
|
3344
3374
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3345
3375
|
import { writeFile, mkdir } from "fs/promises";
|
|
3346
|
-
import { join as
|
|
3376
|
+
import { join as join4 } from "path";
|
|
3347
3377
|
import {
|
|
3348
3378
|
downloadMediaMessage,
|
|
3349
3379
|
downloadContentFromMessage,
|
|
@@ -3429,7 +3459,7 @@ async function downloadInboundMedia(msg, sock, opts) {
|
|
|
3429
3459
|
await mkdir(MEDIA_DIR, { recursive: true });
|
|
3430
3460
|
const ext = mimeToExt(mimetype ?? "application/octet-stream");
|
|
3431
3461
|
const filename = `${randomUUID3()}.${ext}`;
|
|
3432
|
-
const filePath =
|
|
3462
|
+
const filePath = join4(MEDIA_DIR, filename);
|
|
3433
3463
|
await writeFile(filePath, buffer);
|
|
3434
3464
|
const sizeKB = (buffer.length / 1024).toFixed(0);
|
|
3435
3465
|
console.error(`${TAG9} media downloaded type=${mimetype ?? "unknown"} size=${sizeKB}KB path=${filePath}`);
|
|
@@ -4471,6 +4501,12 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4471
4501
|
`${TAG13} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (mediaResult ? ` mediaPath=${mediaResult.path}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
|
|
4472
4502
|
);
|
|
4473
4503
|
if (!accessResult.allowed) return;
|
|
4504
|
+
if (isCustomerTurnSuppressed(accessResult.agentType, isHouseManagedService())) {
|
|
4505
|
+
console.error(
|
|
4506
|
+
`[channel-house] op=inbound from=${senderPhone} accountId=${conn.platformAccountId} autoReply=suppressed`
|
|
4507
|
+
);
|
|
4508
|
+
return;
|
|
4509
|
+
}
|
|
4474
4510
|
const sendReceipts = whatsAppConfig.accounts?.[conn.accountId]?.sendReadReceipts ?? whatsAppConfig.sendReadReceipts ?? true;
|
|
4475
4511
|
if (sendReceipts && msg.key.id) {
|
|
4476
4512
|
sendReadReceipt(conn.sock, remoteJid, [msg.key.id], isGroup ? senderJid : void 0);
|
|
@@ -4541,7 +4577,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4541
4577
|
// app/lib/vnc.ts
|
|
4542
4578
|
import { spawnSync, execFileSync } from "child_process";
|
|
4543
4579
|
import { createConnection } from "net";
|
|
4544
|
-
import { mkdirSync, readFileSync as
|
|
4580
|
+
import { mkdirSync, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
4545
4581
|
import { resolve as resolve4 } from "path";
|
|
4546
4582
|
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
|
|
4547
4583
|
var VNC_SCRIPT = resolve4(PLATFORM_ROOT3, "scripts/vnc.sh");
|
|
@@ -4599,7 +4635,7 @@ function discoverNativeDisplay() {
|
|
|
4599
4635
|
const leaderPid = leaderResult.stdout?.trim();
|
|
4600
4636
|
if (leaderPid) {
|
|
4601
4637
|
try {
|
|
4602
|
-
const environ =
|
|
4638
|
+
const environ = readFileSync6(`/proc/${leaderPid}/environ`, "utf8");
|
|
4603
4639
|
const match = environ.split("\0").find((e) => e.startsWith("WAYLAND_DISPLAY="));
|
|
4604
4640
|
if (match) waylandDisplay = match.split("=")[1];
|
|
4605
4641
|
} catch {
|
|
@@ -4859,7 +4895,7 @@ app.get("/", async (c) => {
|
|
|
4859
4895
|
let pinConfigured = false;
|
|
4860
4896
|
try {
|
|
4861
4897
|
if (existsSync3(USERS_FILE)) {
|
|
4862
|
-
const raw =
|
|
4898
|
+
const raw = readFileSync7(USERS_FILE, "utf-8").trim();
|
|
4863
4899
|
if (raw) {
|
|
4864
4900
|
const users = JSON.parse(raw);
|
|
4865
4901
|
pinConfigured = Array.isArray(users) && users.length > 0;
|
|
@@ -5117,7 +5153,7 @@ async function storeComponentArtefact(accountId, attachmentId, mimeType, content
|
|
|
5117
5153
|
// app/lib/stt/voice-note.ts
|
|
5118
5154
|
import { writeFile as writeFile3, mkdtemp, rm } from "fs/promises";
|
|
5119
5155
|
import { tmpdir } from "os";
|
|
5120
|
-
import { join as
|
|
5156
|
+
import { join as join5 } from "path";
|
|
5121
5157
|
var TAG14 = "[voice]";
|
|
5122
5158
|
var AUDIO_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
5123
5159
|
"audio/ogg",
|
|
@@ -5161,9 +5197,9 @@ async function transcribeVoiceNote(file, source) {
|
|
|
5161
5197
|
let tempDir;
|
|
5162
5198
|
let tempPath;
|
|
5163
5199
|
try {
|
|
5164
|
-
tempDir = await mkdtemp(
|
|
5200
|
+
tempDir = await mkdtemp(join5(tmpdir(), "voice-"));
|
|
5165
5201
|
const ext = audioExtension(mimeType);
|
|
5166
|
-
tempPath =
|
|
5202
|
+
tempPath = join5(tempDir, `recording${ext}`);
|
|
5167
5203
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
5168
5204
|
await writeFile3(tempPath, buffer);
|
|
5169
5205
|
} catch (err) {
|
|
@@ -5240,7 +5276,53 @@ function composePublicSessionTitle(input) {
|
|
|
5240
5276
|
return segments.join(" \xB7 ");
|
|
5241
5277
|
}
|
|
5242
5278
|
|
|
5279
|
+
// app/lib/webchat/persist-customer-inbound.ts
|
|
5280
|
+
var TAG15 = "[channel-house]";
|
|
5281
|
+
async function persistCustomerWebchatInbound(input) {
|
|
5282
|
+
if (!input.text) return null;
|
|
5283
|
+
try {
|
|
5284
|
+
const conv = await ensureConversation(
|
|
5285
|
+
input.houseAccountId,
|
|
5286
|
+
"public",
|
|
5287
|
+
input.sessionKey,
|
|
5288
|
+
input.visitorId,
|
|
5289
|
+
input.agentSlug,
|
|
5290
|
+
void 0,
|
|
5291
|
+
"webchat",
|
|
5292
|
+
input.sessionKey
|
|
5293
|
+
);
|
|
5294
|
+
if (!conv.sessionId) {
|
|
5295
|
+
console.error(
|
|
5296
|
+
`${TAG15} op=persist-skip reason=conversation-merge-failed accountId=${input.houseAccountId} key=${input.sessionKey}`
|
|
5297
|
+
);
|
|
5298
|
+
return null;
|
|
5299
|
+
}
|
|
5300
|
+
const messageId = await persistMessage(
|
|
5301
|
+
conv.sessionId,
|
|
5302
|
+
"user",
|
|
5303
|
+
input.text,
|
|
5304
|
+
input.houseAccountId
|
|
5305
|
+
);
|
|
5306
|
+
if (!messageId) {
|
|
5307
|
+
console.error(
|
|
5308
|
+
`${TAG15} op=persist-skip reason=message-write-failed accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)}`
|
|
5309
|
+
);
|
|
5310
|
+
return null;
|
|
5311
|
+
}
|
|
5312
|
+
console.error(
|
|
5313
|
+
`${TAG15} op=persist accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)} messageId=${messageId.slice(0, 8)} bytes=${Buffer.byteLength(input.text, "utf8")}`
|
|
5314
|
+
);
|
|
5315
|
+
return { sessionId: conv.sessionId, messageId };
|
|
5316
|
+
} catch (err) {
|
|
5317
|
+
console.error(
|
|
5318
|
+
`${TAG15} op=persist-fail accountId=${input.houseAccountId} key=${input.sessionKey} reason=${err instanceof Error ? err.message : String(err)}`
|
|
5319
|
+
);
|
|
5320
|
+
return null;
|
|
5321
|
+
}
|
|
5322
|
+
}
|
|
5323
|
+
|
|
5243
5324
|
// server/routes/chat.ts
|
|
5325
|
+
var MANAGED_SERVICE_ACK = "Thanks, we've received your message and someone will be in touch shortly.";
|
|
5244
5326
|
var WEBCHAT_TAG = "[webchat-adaptor]";
|
|
5245
5327
|
var SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
|
|
5246
5328
|
function webchatTurnTimeoutMs() {
|
|
@@ -5416,6 +5498,38 @@ ${result.result.text}` : result.result.text;
|
|
|
5416
5498
|
console.error(
|
|
5417
5499
|
`${WEBCHAT_TAG} inbound from=${sk} bytes=${Buffer.byteLength(fullMessage, "utf8")} attachments=${attachmentCount} accountId=${account.accountId}`
|
|
5418
5500
|
);
|
|
5501
|
+
if (isCustomerTurnSuppressed("public", isHouseManagedService())) {
|
|
5502
|
+
if (!isGreeting) {
|
|
5503
|
+
await persistCustomerWebchatInbound({
|
|
5504
|
+
houseAccountId: account.accountId,
|
|
5505
|
+
sessionKey: session_key,
|
|
5506
|
+
text: message,
|
|
5507
|
+
...agentSlug ? { agentSlug } : {},
|
|
5508
|
+
...visitorId ? { visitorId } : {}
|
|
5509
|
+
});
|
|
5510
|
+
console.error(
|
|
5511
|
+
`[channel-house] op=inbound from=${sk} accountId=${account.accountId} autoReply=suppressed`
|
|
5512
|
+
);
|
|
5513
|
+
}
|
|
5514
|
+
const ackStream = new ReadableStream({
|
|
5515
|
+
start(controller) {
|
|
5516
|
+
if (!isGreeting) {
|
|
5517
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ text: MANAGED_SERVICE_ACK })}
|
|
5518
|
+
|
|
5519
|
+
`));
|
|
5520
|
+
}
|
|
5521
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
5522
|
+
controller.close();
|
|
5523
|
+
}
|
|
5524
|
+
});
|
|
5525
|
+
return new Response(ackStream, {
|
|
5526
|
+
headers: {
|
|
5527
|
+
"Content-Type": "text/event-stream",
|
|
5528
|
+
"Cache-Control": "no-cache",
|
|
5529
|
+
Connection: "keep-alive"
|
|
5530
|
+
}
|
|
5531
|
+
});
|
|
5532
|
+
}
|
|
5419
5533
|
const readable = new ReadableStream({
|
|
5420
5534
|
async start(controller) {
|
|
5421
5535
|
try {
|
|
@@ -5483,23 +5597,23 @@ ${result.result.text}` : result.result.text;
|
|
|
5483
5597
|
}
|
|
5484
5598
|
|
|
5485
5599
|
// server/routes/whatsapp.ts
|
|
5486
|
-
import { join as
|
|
5487
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
5600
|
+
import { join as join8, resolve as resolve9 } from "path";
|
|
5601
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync9, existsSync as existsSync5 } from "fs";
|
|
5488
5602
|
|
|
5489
5603
|
// app/lib/whatsapp/login.ts
|
|
5490
5604
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
5491
5605
|
|
|
5492
5606
|
// app/lib/whatsapp/config-persist.ts
|
|
5493
|
-
import { readFileSync as
|
|
5494
|
-
import { resolve as resolve7, join as
|
|
5495
|
-
var
|
|
5607
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "fs";
|
|
5608
|
+
import { resolve as resolve7, join as join6 } from "path";
|
|
5609
|
+
var TAG16 = "[whatsapp:config]";
|
|
5496
5610
|
function configPath(accountDir) {
|
|
5497
5611
|
return resolve7(accountDir, "account.json");
|
|
5498
5612
|
}
|
|
5499
5613
|
function readConfig(accountDir) {
|
|
5500
5614
|
const path2 = configPath(accountDir);
|
|
5501
5615
|
if (!existsSync4(path2)) throw new Error(`account.json not found at ${path2}`);
|
|
5502
|
-
return JSON.parse(
|
|
5616
|
+
return JSON.parse(readFileSync8(path2, "utf-8"));
|
|
5503
5617
|
}
|
|
5504
5618
|
function writeConfig(accountDir, config) {
|
|
5505
5619
|
const path2 = configPath(accountDir);
|
|
@@ -5509,9 +5623,9 @@ function reloadManagerConfig(accountDir) {
|
|
|
5509
5623
|
try {
|
|
5510
5624
|
const config = readConfig(accountDir);
|
|
5511
5625
|
reloadConfig(config);
|
|
5512
|
-
console.error(`${
|
|
5626
|
+
console.error(`${TAG16} reloaded manager config`);
|
|
5513
5627
|
} catch (err) {
|
|
5514
|
-
console.error(`${
|
|
5628
|
+
console.error(`${TAG16} manager config reload failed: ${String(err)}`);
|
|
5515
5629
|
}
|
|
5516
5630
|
}
|
|
5517
5631
|
var E164_PATTERN = /^\+\d{7,15}$/;
|
|
@@ -5537,25 +5651,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
|
|
|
5537
5651
|
const adminPhones = wa.adminPhones;
|
|
5538
5652
|
if (!adminPhones.includes(normalized)) {
|
|
5539
5653
|
adminPhones.push(normalized);
|
|
5540
|
-
console.error(`${
|
|
5654
|
+
console.error(`${TAG16} added selfPhone=${normalized} to adminPhones`);
|
|
5541
5655
|
}
|
|
5542
5656
|
} else {
|
|
5543
|
-
console.error(`${
|
|
5657
|
+
console.error(`${TAG16} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
|
|
5544
5658
|
}
|
|
5545
5659
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
5546
5660
|
if (!parsed.success) {
|
|
5547
5661
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
5548
|
-
console.error(`${
|
|
5662
|
+
console.error(`${TAG16} validation failed after pairing: ${msg}`);
|
|
5549
5663
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
5550
5664
|
}
|
|
5551
5665
|
config.whatsapp = parsed.data;
|
|
5552
5666
|
writeConfig(accountDir, config);
|
|
5553
|
-
console.error(`${
|
|
5667
|
+
console.error(`${TAG16} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
|
|
5554
5668
|
reloadManagerConfig(accountDir);
|
|
5555
5669
|
return { ok: true };
|
|
5556
5670
|
} catch (err) {
|
|
5557
5671
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5558
|
-
console.error(`${
|
|
5672
|
+
console.error(`${TAG16} persist failed account=${accountId}: ${msg}`);
|
|
5559
5673
|
return { ok: false, error: msg };
|
|
5560
5674
|
}
|
|
5561
5675
|
}
|
|
@@ -5585,12 +5699,12 @@ function addAdminPhone(accountDir, phone) {
|
|
|
5585
5699
|
}
|
|
5586
5700
|
config.whatsapp = parsed.data;
|
|
5587
5701
|
writeConfig(accountDir, config);
|
|
5588
|
-
console.error(`${
|
|
5702
|
+
console.error(`${TAG16} added admin phone=${normalized}`);
|
|
5589
5703
|
reloadManagerConfig(accountDir);
|
|
5590
5704
|
return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
|
|
5591
5705
|
} catch (err) {
|
|
5592
5706
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5593
|
-
console.error(`${
|
|
5707
|
+
console.error(`${TAG16} addAdminPhone failed: ${msg}`);
|
|
5594
5708
|
return { ok: false, error: msg };
|
|
5595
5709
|
}
|
|
5596
5710
|
}
|
|
@@ -5618,12 +5732,12 @@ function removeAdminPhone(accountDir, phone) {
|
|
|
5618
5732
|
}
|
|
5619
5733
|
config.whatsapp = parsed.data;
|
|
5620
5734
|
writeConfig(accountDir, config);
|
|
5621
|
-
console.error(`${
|
|
5735
|
+
console.error(`${TAG16} removed admin phone=${normalized}`);
|
|
5622
5736
|
reloadManagerConfig(accountDir);
|
|
5623
5737
|
return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
|
|
5624
5738
|
} catch (err) {
|
|
5625
5739
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5626
|
-
console.error(`${
|
|
5740
|
+
console.error(`${TAG16} removeAdminPhone failed: ${msg}`);
|
|
5627
5741
|
return { ok: false, error: msg };
|
|
5628
5742
|
}
|
|
5629
5743
|
}
|
|
@@ -5643,7 +5757,7 @@ function setPublicAgent(accountDir, slug) {
|
|
|
5643
5757
|
if (!trimmed) {
|
|
5644
5758
|
return { ok: false, error: "Agent slug cannot be empty." };
|
|
5645
5759
|
}
|
|
5646
|
-
const agentConfigPath =
|
|
5760
|
+
const agentConfigPath = join6(accountDir, "agents", trimmed, "config.json");
|
|
5647
5761
|
if (!existsSync4(agentConfigPath)) {
|
|
5648
5762
|
return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
|
|
5649
5763
|
}
|
|
@@ -5661,12 +5775,12 @@ function setPublicAgent(accountDir, slug) {
|
|
|
5661
5775
|
}
|
|
5662
5776
|
config.whatsapp = parsed.data;
|
|
5663
5777
|
writeConfig(accountDir, config);
|
|
5664
|
-
console.error(`${
|
|
5778
|
+
console.error(`${TAG16} publicAgent set to ${trimmed}`);
|
|
5665
5779
|
reloadManagerConfig(accountDir);
|
|
5666
5780
|
return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
|
|
5667
5781
|
} catch (err) {
|
|
5668
5782
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5669
|
-
console.error(`${
|
|
5783
|
+
console.error(`${TAG16} setPublicAgent failed: ${msg}`);
|
|
5670
5784
|
return { ok: false, error: msg };
|
|
5671
5785
|
}
|
|
5672
5786
|
}
|
|
@@ -5707,17 +5821,17 @@ function updateConfig(accountDir, fields) {
|
|
|
5707
5821
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
5708
5822
|
if (!parsed.success) {
|
|
5709
5823
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
5710
|
-
console.error(`${
|
|
5824
|
+
console.error(`${TAG16} update validation failed: ${msg}`);
|
|
5711
5825
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
5712
5826
|
}
|
|
5713
5827
|
config.whatsapp = parsed.data;
|
|
5714
5828
|
writeConfig(accountDir, config);
|
|
5715
|
-
console.error(`${
|
|
5829
|
+
console.error(`${TAG16} updated fields=[${fieldNames.join(",")}]`);
|
|
5716
5830
|
reloadManagerConfig(accountDir);
|
|
5717
5831
|
return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
|
|
5718
5832
|
} catch (err) {
|
|
5719
5833
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5720
|
-
console.error(`${
|
|
5834
|
+
console.error(`${TAG16} updateConfig failed: ${msg}`);
|
|
5721
5835
|
return { ok: false, error: msg };
|
|
5722
5836
|
}
|
|
5723
5837
|
}
|
|
@@ -5754,17 +5868,17 @@ function migrateRemovedConfigKeys(accountDir) {
|
|
|
5754
5868
|
}
|
|
5755
5869
|
writeConfig(accountDir, config);
|
|
5756
5870
|
console.error(
|
|
5757
|
-
`${
|
|
5871
|
+
`${TAG16} migration: stripped unknown keys=[${result.droppedKeys.join(",")}] from account.json`
|
|
5758
5872
|
);
|
|
5759
5873
|
return { dropped: result.droppedKeys };
|
|
5760
5874
|
} catch (err) {
|
|
5761
|
-
console.error(`${
|
|
5875
|
+
console.error(`${TAG16} migration failed: ${String(err)}`);
|
|
5762
5876
|
return { dropped: [] };
|
|
5763
5877
|
}
|
|
5764
5878
|
}
|
|
5765
5879
|
|
|
5766
5880
|
// app/lib/whatsapp/login.ts
|
|
5767
|
-
var
|
|
5881
|
+
var TAG17 = "[whatsapp:login]";
|
|
5768
5882
|
function maskPhone(digits) {
|
|
5769
5883
|
if (digits.length <= 4) return "*".repeat(digits.length);
|
|
5770
5884
|
return digits.slice(0, 2) + "*".repeat(digits.length - 4) + digits.slice(-2);
|
|
@@ -5775,7 +5889,7 @@ function closeSocket(sock) {
|
|
|
5775
5889
|
try {
|
|
5776
5890
|
sock.ws?.close?.();
|
|
5777
5891
|
} catch (err) {
|
|
5778
|
-
console.warn(`${
|
|
5892
|
+
console.warn(`${TAG17} socket close error during cleanup: ${String(err)}`);
|
|
5779
5893
|
}
|
|
5780
5894
|
}
|
|
5781
5895
|
function resetActiveLogin(accountId) {
|
|
@@ -5802,18 +5916,18 @@ async function runPostPairing(login) {
|
|
|
5802
5916
|
const masked = selfPhone ? `${selfPhone.slice(0, 4)}***` : "null";
|
|
5803
5917
|
console.error(`[whatsapp-persist] wa-persist-on-connect account=${login.accountId} phone=${masked}`);
|
|
5804
5918
|
} else {
|
|
5805
|
-
console.error(`${
|
|
5919
|
+
console.error(`${TAG17} wa-persist-on-connect FAILED account=${login.accountId}: ${result.error}`);
|
|
5806
5920
|
}
|
|
5807
5921
|
} catch (err) {
|
|
5808
|
-
console.error(`${
|
|
5922
|
+
console.error(`${TAG17} wa-persist-on-connect error account=${login.accountId}: ${String(err)}`);
|
|
5809
5923
|
}
|
|
5810
5924
|
} else {
|
|
5811
|
-
console.error(`${
|
|
5925
|
+
console.error(`${TAG17} wa-persist-on-connect skipped \u2014 no accountDir account=${login.accountId}`);
|
|
5812
5926
|
}
|
|
5813
5927
|
try {
|
|
5814
5928
|
await registerLoginSocket(login.accountId, login.sock, login.authDir);
|
|
5815
5929
|
} catch (err) {
|
|
5816
|
-
console.error(`${
|
|
5930
|
+
console.error(`${TAG17} registerLoginSocket failed account=${login.accountId}: ${String(err)}`);
|
|
5817
5931
|
}
|
|
5818
5932
|
}
|
|
5819
5933
|
async function loginConnectionLoop(accountId, login) {
|
|
@@ -5825,7 +5939,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
5825
5939
|
if (current?.id === login.id) {
|
|
5826
5940
|
await runPostPairing(current);
|
|
5827
5941
|
current.connected = true;
|
|
5828
|
-
console.error(`${
|
|
5942
|
+
console.error(`${TAG17} loginConnectionLoop: connected account=${accountId} attempt=${attempt} configPersisted=${current.configPersisted}`);
|
|
5829
5943
|
}
|
|
5830
5944
|
return;
|
|
5831
5945
|
} catch (err) {
|
|
@@ -5835,7 +5949,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
5835
5949
|
if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
|
|
5836
5950
|
if (attempt >= LOGIN_MAX_RECONNECTS) {
|
|
5837
5951
|
console.error(
|
|
5838
|
-
`${
|
|
5952
|
+
`${TAG17} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
|
|
5839
5953
|
);
|
|
5840
5954
|
current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
|
|
5841
5955
|
} else {
|
|
@@ -5847,7 +5961,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
5847
5961
|
attempt++;
|
|
5848
5962
|
const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
|
|
5849
5963
|
console.error(
|
|
5850
|
-
`${
|
|
5964
|
+
`${TAG17} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
|
|
5851
5965
|
);
|
|
5852
5966
|
closeSocket(current.sock);
|
|
5853
5967
|
await new Promise((r) => setTimeout(r, delay));
|
|
@@ -5858,7 +5972,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
5858
5972
|
current.sock = newSock;
|
|
5859
5973
|
} catch (sockErr) {
|
|
5860
5974
|
console.error(
|
|
5861
|
-
`${
|
|
5975
|
+
`${TAG17} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
|
|
5862
5976
|
);
|
|
5863
5977
|
current.error = `Reconnection failed: ${String(sockErr)}`;
|
|
5864
5978
|
return;
|
|
@@ -5872,7 +5986,7 @@ async function startLogin(opts) {
|
|
|
5872
5986
|
const hasAuth = await authExists(authDir);
|
|
5873
5987
|
const selfId = readSelfId(authDir);
|
|
5874
5988
|
console.error(
|
|
5875
|
-
`${
|
|
5989
|
+
`${TAG17} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasCode=${!!existing0.pairingCode}}` : " activeLogin=none")
|
|
5876
5990
|
);
|
|
5877
5991
|
if (hasAuth && !force) {
|
|
5878
5992
|
const who = selfId.e164 ?? selfId.jid ?? "unknown";
|
|
@@ -5887,7 +6001,7 @@ async function startLogin(opts) {
|
|
|
5887
6001
|
}
|
|
5888
6002
|
const existing = activeLogins.get(accountId);
|
|
5889
6003
|
if (existing && isLoginFresh(existing) && existing.pairingCode && !force) {
|
|
5890
|
-
console.error(`${
|
|
6004
|
+
console.error(`${TAG17} startLogin account=${accountId} guard: returning existing pairing code (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
|
|
5891
6005
|
return {
|
|
5892
6006
|
pairingCode: existing.pairingCode,
|
|
5893
6007
|
phone: existing.phone,
|
|
@@ -5895,7 +6009,7 @@ async function startLogin(opts) {
|
|
|
5895
6009
|
};
|
|
5896
6010
|
}
|
|
5897
6011
|
if (existing) {
|
|
5898
|
-
console.error(`${
|
|
6012
|
+
console.error(`${TAG17} startLogin account=${accountId} ${force ? "force override" : "stale/no-code"}, resetting active login`);
|
|
5899
6013
|
}
|
|
5900
6014
|
resetActiveLogin(accountId);
|
|
5901
6015
|
await clearAuth(authDir);
|
|
@@ -5925,7 +6039,7 @@ async function startLogin(opts) {
|
|
|
5925
6039
|
const current = activeLogins.get(accountId);
|
|
5926
6040
|
if (current?.id !== login.id) return;
|
|
5927
6041
|
if (!current.pairingCode) current.pairingCode = code;
|
|
5928
|
-
console.error(`${
|
|
6042
|
+
console.error(`${TAG17} pairing-code-issued account=${accountId} phone=${maskPhone(digits)} codeLen=${code.length}`);
|
|
5929
6043
|
resolveCode?.(code);
|
|
5930
6044
|
}).catch((err) => {
|
|
5931
6045
|
clearTimeout(codeTimer);
|
|
@@ -5954,7 +6068,7 @@ async function startLogin(opts) {
|
|
|
5954
6068
|
const ttlTimer = setTimeout(() => {
|
|
5955
6069
|
const current = activeLogins.get(accountId);
|
|
5956
6070
|
if (current?.id === login.id && !current.connected) {
|
|
5957
|
-
console.error(`${
|
|
6071
|
+
console.error(`${TAG17} pairing-window-elapsed account=${accountId} (ttl sweep)`);
|
|
5958
6072
|
resetActiveLogin(accountId);
|
|
5959
6073
|
}
|
|
5960
6074
|
}, ACTIVE_LOGIN_TTL_MS);
|
|
@@ -5962,7 +6076,7 @@ async function startLogin(opts) {
|
|
|
5962
6076
|
ttlTimer.unref();
|
|
5963
6077
|
}
|
|
5964
6078
|
loginConnectionLoop(accountId, login).catch((err) => {
|
|
5965
|
-
console.error(`${
|
|
6079
|
+
console.error(`${TAG17} loginConnectionLoop unexpected error: ${String(err)}`);
|
|
5966
6080
|
const current = activeLogins.get(accountId);
|
|
5967
6081
|
if (current?.id === login.id) {
|
|
5968
6082
|
current.error = `Unexpected login error: ${String(err)}`;
|
|
@@ -5987,13 +6101,13 @@ async function waitForLogin(opts) {
|
|
|
5987
6101
|
const { accountId, timeoutMs = 6e4 } = opts;
|
|
5988
6102
|
const login = activeLogins.get(accountId);
|
|
5989
6103
|
console.error(
|
|
5990
|
-
`${
|
|
6104
|
+
`${TAG17} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasCode=${!!login.pairingCode}}` : " login=none")
|
|
5991
6105
|
);
|
|
5992
6106
|
if (!login) {
|
|
5993
6107
|
return { connected: false, message: "No active WhatsApp login in progress.", configPersisted: false };
|
|
5994
6108
|
}
|
|
5995
6109
|
if (!isLoginFresh(login)) {
|
|
5996
|
-
console.error(`${
|
|
6110
|
+
console.error(`${TAG17} pairing-window-elapsed account=${accountId}`);
|
|
5997
6111
|
resetActiveLogin(accountId);
|
|
5998
6112
|
return { connected: false, message: "The pairing window expired. Ask me to generate a new code.", configPersisted: false };
|
|
5999
6113
|
}
|
|
@@ -6001,8 +6115,8 @@ async function waitForLogin(opts) {
|
|
|
6001
6115
|
while (Date.now() < deadline) {
|
|
6002
6116
|
if (login.connected) {
|
|
6003
6117
|
const selfId = readSelfId(login.authDir);
|
|
6004
|
-
console.error(`${
|
|
6005
|
-
console.error(`${
|
|
6118
|
+
console.error(`${TAG17} pairing-connected account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
6119
|
+
console.error(`${TAG17} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"} configPersisted=${login.configPersisted}`);
|
|
6006
6120
|
const configPersisted = login.configPersisted;
|
|
6007
6121
|
activeLogins.delete(accountId);
|
|
6008
6122
|
return {
|
|
@@ -6020,7 +6134,7 @@ async function waitForLogin(opts) {
|
|
|
6020
6134
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
6021
6135
|
}
|
|
6022
6136
|
const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
|
|
6023
|
-
console.error(`${
|
|
6137
|
+
console.error(`${TAG17} waitForLogin poll timeout account=${accountId} elapsed=${elapsed}s \u2014 login kept alive (pairing code still valid)`);
|
|
6024
6138
|
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 };
|
|
6025
6139
|
}
|
|
6026
6140
|
|
|
@@ -6028,7 +6142,7 @@ async function waitForLogin(opts) {
|
|
|
6028
6142
|
import { realpathSync as realpathSync3 } from "fs";
|
|
6029
6143
|
import { readFile, stat as stat2 } from "fs/promises";
|
|
6030
6144
|
import { resolve as resolve8, basename } from "path";
|
|
6031
|
-
var
|
|
6145
|
+
var TAG18 = "[whatsapp:outbound]";
|
|
6032
6146
|
var WHATSAPP_DOCUMENT_MAX_BYTES = 100 * 1024 * 1024;
|
|
6033
6147
|
var lastDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
6034
6148
|
var lastRouteDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
@@ -6062,16 +6176,16 @@ async function sendWhatsAppDocument(input) {
|
|
|
6062
6176
|
const accountResolved = realpathSync3(accountDir);
|
|
6063
6177
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
6064
6178
|
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
6065
|
-
console.error(`${
|
|
6179
|
+
console.error(`${TAG18} document REJECTED path=${sanitised} reason=outside_account_directory`);
|
|
6066
6180
|
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
6067
6181
|
}
|
|
6068
6182
|
} catch (err) {
|
|
6069
6183
|
const code = err.code;
|
|
6070
6184
|
if (code === "ENOENT") {
|
|
6071
|
-
console.error(`${
|
|
6185
|
+
console.error(`${TAG18} document ENOENT path=${filePath}`);
|
|
6072
6186
|
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
6073
6187
|
}
|
|
6074
|
-
console.error(`${
|
|
6188
|
+
console.error(`${TAG18} document path error: ${String(err)}`);
|
|
6075
6189
|
return { ok: false, status: 500, error: String(err) };
|
|
6076
6190
|
}
|
|
6077
6191
|
const fileStat = await stat2(resolvedPath);
|
|
@@ -6086,7 +6200,7 @@ async function sendWhatsAppDocument(input) {
|
|
|
6086
6200
|
const jid = normalizeJid2(to);
|
|
6087
6201
|
const sock = getSocket(accountId);
|
|
6088
6202
|
if (!sock) {
|
|
6089
|
-
console.error(`${
|
|
6203
|
+
console.error(`${TAG18} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=false reason=not-connected`);
|
|
6090
6204
|
return { ok: false, status: 503, error: `WhatsApp account "${accountId}" is not connected` };
|
|
6091
6205
|
}
|
|
6092
6206
|
const buffer = Buffer.from(await readFile(resolvedPath));
|
|
@@ -6098,7 +6212,7 @@ async function sendWhatsAppDocument(input) {
|
|
|
6098
6212
|
{ accountId }
|
|
6099
6213
|
);
|
|
6100
6214
|
console.error(
|
|
6101
|
-
`${
|
|
6215
|
+
`${TAG18} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
|
|
6102
6216
|
);
|
|
6103
6217
|
if (result.success) {
|
|
6104
6218
|
recordDocumentOutbound(to);
|
|
@@ -6217,11 +6331,11 @@ function serializeWhatsAppSchema() {
|
|
|
6217
6331
|
|
|
6218
6332
|
// app/lib/whatsapp/status-reconcile.ts
|
|
6219
6333
|
import { readdirSync as readdirSync2 } from "fs";
|
|
6220
|
-
import { join as
|
|
6221
|
-
var
|
|
6334
|
+
import { join as join7 } from "path";
|
|
6335
|
+
var TAG19 = "[whatsapp:reconcile]";
|
|
6222
6336
|
function listCredsAccountIds(credsRoot) {
|
|
6223
6337
|
try {
|
|
6224
|
-
return readdirSync2(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(
|
|
6338
|
+
return readdirSync2(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(join7(credsRoot, e.name))).map((e) => e.name);
|
|
6225
6339
|
} catch {
|
|
6226
6340
|
return [];
|
|
6227
6341
|
}
|
|
@@ -6236,18 +6350,18 @@ function reconcileCredsOnDisk(opts) {
|
|
|
6236
6350
|
const entries = [];
|
|
6237
6351
|
for (const accountId of listCredsAccountIds(credsRoot)) {
|
|
6238
6352
|
if (liveAccountIds.has(accountId)) continue;
|
|
6239
|
-
const authDir =
|
|
6353
|
+
const authDir = join7(credsRoot, accountId);
|
|
6240
6354
|
const selfPhone = readSelfId(authDir).e164 ?? void 0;
|
|
6241
6355
|
const configured = accountDir ? isAccountConfigured(accountDir, accountId) : false;
|
|
6242
6356
|
console.error(
|
|
6243
|
-
`${
|
|
6357
|
+
`${TAG19} wa-link-unconfigured account=${accountId} creds=present config=${configured ? "present" : "absent"}`
|
|
6244
6358
|
);
|
|
6245
6359
|
if (!configured && accountDir) {
|
|
6246
6360
|
const result = persistAfterPairing(accountDir, accountId, selfPhone ?? null);
|
|
6247
6361
|
if (result.ok) {
|
|
6248
|
-
console.error(`${
|
|
6362
|
+
console.error(`${TAG19} self-healed account=${accountId}`);
|
|
6249
6363
|
} else {
|
|
6250
|
-
console.error(`${
|
|
6364
|
+
console.error(`${TAG19} self-heal FAILED account=${accountId}: ${result.error}`);
|
|
6251
6365
|
}
|
|
6252
6366
|
}
|
|
6253
6367
|
entries.push({ accountId, selfPhone, connected: false, linkedUnconfigured: !configured });
|
|
@@ -6256,22 +6370,22 @@ function reconcileCredsOnDisk(opts) {
|
|
|
6256
6370
|
}
|
|
6257
6371
|
|
|
6258
6372
|
// server/routes/whatsapp.ts
|
|
6259
|
-
var
|
|
6373
|
+
var TAG20 = "[whatsapp:api]";
|
|
6260
6374
|
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
6261
6375
|
var app2 = new Hono();
|
|
6262
6376
|
app2.get("/status", (c) => {
|
|
6263
6377
|
try {
|
|
6264
6378
|
const live = getStatus();
|
|
6265
6379
|
const liveIds = new Set(live.map((a) => a.accountId));
|
|
6266
|
-
const credsRoot =
|
|
6380
|
+
const credsRoot = join8(MAXY_DIR, "credentials", "whatsapp");
|
|
6267
6381
|
const accountDir = resolveAccount()?.accountDir ?? null;
|
|
6268
6382
|
const reconciled = reconcileCredsOnDisk({ credsRoot, accountDir, liveAccountIds: liveIds });
|
|
6269
6383
|
const accounts = [...live, ...reconciled];
|
|
6270
6384
|
const summary = accounts.map((a) => `${a.accountId}:${a.connected ? "up" : a.linkedUnconfigured ? "linked-unconfigured" : "down"}`).join(", ");
|
|
6271
|
-
console.error(`${
|
|
6385
|
+
console.error(`${TAG20} status accounts=${accounts.length} [${summary}]`);
|
|
6272
6386
|
return c.json({ accounts });
|
|
6273
6387
|
} catch (err) {
|
|
6274
|
-
console.error(`${
|
|
6388
|
+
console.error(`${TAG20} status error: ${String(err)}`);
|
|
6275
6389
|
return c.json({ error: String(err) }, 500);
|
|
6276
6390
|
}
|
|
6277
6391
|
});
|
|
@@ -6284,13 +6398,13 @@ app2.post("/login/start", async (c) => {
|
|
|
6284
6398
|
if (!phone) {
|
|
6285
6399
|
return c.json({ error: 'Missing required field "phone" (E.164, e.g. +441234567890).' }, 400);
|
|
6286
6400
|
}
|
|
6287
|
-
const authDir =
|
|
6401
|
+
const authDir = join8(MAXY_DIR, "credentials", "whatsapp", accountId);
|
|
6288
6402
|
const accountDir = resolveAccount()?.accountDir ?? null;
|
|
6289
6403
|
const result = await startLogin({ accountId, authDir, phone, accountDir, force });
|
|
6290
|
-
console.error(`${
|
|
6404
|
+
console.error(`${TAG20} login/start result account=${accountId} hasCode=${!!result.pairingCode}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
|
|
6291
6405
|
return c.json(result);
|
|
6292
6406
|
} catch (err) {
|
|
6293
|
-
console.error(`${
|
|
6407
|
+
console.error(`${TAG20} login/start error: ${String(err)}`);
|
|
6294
6408
|
return c.json({ error: String(err) }, 500);
|
|
6295
6409
|
}
|
|
6296
6410
|
});
|
|
@@ -6300,7 +6414,7 @@ app2.post("/login/wait", async (c) => {
|
|
|
6300
6414
|
const accountId = validateAccountId(body.accountId);
|
|
6301
6415
|
const timeoutMs = body.timeoutMs ?? 6e4;
|
|
6302
6416
|
const result = await waitForLogin({ accountId, timeoutMs });
|
|
6303
|
-
console.error(`${
|
|
6417
|
+
console.error(`${TAG20} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${result.configPersisted}`);
|
|
6304
6418
|
return c.json({
|
|
6305
6419
|
connected: result.connected,
|
|
6306
6420
|
message: result.message,
|
|
@@ -6308,7 +6422,7 @@ app2.post("/login/wait", async (c) => {
|
|
|
6308
6422
|
configPersisted: result.configPersisted
|
|
6309
6423
|
});
|
|
6310
6424
|
} catch (err) {
|
|
6311
|
-
console.error(`${
|
|
6425
|
+
console.error(`${TAG20} login/wait error: ${String(err)}`);
|
|
6312
6426
|
return c.json({ error: String(err) }, 500);
|
|
6313
6427
|
}
|
|
6314
6428
|
});
|
|
@@ -6319,7 +6433,7 @@ app2.post("/disconnect", async (c) => {
|
|
|
6319
6433
|
await stopConnection(accountId);
|
|
6320
6434
|
return c.json({ disconnected: true, accountId });
|
|
6321
6435
|
} catch (err) {
|
|
6322
|
-
console.error(`${
|
|
6436
|
+
console.error(`${TAG20} disconnect error: ${String(err)}`);
|
|
6323
6437
|
return c.json({ error: String(err) }, 500);
|
|
6324
6438
|
}
|
|
6325
6439
|
});
|
|
@@ -6330,7 +6444,7 @@ app2.post("/reconnect", async (c) => {
|
|
|
6330
6444
|
await startConnection(accountId);
|
|
6331
6445
|
return c.json({ reconnecting: true, accountId });
|
|
6332
6446
|
} catch (err) {
|
|
6333
|
-
console.error(`${
|
|
6447
|
+
console.error(`${TAG20} reconnect error: ${String(err)}`);
|
|
6334
6448
|
return c.json({ error: String(err) }, 500);
|
|
6335
6449
|
}
|
|
6336
6450
|
});
|
|
@@ -6351,7 +6465,7 @@ app2.post("/config", async (c) => {
|
|
|
6351
6465
|
return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
|
|
6352
6466
|
}
|
|
6353
6467
|
const result = addAdminPhone(account.accountDir, phone);
|
|
6354
|
-
console.error(`${
|
|
6468
|
+
console.error(`${TAG20} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
|
|
6355
6469
|
return c.json(result, result.ok ? 200 : 400);
|
|
6356
6470
|
}
|
|
6357
6471
|
case "remove-admin-phone": {
|
|
@@ -6359,12 +6473,12 @@ app2.post("/config", async (c) => {
|
|
|
6359
6473
|
return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
|
|
6360
6474
|
}
|
|
6361
6475
|
const result = removeAdminPhone(account.accountDir, phone);
|
|
6362
|
-
console.error(`${
|
|
6476
|
+
console.error(`${TAG20} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
|
|
6363
6477
|
return c.json(result, result.ok ? 200 : 400);
|
|
6364
6478
|
}
|
|
6365
6479
|
case "list-admin-phones": {
|
|
6366
6480
|
const phones = readAdminPhones(account.accountDir);
|
|
6367
|
-
console.error(`${
|
|
6481
|
+
console.error(`${TAG20} config action=list-admin-phones count=${phones.length}`);
|
|
6368
6482
|
return c.json({ ok: true, phones });
|
|
6369
6483
|
}
|
|
6370
6484
|
case "set-public-agent": {
|
|
@@ -6372,13 +6486,13 @@ app2.post("/config", async (c) => {
|
|
|
6372
6486
|
return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
|
|
6373
6487
|
}
|
|
6374
6488
|
const result = setPublicAgent(account.accountDir, slug);
|
|
6375
|
-
console.error(`${
|
|
6489
|
+
console.error(`${TAG20} config action=set-public-agent slug=${slug} ok=${result.ok}`);
|
|
6376
6490
|
return c.json(result, result.ok ? 200 : 400);
|
|
6377
6491
|
}
|
|
6378
6492
|
case "get-public-agent": {
|
|
6379
6493
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
6380
6494
|
const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount });
|
|
6381
|
-
console.error(`${
|
|
6495
|
+
console.error(`${TAG20} config action=get-public-agent accountId=${targetAccount} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
|
|
6382
6496
|
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
6383
6497
|
}
|
|
6384
6498
|
case "list-public-agents": {
|
|
@@ -6392,29 +6506,29 @@ app2.post("/config", async (c) => {
|
|
|
6392
6506
|
const configPath2 = resolve9(agentsDir, entry.name, "config.json");
|
|
6393
6507
|
if (!existsSync5(configPath2)) continue;
|
|
6394
6508
|
try {
|
|
6395
|
-
const config = JSON.parse(
|
|
6509
|
+
const config = JSON.parse(readFileSync9(configPath2, "utf-8"));
|
|
6396
6510
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
6397
6511
|
} catch {
|
|
6398
|
-
console.error(`${
|
|
6512
|
+
console.error(`${TAG20} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
6399
6513
|
}
|
|
6400
6514
|
}
|
|
6401
6515
|
} catch (err) {
|
|
6402
|
-
console.error(`${
|
|
6516
|
+
console.error(`${TAG20} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
|
|
6403
6517
|
}
|
|
6404
6518
|
}
|
|
6405
|
-
console.error(`${
|
|
6519
|
+
console.error(`${TAG20} config action=list-public-agents count=${agents.length}`);
|
|
6406
6520
|
return c.json({ ok: true, agents });
|
|
6407
6521
|
}
|
|
6408
6522
|
case "schema": {
|
|
6409
6523
|
const text = serializeWhatsAppSchema();
|
|
6410
|
-
console.error(`${
|
|
6524
|
+
console.error(`${TAG20} config action=schema`);
|
|
6411
6525
|
return c.json({ ok: true, text });
|
|
6412
6526
|
}
|
|
6413
6527
|
case "list-groups": {
|
|
6414
6528
|
const groupAccountId = accountId ?? "default";
|
|
6415
6529
|
const sock = getSocket(groupAccountId);
|
|
6416
6530
|
if (!sock) {
|
|
6417
|
-
console.error(`${
|
|
6531
|
+
console.error(`${TAG20} config action=list-groups error="not connected" accountId=${groupAccountId}`);
|
|
6418
6532
|
return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
|
|
6419
6533
|
}
|
|
6420
6534
|
try {
|
|
@@ -6424,10 +6538,10 @@ app2.post("/config", async (c) => {
|
|
|
6424
6538
|
name: g.subject ?? g.id,
|
|
6425
6539
|
participantCount: Array.isArray(g.participants) ? g.participants.length : 0
|
|
6426
6540
|
}));
|
|
6427
|
-
console.error(`${
|
|
6541
|
+
console.error(`${TAG20} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
|
|
6428
6542
|
return c.json({ ok: true, groups });
|
|
6429
6543
|
} catch (err) {
|
|
6430
|
-
console.error(`${
|
|
6544
|
+
console.error(`${TAG20} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
|
|
6431
6545
|
return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
|
|
6432
6546
|
}
|
|
6433
6547
|
}
|
|
@@ -6437,12 +6551,12 @@ app2.post("/config", async (c) => {
|
|
|
6437
6551
|
}
|
|
6438
6552
|
const result = updateConfig(account.accountDir, fields);
|
|
6439
6553
|
const fieldNames = Object.keys(fields);
|
|
6440
|
-
console.error(`${
|
|
6554
|
+
console.error(`${TAG20} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
|
|
6441
6555
|
return c.json(result, result.ok ? 200 : 400);
|
|
6442
6556
|
}
|
|
6443
6557
|
case "get-config": {
|
|
6444
6558
|
const waConfig = getConfig(account.accountDir);
|
|
6445
|
-
console.error(`${
|
|
6559
|
+
console.error(`${TAG20} config action=get-config`);
|
|
6446
6560
|
return c.json({ ok: true, config: waConfig });
|
|
6447
6561
|
}
|
|
6448
6562
|
default:
|
|
@@ -6452,7 +6566,7 @@ app2.post("/config", async (c) => {
|
|
|
6452
6566
|
);
|
|
6453
6567
|
}
|
|
6454
6568
|
} catch (err) {
|
|
6455
|
-
console.error(`${
|
|
6569
|
+
console.error(`${TAG20} config error: ${String(err)}`);
|
|
6456
6570
|
return c.json({ ok: false, error: String(err) }, 500);
|
|
6457
6571
|
}
|
|
6458
6572
|
});
|
|
@@ -6473,7 +6587,7 @@ app2.post("/send-document", async (c) => {
|
|
|
6473
6587
|
recordRouteDocumentOutbound(to, filePath);
|
|
6474
6588
|
return c.json({ success: true, messageId: result.messageId });
|
|
6475
6589
|
} catch (err) {
|
|
6476
|
-
console.error(`${
|
|
6590
|
+
console.error(`${TAG20} send-document error: ${String(err)}`);
|
|
6477
6591
|
return c.json({ error: String(err) }, 500);
|
|
6478
6592
|
}
|
|
6479
6593
|
});
|
|
@@ -6483,11 +6597,11 @@ app2.get("/activity", (c) => {
|
|
|
6483
6597
|
const result = getChannelActivity(accountId);
|
|
6484
6598
|
const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
|
|
6485
6599
|
console.error(
|
|
6486
|
-
`${
|
|
6600
|
+
`${TAG20} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
|
|
6487
6601
|
);
|
|
6488
6602
|
return c.json(result);
|
|
6489
6603
|
} catch (err) {
|
|
6490
|
-
console.error(`${
|
|
6604
|
+
console.error(`${TAG20} activity error: ${String(err)}`);
|
|
6491
6605
|
return c.json({ error: String(err) }, 500);
|
|
6492
6606
|
}
|
|
6493
6607
|
});
|
|
@@ -6506,10 +6620,10 @@ app2.get("/conversations", (c) => {
|
|
|
6506
6620
|
};
|
|
6507
6621
|
});
|
|
6508
6622
|
conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
|
|
6509
|
-
console.error(`${
|
|
6623
|
+
console.error(`${TAG20} conversations account=${accountId} count=${conversations.length}`);
|
|
6510
6624
|
return c.json({ conversations });
|
|
6511
6625
|
} catch (err) {
|
|
6512
|
-
console.error(`${
|
|
6626
|
+
console.error(`${TAG20} conversations error: ${String(err)}`);
|
|
6513
6627
|
return c.json({ error: String(err) }, 500);
|
|
6514
6628
|
}
|
|
6515
6629
|
});
|
|
@@ -6524,10 +6638,10 @@ app2.get("/messages", (c) => {
|
|
|
6524
6638
|
const limit = limitParam ? parseInt(limitParam, 10) : void 0;
|
|
6525
6639
|
const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
|
|
6526
6640
|
const messages = getMessages(accountId, jid, effectiveLimit);
|
|
6527
|
-
console.error(`${
|
|
6641
|
+
console.error(`${TAG20} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
|
|
6528
6642
|
return c.json({ messages });
|
|
6529
6643
|
} catch (err) {
|
|
6530
|
-
console.error(`${
|
|
6644
|
+
console.error(`${TAG20} messages error: ${String(err)}`);
|
|
6531
6645
|
return c.json({ error: String(err) }, 500);
|
|
6532
6646
|
}
|
|
6533
6647
|
});
|
|
@@ -6608,7 +6722,7 @@ app2.get("/conversation-graph-state", async (c) => {
|
|
|
6608
6722
|
}
|
|
6609
6723
|
} catch (err) {
|
|
6610
6724
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6611
|
-
console.error(`${
|
|
6725
|
+
console.error(`${TAG20} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
|
|
6612
6726
|
return c.json({ error: `Graph query failed: ${msg}`, cacheKey, cypher: cypher.trim() }, 500);
|
|
6613
6727
|
}
|
|
6614
6728
|
const ms = Date.now() - t0;
|
|
@@ -6621,7 +6735,7 @@ app2.get("/conversation-graph-state", async (c) => {
|
|
|
6621
6735
|
ms
|
|
6622
6736
|
});
|
|
6623
6737
|
} catch (err) {
|
|
6624
|
-
console.error(`${
|
|
6738
|
+
console.error(`${TAG20} conversation-graph-state error: ${String(err)}`);
|
|
6625
6739
|
return c.json({ error: String(err) }, 500);
|
|
6626
6740
|
}
|
|
6627
6741
|
});
|
|
@@ -6633,12 +6747,12 @@ app2.get("/group-info", async (c) => {
|
|
|
6633
6747
|
return c.json({ error: "Missing required parameter: jid" }, 400);
|
|
6634
6748
|
}
|
|
6635
6749
|
if (!isGroupJid(jid)) {
|
|
6636
|
-
console.error(`${
|
|
6750
|
+
console.error(`${TAG20} group-info error="not a group JID" jid=${jid} account=${accountId}`);
|
|
6637
6751
|
return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
|
|
6638
6752
|
}
|
|
6639
6753
|
const sock = getSocket(accountId);
|
|
6640
6754
|
if (!sock) {
|
|
6641
|
-
console.error(`${
|
|
6755
|
+
console.error(`${TAG20} group-info error="not connected" account=${accountId}`);
|
|
6642
6756
|
return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
|
|
6643
6757
|
}
|
|
6644
6758
|
const meta = await sock.groupMetadata(jid);
|
|
@@ -6651,22 +6765,22 @@ app2.get("/group-info", async (c) => {
|
|
|
6651
6765
|
participantCount: meta.participants.length,
|
|
6652
6766
|
participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
|
|
6653
6767
|
};
|
|
6654
|
-
console.error(`${
|
|
6768
|
+
console.error(`${TAG20} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
|
|
6655
6769
|
return c.json(result);
|
|
6656
6770
|
} catch (err) {
|
|
6657
|
-
console.error(`${
|
|
6771
|
+
console.error(`${TAG20} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
|
|
6658
6772
|
return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
|
|
6659
6773
|
}
|
|
6660
6774
|
});
|
|
6661
6775
|
var whatsapp_default = app2;
|
|
6662
6776
|
|
|
6663
6777
|
// server/routes/whatsapp-reader.ts
|
|
6664
|
-
import { readFileSync as
|
|
6665
|
-
import { basename as basename2, dirname, isAbsolute, join as
|
|
6778
|
+
import { readFileSync as readFileSync13, watch, statSync as statSync4, openSync, readSync, closeSync, existsSync as existsSync7, readdirSync as readdirSync7 } from "fs";
|
|
6779
|
+
import { basename as basename2, dirname, isAbsolute, join as join12, relative as relative2, resolve as resolve11, sep as sep3 } from "path";
|
|
6666
6780
|
|
|
6667
6781
|
// server/routes/admin/sidebar-sessions.ts
|
|
6668
|
-
import { readdirSync as readdirSync4, readFileSync as
|
|
6669
|
-
import { join as
|
|
6782
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync2 } from "fs";
|
|
6783
|
+
import { join as join9, resolve as resolve10 } from "path";
|
|
6670
6784
|
|
|
6671
6785
|
// app/lib/whatsapp-reader/select-sessions.ts
|
|
6672
6786
|
function isReaderChannelSession(role, channel) {
|
|
@@ -6725,7 +6839,7 @@ function enumerateJsonls(projectsRoot) {
|
|
|
6725
6839
|
throw err;
|
|
6726
6840
|
}
|
|
6727
6841
|
for (const slug of slugs) {
|
|
6728
|
-
const slugDir =
|
|
6842
|
+
const slugDir = join9(projectsRoot, slug);
|
|
6729
6843
|
let entries;
|
|
6730
6844
|
try {
|
|
6731
6845
|
entries = readdirSync4(slugDir, { withFileTypes: true });
|
|
@@ -6736,9 +6850,9 @@ function enumerateJsonls(projectsRoot) {
|
|
|
6736
6850
|
}
|
|
6737
6851
|
for (const entry of entries) {
|
|
6738
6852
|
if (entry.isFile() && SESSION_ID_RE.test(entry.name)) {
|
|
6739
|
-
out.push({ path:
|
|
6853
|
+
out.push({ path: join9(slugDir, entry.name), isSubagent: false, archived: false });
|
|
6740
6854
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
6741
|
-
const subDir =
|
|
6855
|
+
const subDir = join9(slugDir, entry.name);
|
|
6742
6856
|
let subEntries;
|
|
6743
6857
|
try {
|
|
6744
6858
|
subEntries = readdirSync4(subDir, { withFileTypes: true });
|
|
@@ -6749,11 +6863,11 @@ function enumerateJsonls(projectsRoot) {
|
|
|
6749
6863
|
}
|
|
6750
6864
|
for (const sub of subEntries) {
|
|
6751
6865
|
if (sub.isFile() && SESSION_ID_RE.test(sub.name)) {
|
|
6752
|
-
out.push({ path:
|
|
6866
|
+
out.push({ path: join9(subDir, sub.name), isSubagent: true, archived: false });
|
|
6753
6867
|
}
|
|
6754
6868
|
}
|
|
6755
6869
|
} else if (entry.isDirectory() && entry.name === "archive") {
|
|
6756
|
-
const archiveDir =
|
|
6870
|
+
const archiveDir = join9(slugDir, entry.name);
|
|
6757
6871
|
let archiveEntries;
|
|
6758
6872
|
try {
|
|
6759
6873
|
archiveEntries = readdirSync4(archiveDir, { withFileTypes: true });
|
|
@@ -6764,7 +6878,7 @@ function enumerateJsonls(projectsRoot) {
|
|
|
6764
6878
|
}
|
|
6765
6879
|
for (const arc of archiveEntries) {
|
|
6766
6880
|
if (arc.isFile() && SESSION_ID_RE.test(arc.name)) {
|
|
6767
|
-
out.push({ path:
|
|
6881
|
+
out.push({ path: join9(archiveDir, arc.name), isSubagent: false, archived: true });
|
|
6768
6882
|
}
|
|
6769
6883
|
}
|
|
6770
6884
|
}
|
|
@@ -6797,10 +6911,10 @@ async function fetchLiveSessions() {
|
|
|
6797
6911
|
function loadUserTitles(accountDir) {
|
|
6798
6912
|
const out = /* @__PURE__ */ new Map();
|
|
6799
6913
|
if (!accountDir) return out;
|
|
6800
|
-
const path2 =
|
|
6914
|
+
const path2 = join9(accountDir, "session-titles.json");
|
|
6801
6915
|
let raw;
|
|
6802
6916
|
try {
|
|
6803
|
-
raw =
|
|
6917
|
+
raw = readFileSync10(path2, "utf8");
|
|
6804
6918
|
} catch {
|
|
6805
6919
|
return out;
|
|
6806
6920
|
}
|
|
@@ -6823,10 +6937,10 @@ function loadUserTitles(accountDir) {
|
|
|
6823
6937
|
return out;
|
|
6824
6938
|
}
|
|
6825
6939
|
function readSidecarMeta(metaPath) {
|
|
6826
|
-
const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null, visitorId: null, agentSlug: null };
|
|
6940
|
+
const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null, visitorId: null, agentSlug: null, accountId: null };
|
|
6827
6941
|
let raw;
|
|
6828
6942
|
try {
|
|
6829
|
-
raw =
|
|
6943
|
+
raw = readFileSync10(metaPath, "utf8");
|
|
6830
6944
|
} catch {
|
|
6831
6945
|
return empty;
|
|
6832
6946
|
}
|
|
@@ -6848,7 +6962,8 @@ function readSidecarMeta(metaPath) {
|
|
|
6848
6962
|
personId: typeof r.personId === "string" ? r.personId : null,
|
|
6849
6963
|
adminUserId: typeof r.adminUserId === "string" ? r.adminUserId : null,
|
|
6850
6964
|
visitorId: typeof r.visitorId === "string" ? r.visitorId : null,
|
|
6851
|
-
agentSlug: typeof r.agentSlug === "string" ? r.agentSlug : null
|
|
6965
|
+
agentSlug: typeof r.agentSlug === "string" ? r.agentSlug : null,
|
|
6966
|
+
accountId: typeof r.accountId === "string" ? r.accountId : null
|
|
6852
6967
|
};
|
|
6853
6968
|
}
|
|
6854
6969
|
function startsWithCliMarker(s) {
|
|
@@ -6915,12 +7030,33 @@ function resolveTitle(body, sessionId, userTitles) {
|
|
|
6915
7030
|
}
|
|
6916
7031
|
app3.get("/", requireAdminSession, async (c) => {
|
|
6917
7032
|
const accountId = process.env.ACCOUNT_ID ?? null;
|
|
7033
|
+
const switcherUserId = (() => {
|
|
7034
|
+
const cacheKey = c.get("cacheKey");
|
|
7035
|
+
return cacheKey ? getUserIdForSession(cacheKey) : void 0;
|
|
7036
|
+
})();
|
|
7037
|
+
const accounts = [];
|
|
7038
|
+
if (switcherUserId) {
|
|
7039
|
+
for (const a of resolveUserAccounts(switcherUserId)) {
|
|
7040
|
+
let businessName = null;
|
|
7041
|
+
try {
|
|
7042
|
+
const branding = await fetchBranding(a.accountId);
|
|
7043
|
+
businessName = branding?.name || null;
|
|
7044
|
+
} catch {
|
|
7045
|
+
}
|
|
7046
|
+
accounts.push({
|
|
7047
|
+
accountId: a.accountId,
|
|
7048
|
+
role: a.config.role ?? "",
|
|
7049
|
+
businessName,
|
|
7050
|
+
isHouse: a.config.role === "house"
|
|
7051
|
+
});
|
|
7052
|
+
}
|
|
7053
|
+
}
|
|
6918
7054
|
const configDir2 = claudeConfigDir();
|
|
6919
7055
|
if (!configDir2) {
|
|
6920
7056
|
console.error("[admin-sessions-list] CLAUDE_CONFIG_DIR not set; returning empty list");
|
|
6921
|
-
return c.json({ sessions: [], accountId });
|
|
7057
|
+
return c.json({ sessions: [], accountId, accounts });
|
|
6922
7058
|
}
|
|
6923
|
-
const projectsRoot =
|
|
7059
|
+
const projectsRoot = join9(configDir2, "projects");
|
|
6924
7060
|
const jsonls = enumerateJsonls(projectsRoot);
|
|
6925
7061
|
const liveSessions = await fetchLiveSessions();
|
|
6926
7062
|
const accountDir = accountId ? resolve10(ACCOUNTS_DIR, accountId) : null;
|
|
@@ -6947,7 +7083,7 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
6947
7083
|
let body;
|
|
6948
7084
|
let mtimeMs;
|
|
6949
7085
|
try {
|
|
6950
|
-
body =
|
|
7086
|
+
body = readFileSync10(path2, "utf8");
|
|
6951
7087
|
mtimeMs = statSync2(path2).mtimeMs;
|
|
6952
7088
|
} catch {
|
|
6953
7089
|
continue;
|
|
@@ -6956,8 +7092,8 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
6956
7092
|
const pid = live ? liveSessions.get(sessionId) ?? null : null;
|
|
6957
7093
|
const resolved = resolveTitle(body, sessionId, userTitles);
|
|
6958
7094
|
titleSourceCounts[resolved.source] += 1;
|
|
6959
|
-
const metaPath =
|
|
6960
|
-
const { bridgeIds, role, channel: sidecarChannel, personId, adminUserId } = readSidecarMeta(metaPath);
|
|
7095
|
+
const metaPath = join9(projectDir, `${sessionId}.meta.json`);
|
|
7096
|
+
const { bridgeIds, role, channel: sidecarChannel, personId, adminUserId, accountId: rowAccountId } = readSidecarMeta(metaPath);
|
|
6961
7097
|
if (isReaderChannelSession(role, sidecarChannel)) {
|
|
6962
7098
|
excludedChannelCount += 1;
|
|
6963
7099
|
continue;
|
|
@@ -6979,7 +7115,8 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
6979
7115
|
archived,
|
|
6980
7116
|
channel: resolved.channel,
|
|
6981
7117
|
personName: null,
|
|
6982
|
-
model: lastAssistantModel(body)
|
|
7118
|
+
model: lastAssistantModel(body),
|
|
7119
|
+
accountId: rowAccountId
|
|
6983
7120
|
});
|
|
6984
7121
|
if (live) liveCount += 1;
|
|
6985
7122
|
if (isSubagent) subagentCount += 1;
|
|
@@ -7015,20 +7152,20 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
7015
7152
|
console.log(
|
|
7016
7153
|
`[admin-sessions-list] rows=${rows.length} live=${liveCount} subagents=${subagentCount} archived=${archivedCount} excludedChannel=${excludedChannelCount} titles user=${titleSourceCounts.user} ai=${titleSourceCounts.ai} first=${titleSourceCounts["first-message"]} prefix=${titleSourceCounts.prefix} personNames=${personNameCount} adminNames=${adminNameCount} accountId=${accountId ? accountId.slice(0, 8) : "unset"}`
|
|
7017
7154
|
);
|
|
7018
|
-
return c.json({ sessions: rows, accountId });
|
|
7155
|
+
return c.json({ sessions: rows, accountId, accounts });
|
|
7019
7156
|
});
|
|
7020
7157
|
var sidebar_sessions_default = app3;
|
|
7021
7158
|
|
|
7022
7159
|
// app/lib/admin-identity/pin-validator.ts
|
|
7023
7160
|
import { createHash } from "crypto";
|
|
7024
|
-
import { existsSync as existsSync6, readFileSync as
|
|
7025
|
-
import { join as
|
|
7161
|
+
import { existsSync as existsSync6, readFileSync as readFileSync11, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
|
|
7162
|
+
import { join as join10 } from "path";
|
|
7026
7163
|
function hashPin(pin) {
|
|
7027
7164
|
return createHash("sha256").update(pin).digest("hex");
|
|
7028
7165
|
}
|
|
7029
7166
|
function readUsersFile(usersFilePath) {
|
|
7030
7167
|
if (!existsSync6(usersFilePath)) return null;
|
|
7031
|
-
const raw =
|
|
7168
|
+
const raw = readFileSync11(usersFilePath, "utf-8").trim();
|
|
7032
7169
|
if (!raw) return [];
|
|
7033
7170
|
return JSON.parse(raw);
|
|
7034
7171
|
}
|
|
@@ -7056,16 +7193,16 @@ function scanForOrphanedAccountAdmins(users, accountsDir) {
|
|
|
7056
7193
|
}
|
|
7057
7194
|
for (const entry of entries) {
|
|
7058
7195
|
if (entry.startsWith(".")) continue;
|
|
7059
|
-
const accountDir =
|
|
7196
|
+
const accountDir = join10(accountsDir, entry);
|
|
7060
7197
|
try {
|
|
7061
7198
|
if (!statSync3(accountDir).isDirectory()) continue;
|
|
7062
7199
|
} catch {
|
|
7063
7200
|
continue;
|
|
7064
7201
|
}
|
|
7065
|
-
const accountJsonPath =
|
|
7202
|
+
const accountJsonPath = join10(accountDir, "account.json");
|
|
7066
7203
|
if (!existsSync6(accountJsonPath)) continue;
|
|
7067
7204
|
try {
|
|
7068
|
-
const config = JSON.parse(
|
|
7205
|
+
const config = JSON.parse(readFileSync11(accountJsonPath, "utf-8"));
|
|
7069
7206
|
const admins = config.admins ?? [];
|
|
7070
7207
|
for (const a of admins) {
|
|
7071
7208
|
if (typeof a.userId === "string" && !known.has(a.userId)) orphans.push(a.userId);
|
|
@@ -7321,8 +7458,8 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
7321
7458
|
}
|
|
7322
7459
|
|
|
7323
7460
|
// app/lib/whatsapp-reader/enrich-attachments.ts
|
|
7324
|
-
import { readFileSync as
|
|
7325
|
-
import { join as
|
|
7461
|
+
import { readFileSync as readFileSync12, readdirSync as readdirSync6 } from "fs";
|
|
7462
|
+
import { join as join11 } from "path";
|
|
7326
7463
|
var ADMIN_UPLOAD_PATH = /\/uploads\/(?!public\/)([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\//gi;
|
|
7327
7464
|
function adminAttachmentIds(text) {
|
|
7328
7465
|
const ids = [];
|
|
@@ -7335,7 +7472,7 @@ function adminAttachmentIds(text) {
|
|
|
7335
7472
|
function adminAttachmentsFromText(text, uploadsBase) {
|
|
7336
7473
|
const out = [];
|
|
7337
7474
|
for (const id of adminAttachmentIds(text)) {
|
|
7338
|
-
const meta = readAttachmentMeta(
|
|
7475
|
+
const meta = readAttachmentMeta(join11(uploadsBase, id));
|
|
7339
7476
|
if (meta) out.push(meta);
|
|
7340
7477
|
}
|
|
7341
7478
|
return out;
|
|
@@ -7343,7 +7480,7 @@ function adminAttachmentsFromText(text, uploadsBase) {
|
|
|
7343
7480
|
function readSidecar(dir, attachmentId) {
|
|
7344
7481
|
let parsed;
|
|
7345
7482
|
try {
|
|
7346
|
-
parsed = JSON.parse(
|
|
7483
|
+
parsed = JSON.parse(readFileSync12(join11(dir, `${attachmentId}.meta.json`), "utf8"));
|
|
7347
7484
|
} catch {
|
|
7348
7485
|
return null;
|
|
7349
7486
|
}
|
|
@@ -7372,7 +7509,7 @@ function listSessionAttachmentsInDir(dir) {
|
|
|
7372
7509
|
}
|
|
7373
7510
|
const sidecars = [];
|
|
7374
7511
|
for (const name of entries) {
|
|
7375
|
-
const sidecar = readSidecar(
|
|
7512
|
+
const sidecar = readSidecar(join11(dir, name), name);
|
|
7376
7513
|
if (sidecar) sidecars.push(sidecar);
|
|
7377
7514
|
}
|
|
7378
7515
|
sidecars.sort((a, b) => a.storedAt.localeCompare(b.storedAt));
|
|
@@ -7407,17 +7544,17 @@ var app4 = new Hono();
|
|
|
7407
7544
|
app4.get("/conversations", requireAdminSession, async (c) => {
|
|
7408
7545
|
const cfg = claudeConfigDir();
|
|
7409
7546
|
if (!cfg) return c.json({ conversations: [] });
|
|
7410
|
-
const projectsRoot =
|
|
7547
|
+
const projectsRoot = join12(cfg, "projects");
|
|
7411
7548
|
const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
|
|
7412
7549
|
const rows = [];
|
|
7413
7550
|
for (const { path: path2 } of enumerateJsonls(projectsRoot)) {
|
|
7414
7551
|
const sessionId = basename2(path2).replace(/\.jsonl$/, "");
|
|
7415
7552
|
const projectDir = dirname(path2);
|
|
7416
|
-
const meta = readSidecarMeta(
|
|
7553
|
+
const meta = readSidecarMeta(join12(projectDir, `${sessionId}.meta.json`));
|
|
7417
7554
|
if (!isReaderChannelSession(meta.role, meta.channel)) continue;
|
|
7418
7555
|
let body = "";
|
|
7419
7556
|
try {
|
|
7420
|
-
body =
|
|
7557
|
+
body = readFileSync13(path2, "utf8");
|
|
7421
7558
|
} catch {
|
|
7422
7559
|
}
|
|
7423
7560
|
const { title } = resolveTitle(body, sessionId, userTitles);
|
|
@@ -7521,11 +7658,11 @@ app4.get("/stream", requireAdminSession, (c) => {
|
|
|
7521
7658
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
7522
7659
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
7523
7660
|
const cfg = claudeConfigDir();
|
|
7524
|
-
const projectsRoot = cfg ? resolve11(
|
|
7661
|
+
const projectsRoot = cfg ? resolve11(join12(cfg, "projects")) : null;
|
|
7525
7662
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
7526
7663
|
return c.json({ error: "bad session reference" }, 400);
|
|
7527
7664
|
}
|
|
7528
|
-
const jsonlPath = resolve11(
|
|
7665
|
+
const jsonlPath = resolve11(join12(projectDir, `${sessionId}.jsonl`));
|
|
7529
7666
|
if (!jsonlPath.startsWith(projectsRoot + sep3)) {
|
|
7530
7667
|
return c.json({ error: "bad session reference" }, 400);
|
|
7531
7668
|
}
|
|
@@ -7638,7 +7775,7 @@ app4.get("/directives", requireAdminSession, (c) => {
|
|
|
7638
7775
|
const pid = Number(pidPart);
|
|
7639
7776
|
let len = 0;
|
|
7640
7777
|
try {
|
|
7641
|
-
len = statSync4(
|
|
7778
|
+
len = statSync4(join12(dir, name)).size;
|
|
7642
7779
|
} catch {
|
|
7643
7780
|
}
|
|
7644
7781
|
return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
|
|
@@ -7653,11 +7790,11 @@ app4.get("/directive", requireAdminSession, (c) => {
|
|
|
7653
7790
|
}
|
|
7654
7791
|
const dir = directiveStoreDir(sessionId);
|
|
7655
7792
|
if (!dir) return c.json({ error: "no account" }, 404);
|
|
7656
|
-
const path2 = resolve11(
|
|
7793
|
+
const path2 = resolve11(join12(dir, name));
|
|
7657
7794
|
if (!path2.startsWith(dir + sep3)) return c.json({ error: "bad reference" }, 400);
|
|
7658
7795
|
let body;
|
|
7659
7796
|
try {
|
|
7660
|
-
body =
|
|
7797
|
+
body = readFileSync13(path2, "utf8");
|
|
7661
7798
|
} catch {
|
|
7662
7799
|
return c.json({ error: "not found" }, 404);
|
|
7663
7800
|
}
|
|
@@ -7666,8 +7803,8 @@ app4.get("/directive", requireAdminSession, (c) => {
|
|
|
7666
7803
|
var whatsapp_reader_default = app4;
|
|
7667
7804
|
|
|
7668
7805
|
// server/routes/public-reader.ts
|
|
7669
|
-
import { statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2, readFileSync as
|
|
7670
|
-
import { basename as basename3, dirname as dirname2, join as
|
|
7806
|
+
import { statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2, readFileSync as readFileSync14, readdirSync as readdirSync8 } from "fs";
|
|
7807
|
+
import { basename as basename3, dirname as dirname2, join as join13, resolve as resolve12, sep as sep4 } from "path";
|
|
7671
7808
|
|
|
7672
7809
|
// app/lib/whatsapp-reader/delivered-kinds.ts
|
|
7673
7810
|
var PUBLIC_DELIVERED_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -7714,7 +7851,7 @@ function resolveOpenVisitorSession(rows, visitorId, agentSlug) {
|
|
|
7714
7851
|
|
|
7715
7852
|
// server/routes/public-reader.ts
|
|
7716
7853
|
var app5 = new Hono();
|
|
7717
|
-
var
|
|
7854
|
+
var TAG21 = "[public-webchat]";
|
|
7718
7855
|
function parseAccessSessionId(cookieHeader) {
|
|
7719
7856
|
if (!cookieHeader) return null;
|
|
7720
7857
|
const part = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="));
|
|
@@ -7741,10 +7878,10 @@ function enumeratePublicRows() {
|
|
|
7741
7878
|
const cfg = claudeConfigDir();
|
|
7742
7879
|
if (!cfg) return [];
|
|
7743
7880
|
const rows = [];
|
|
7744
|
-
for (const { path: path2 } of enumerateJsonls(
|
|
7881
|
+
for (const { path: path2 } of enumerateJsonls(join13(cfg, "projects"))) {
|
|
7745
7882
|
const sessionId = basename3(path2).replace(/\.jsonl$/, "");
|
|
7746
7883
|
const projectDir = dirname2(path2);
|
|
7747
|
-
const meta = readSidecarMeta(
|
|
7884
|
+
const meta = readSidecarMeta(join13(projectDir, `${sessionId}.meta.json`));
|
|
7748
7885
|
rows.push({
|
|
7749
7886
|
sessionId,
|
|
7750
7887
|
projectDir,
|
|
@@ -7762,7 +7899,7 @@ function enumeratePublicRows() {
|
|
|
7762
7899
|
app5.get("/session", (c) => {
|
|
7763
7900
|
const visitor = resolveVisitor(c);
|
|
7764
7901
|
if (!visitor) {
|
|
7765
|
-
console.error(`${
|
|
7902
|
+
console.error(`${TAG21} op=reader-refused reason=no-anchor path=/session`);
|
|
7766
7903
|
return c.json({ error: "gate-required" }, 401);
|
|
7767
7904
|
}
|
|
7768
7905
|
const rows = enumeratePublicRows();
|
|
@@ -7781,11 +7918,11 @@ app5.get("/session", (c) => {
|
|
|
7781
7918
|
found = effectiveSlug ? resolveOpenVisitorSession(rows, visitor.visitorId, effectiveSlug) : null;
|
|
7782
7919
|
}
|
|
7783
7920
|
if (found) {
|
|
7784
|
-
console.log(`${
|
|
7921
|
+
console.log(`${TAG21} op=session-resume anchor=${visitor.kind} key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
|
|
7785
7922
|
return c.json({ sessionId: found.sessionId, projectDir: found.projectDir, sessionKey: found.senderId });
|
|
7786
7923
|
}
|
|
7787
7924
|
const sessionKey = crypto.randomUUID();
|
|
7788
|
-
console.log(`${
|
|
7925
|
+
console.log(`${TAG21} op=session-new anchor=${visitor.kind} key=${sessionKey.slice(0, 8)}`);
|
|
7789
7926
|
return c.json({ sessionId: null, projectDir: null, sessionKey });
|
|
7790
7927
|
});
|
|
7791
7928
|
function publicUploadsDir(accountId, sessionId) {
|
|
@@ -7814,30 +7951,30 @@ function readFrom2(path2, from) {
|
|
|
7814
7951
|
app5.get("/stream", (c) => {
|
|
7815
7952
|
const visitor = resolveVisitor(c);
|
|
7816
7953
|
if (!visitor) {
|
|
7817
|
-
console.error(`${
|
|
7954
|
+
console.error(`${TAG21} op=reader-refused reason=no-anchor path=/stream`);
|
|
7818
7955
|
return c.json({ error: "gate-required" }, 401);
|
|
7819
7956
|
}
|
|
7820
7957
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
7821
7958
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
7822
7959
|
const cfg = claudeConfigDir();
|
|
7823
|
-
const projectsRoot = cfg ? resolve12(
|
|
7960
|
+
const projectsRoot = cfg ? resolve12(join13(cfg, "projects")) : null;
|
|
7824
7961
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
7825
7962
|
return c.json({ error: "bad session reference" }, 400);
|
|
7826
7963
|
}
|
|
7827
|
-
const jsonlPath = resolve12(
|
|
7964
|
+
const jsonlPath = resolve12(join13(projectDir, `${sessionId}.jsonl`));
|
|
7828
7965
|
if (!jsonlPath.startsWith(projectsRoot + sep4)) {
|
|
7829
7966
|
return c.json({ error: "bad session reference" }, 400);
|
|
7830
7967
|
}
|
|
7831
|
-
const meta = readSidecarMeta(
|
|
7968
|
+
const meta = readSidecarMeta(join13(projectDir, `${sessionId}.meta.json`));
|
|
7832
7969
|
const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
|
|
7833
7970
|
const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
|
|
7834
7971
|
if (!(isPublicWebchat && owns)) {
|
|
7835
|
-
console.error(`${
|
|
7972
|
+
console.error(`${TAG21} op=reader-refused reason=not-owner anchor=${visitor.kind} sessionId=${sessionId.slice(0, 8)}`);
|
|
7836
7973
|
return c.json({ error: "forbidden" }, 403);
|
|
7837
7974
|
}
|
|
7838
7975
|
const senderShort = (meta.senderId ?? "").slice(0, 8);
|
|
7839
7976
|
const encoder = new TextEncoder();
|
|
7840
|
-
console.log(`${
|
|
7977
|
+
console.log(`${TAG21} op=reader-open anchor=${visitor.kind} key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
|
|
7841
7978
|
const send = (controller, turn, id) => controller.enqueue(encoder.encode(`id: ${id}
|
|
7842
7979
|
data: ${JSON.stringify(turn)}
|
|
7843
7980
|
|
|
@@ -7852,7 +7989,7 @@ data: ${JSON.stringify(turn)}
|
|
|
7852
7989
|
const { turns, dropped } = filterDeliveredFrames(lines);
|
|
7853
7990
|
if (uploadsDir) enrichPublicAttachments(turns, listSessionAttachmentsInDir(uploadsDir), attachmentCursor);
|
|
7854
7991
|
for (const turn of turns) send(controller, turn, offset);
|
|
7855
|
-
if (dropped > 0) console.log(`${
|
|
7992
|
+
if (dropped > 0) console.log(`${TAG21} op=reader-filtered key=${senderShort} dropped=${dropped}`);
|
|
7856
7993
|
};
|
|
7857
7994
|
try {
|
|
7858
7995
|
const { buf, end } = readFrom2(jsonlPath, 0);
|
|
@@ -7887,7 +8024,7 @@ data: ${JSON.stringify(turn)}
|
|
|
7887
8024
|
watcher?.close();
|
|
7888
8025
|
clearInterval(poll);
|
|
7889
8026
|
clearInterval(heartbeat);
|
|
7890
|
-
console.log(`${
|
|
8027
|
+
console.log(`${TAG21} op=reader-close key=${senderShort}`);
|
|
7891
8028
|
try {
|
|
7892
8029
|
controller.close();
|
|
7893
8030
|
} catch {
|
|
@@ -7943,7 +8080,7 @@ app5.get("/attachment/:attachmentId", (c) => {
|
|
|
7943
8080
|
try {
|
|
7944
8081
|
dataFile = readdirSync8(dir).find((f) => !f.endsWith(".meta.json"));
|
|
7945
8082
|
if (!dataFile) throw new Error("no data file");
|
|
7946
|
-
buffer =
|
|
8083
|
+
buffer = readFileSync14(resolve12(dir, dataFile));
|
|
7947
8084
|
} catch {
|
|
7948
8085
|
console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
|
|
7949
8086
|
return new Response("Not found", { status: 404 });
|
|
@@ -7962,20 +8099,20 @@ var public_reader_default = app5;
|
|
|
7962
8099
|
|
|
7963
8100
|
// server/routes/webchat.ts
|
|
7964
8101
|
import { basename as basename4, dirname as dirname3 } from "path";
|
|
7965
|
-
import { join as
|
|
7966
|
-
import { existsSync as existsSync9, readdirSync as readdirSync10, readFileSync as
|
|
8102
|
+
import { join as join16 } from "path";
|
|
8103
|
+
import { existsSync as existsSync9, readdirSync as readdirSync10, readFileSync as readFileSync17, renameSync as renameSync3, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
7967
8104
|
|
|
7968
8105
|
// server/canonical-webchat-override.ts
|
|
7969
|
-
import { readFileSync as
|
|
7970
|
-
import { join as
|
|
8106
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync5, renameSync } from "fs";
|
|
8107
|
+
import { join as join14 } from "path";
|
|
7971
8108
|
var FILE = "canonical-webchat-session.json";
|
|
7972
8109
|
var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
7973
8110
|
function canonicalOverridePath(accountDir) {
|
|
7974
|
-
return
|
|
8111
|
+
return join14(accountDir, FILE);
|
|
7975
8112
|
}
|
|
7976
8113
|
function readRaw(accountDir) {
|
|
7977
8114
|
try {
|
|
7978
|
-
return JSON.parse(
|
|
8115
|
+
return JSON.parse(readFileSync15(canonicalOverridePath(accountDir), "utf8"));
|
|
7979
8116
|
} catch {
|
|
7980
8117
|
return null;
|
|
7981
8118
|
}
|
|
@@ -8062,8 +8199,8 @@ function isNewSessionEffortLevel(value) {
|
|
|
8062
8199
|
}
|
|
8063
8200
|
|
|
8064
8201
|
// ../services/claude-session-manager/src/sidecar-store.ts
|
|
8065
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync as readdirSync9, readFileSync as
|
|
8066
|
-
import { join as
|
|
8202
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync as readdirSync9, readFileSync as readFileSync16, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
|
|
8203
|
+
import { join as join15 } from "path";
|
|
8067
8204
|
function escapeRegExp(s) {
|
|
8068
8205
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8069
8206
|
}
|
|
@@ -8071,7 +8208,7 @@ function createSidecarStore(config) {
|
|
|
8071
8208
|
const { suffix, validate: validate2 } = config;
|
|
8072
8209
|
const suffixRe = new RegExp(`${escapeRegExp(suffix)}$`);
|
|
8073
8210
|
function pathFor(sessionsDir, id) {
|
|
8074
|
-
return
|
|
8211
|
+
return join15(sessionsDir, `${id}${suffix}`);
|
|
8075
8212
|
}
|
|
8076
8213
|
function write(sessionsDir, id, record) {
|
|
8077
8214
|
mkdirSync2(sessionsDir, { recursive: true });
|
|
@@ -8092,7 +8229,7 @@ function createSidecarStore(config) {
|
|
|
8092
8229
|
function readFileAt(path2) {
|
|
8093
8230
|
let raw;
|
|
8094
8231
|
try {
|
|
8095
|
-
raw =
|
|
8232
|
+
raw = readFileSync16(path2, "utf8");
|
|
8096
8233
|
} catch {
|
|
8097
8234
|
return null;
|
|
8098
8235
|
}
|
|
@@ -8115,10 +8252,10 @@ function createSidecarStore(config) {
|
|
|
8115
8252
|
const out = [];
|
|
8116
8253
|
for (const name of names) {
|
|
8117
8254
|
if (!suffixRe.test(name)) continue;
|
|
8118
|
-
const path2 =
|
|
8255
|
+
const path2 = join15(sessionsDir, name);
|
|
8119
8256
|
let raw;
|
|
8120
8257
|
try {
|
|
8121
|
-
raw =
|
|
8258
|
+
raw = readFileSync16(path2, "utf8");
|
|
8122
8259
|
} catch {
|
|
8123
8260
|
onSkip?.(name, "unreadable");
|
|
8124
8261
|
continue;
|
|
@@ -8192,10 +8329,10 @@ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9
|
|
|
8192
8329
|
function locateSession(sessionId) {
|
|
8193
8330
|
const cfg = claudeConfigDir();
|
|
8194
8331
|
if (!cfg) return { projectDir: null, channel: null };
|
|
8195
|
-
for (const { path: path2 } of enumerateJsonls(
|
|
8332
|
+
for (const { path: path2 } of enumerateJsonls(join16(cfg, "projects"))) {
|
|
8196
8333
|
if (basename4(path2) === `${sessionId}.jsonl`) {
|
|
8197
8334
|
const dir = dirname3(path2);
|
|
8198
|
-
const meta = readSidecarMeta(
|
|
8335
|
+
const meta = readSidecarMeta(join16(dir, `${sessionId}.meta.json`));
|
|
8199
8336
|
return { projectDir: dir, channel: meta.channel };
|
|
8200
8337
|
}
|
|
8201
8338
|
}
|
|
@@ -8204,7 +8341,7 @@ function locateSession(sessionId) {
|
|
|
8204
8341
|
function locateSidecar(sessionId) {
|
|
8205
8342
|
const cfg = claudeConfigDir();
|
|
8206
8343
|
if (!cfg) return null;
|
|
8207
|
-
const projectsRoot =
|
|
8344
|
+
const projectsRoot = join16(cfg, "projects");
|
|
8208
8345
|
let slugs;
|
|
8209
8346
|
try {
|
|
8210
8347
|
slugs = readdirSync10(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -8212,7 +8349,7 @@ function locateSidecar(sessionId) {
|
|
|
8212
8349
|
return null;
|
|
8213
8350
|
}
|
|
8214
8351
|
for (const slug of slugs) {
|
|
8215
|
-
const metaPath =
|
|
8352
|
+
const metaPath = join16(projectsRoot, slug, `${sessionId}.meta.json`);
|
|
8216
8353
|
if (existsSync9(metaPath)) {
|
|
8217
8354
|
const meta = readSidecarMeta(metaPath);
|
|
8218
8355
|
return { channel: meta.channel, adminUserId: meta.adminUserId };
|
|
@@ -8225,7 +8362,7 @@ function readTargetOwner(sessionId) {
|
|
|
8225
8362
|
if (sidecar !== null) return { found: true, owner: sidecar.adminUserId };
|
|
8226
8363
|
const { projectDir } = locateSession(sessionId);
|
|
8227
8364
|
if (projectDir !== null) {
|
|
8228
|
-
return { found: true, owner: readSidecarMeta(
|
|
8365
|
+
return { found: true, owner: readSidecarMeta(join16(projectDir, `${sessionId}.meta.json`)).adminUserId };
|
|
8229
8366
|
}
|
|
8230
8367
|
return { found: false, owner: null };
|
|
8231
8368
|
}
|
|
@@ -8239,7 +8376,7 @@ function sessionSidecarExists(sessionId) {
|
|
|
8239
8376
|
function jsonlSizeBytes(projectDir, sessionId) {
|
|
8240
8377
|
if (projectDir === null) return null;
|
|
8241
8378
|
try {
|
|
8242
|
-
return statSync6(
|
|
8379
|
+
return statSync6(join16(projectDir, `${sessionId}.jsonl`)).size;
|
|
8243
8380
|
} catch {
|
|
8244
8381
|
return null;
|
|
8245
8382
|
}
|
|
@@ -8327,10 +8464,10 @@ function latestAdminWebchatSessionId(requesterUserId, primaryUserId) {
|
|
|
8327
8464
|
const cfg = claudeConfigDir();
|
|
8328
8465
|
if (!cfg) return null;
|
|
8329
8466
|
let best = null;
|
|
8330
|
-
for (const { path: path2, isSubagent, archived } of enumerateJsonls(
|
|
8467
|
+
for (const { path: path2, isSubagent, archived } of enumerateJsonls(join16(cfg, "projects"))) {
|
|
8331
8468
|
if (isSubagent || archived) continue;
|
|
8332
8469
|
const id = basename4(path2).slice(0, -".jsonl".length);
|
|
8333
|
-
const meta = readSidecarMeta(
|
|
8470
|
+
const meta = readSidecarMeta(join16(dirname3(path2), `${id}.meta.json`));
|
|
8334
8471
|
if (meta.role !== "admin" || meta.channel !== "webchat") continue;
|
|
8335
8472
|
let mtimeMs;
|
|
8336
8473
|
try {
|
|
@@ -8581,9 +8718,9 @@ ${note}` : note;
|
|
|
8581
8718
|
console.error(`[webchat:settings] op=${op} outcome=refused value=${value} reason=account-unresolved`);
|
|
8582
8719
|
return c.json({ error: "account unresolved" }, 503);
|
|
8583
8720
|
}
|
|
8584
|
-
const accountJsonPath =
|
|
8721
|
+
const accountJsonPath = join16(account.accountDir, "account.json");
|
|
8585
8722
|
try {
|
|
8586
|
-
const parsed = JSON.parse(
|
|
8723
|
+
const parsed = JSON.parse(readFileSync17(accountJsonPath, "utf8"));
|
|
8587
8724
|
if (op === "model") parsed.adminModel = value;
|
|
8588
8725
|
else if (op === "effort") parsed.effort = value;
|
|
8589
8726
|
else parsed.adminPermissionMode = value;
|
|
@@ -8628,7 +8765,7 @@ ${note}` : note;
|
|
|
8628
8765
|
const cfg2 = claudeConfigDir();
|
|
8629
8766
|
if (cfg2) {
|
|
8630
8767
|
const source2 = resolveBridgeSource(
|
|
8631
|
-
|
|
8768
|
+
join16(cfg2, "sessions"),
|
|
8632
8769
|
target,
|
|
8633
8770
|
(id) => locateSession(id).projectDir !== null
|
|
8634
8771
|
);
|
|
@@ -8660,7 +8797,7 @@ ${note}` : note;
|
|
|
8660
8797
|
const cfg = claudeConfigDir();
|
|
8661
8798
|
let projectDir = null;
|
|
8662
8799
|
if (cfg) {
|
|
8663
|
-
for (const { path: path2 } of enumerateJsonls(
|
|
8800
|
+
for (const { path: path2 } of enumerateJsonls(join16(cfg, "projects"))) {
|
|
8664
8801
|
if (basename4(path2) === `${sessionId}.jsonl`) {
|
|
8665
8802
|
projectDir = dirname3(path2);
|
|
8666
8803
|
break;
|
|
@@ -8668,7 +8805,7 @@ ${note}` : note;
|
|
|
8668
8805
|
}
|
|
8669
8806
|
}
|
|
8670
8807
|
if (source === "latest" && requesterUserId !== null) {
|
|
8671
|
-
const owner = readSidecarMeta(
|
|
8808
|
+
const owner = readSidecarMeta(join16(projectDir ?? "", `${sessionId}.meta.json`)).adminUserId;
|
|
8672
8809
|
if (owner !== null && owner !== requesterUserId && requesterUserId !== primaryUserId) {
|
|
8673
8810
|
console.error(`[webchat:inbound] op=session-resolve-foreign key=${sessionId.slice(0, 8)} owner=${owner.slice(0, 8)} requester=${requesterUserId.slice(0, 8)}`);
|
|
8674
8811
|
}
|
|
@@ -8700,8 +8837,8 @@ ${note}` : note;
|
|
|
8700
8837
|
import { resolve as resolve13 } from "path";
|
|
8701
8838
|
|
|
8702
8839
|
// app/lib/claude-agent/specialist-roster.ts
|
|
8703
|
-
import { existsSync as existsSync10, readFileSync as
|
|
8704
|
-
import { join as
|
|
8840
|
+
import { existsSync as existsSync10, readFileSync as readFileSync18, readdirSync as readdirSync11 } from "fs";
|
|
8841
|
+
import { join as join17 } from "path";
|
|
8705
8842
|
function field(fm, name) {
|
|
8706
8843
|
const m = fm.match(new RegExp(`^${name}:\\s*(.*?)\\r?$`, "m"));
|
|
8707
8844
|
if (!m) return null;
|
|
@@ -8720,7 +8857,7 @@ function readSpecialistRoster(specialistsDir) {
|
|
|
8720
8857
|
if (!file.endsWith(".md")) continue;
|
|
8721
8858
|
let raw;
|
|
8722
8859
|
try {
|
|
8723
|
-
raw =
|
|
8860
|
+
raw = readFileSync18(join17(specialistsDir, file), "utf-8");
|
|
8724
8861
|
} catch (err) {
|
|
8725
8862
|
skipped.push({ file, reason: err instanceof Error ? err.message : String(err) });
|
|
8726
8863
|
continue;
|
|
@@ -8837,8 +8974,8 @@ var webchat_greeting_default = app6;
|
|
|
8837
8974
|
|
|
8838
8975
|
// server/routes/telegram.ts
|
|
8839
8976
|
import { homedir } from "os";
|
|
8840
|
-
import { resolve as resolve17, join as
|
|
8841
|
-
import { existsSync as existsSync11, readFileSync as
|
|
8977
|
+
import { resolve as resolve17, join as join18 } from "path";
|
|
8978
|
+
import { existsSync as existsSync11, readFileSync as readFileSync19 } from "fs";
|
|
8842
8979
|
|
|
8843
8980
|
// app/lib/channel-pty-bridge/bridge.ts
|
|
8844
8981
|
import { resolve as resolve16 } from "path";
|
|
@@ -8992,7 +9129,7 @@ function classifyTimeout(ndjson, writeAtMs) {
|
|
|
8992
9129
|
|
|
8993
9130
|
// app/lib/channel-pty-bridge/public-session-end-review.ts
|
|
8994
9131
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
8995
|
-
var
|
|
9132
|
+
var TAG22 = "[public-session-review]";
|
|
8996
9133
|
var CONSUMED_CAP = 500;
|
|
8997
9134
|
var consumed = /* @__PURE__ */ new Set();
|
|
8998
9135
|
function consumeOnce(sessionId) {
|
|
@@ -9019,7 +9156,7 @@ async function fetchJsonl(sessionId) {
|
|
|
9019
9156
|
return await res.text();
|
|
9020
9157
|
} catch (err) {
|
|
9021
9158
|
console.error(
|
|
9022
|
-
`${
|
|
9159
|
+
`${TAG22} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
9023
9160
|
);
|
|
9024
9161
|
return null;
|
|
9025
9162
|
}
|
|
@@ -9043,7 +9180,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
9043
9180
|
const res = await fetch(url);
|
|
9044
9181
|
if (!res.ok) {
|
|
9045
9182
|
console.error(
|
|
9046
|
-
`${
|
|
9183
|
+
`${TAG22} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
|
|
9047
9184
|
);
|
|
9048
9185
|
return [];
|
|
9049
9186
|
}
|
|
@@ -9051,7 +9188,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
9051
9188
|
return Array.isArray(body.writes) ? body.writes : [];
|
|
9052
9189
|
} catch (err) {
|
|
9053
9190
|
console.error(
|
|
9054
|
-
`${
|
|
9191
|
+
`${TAG22} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
9055
9192
|
);
|
|
9056
9193
|
return [];
|
|
9057
9194
|
}
|
|
@@ -9080,7 +9217,7 @@ function composeInitialMessage(input) {
|
|
|
9080
9217
|
}
|
|
9081
9218
|
async function dispatchReviewer(input, initialMessage) {
|
|
9082
9219
|
const sessionId = randomUUID7();
|
|
9083
|
-
console.log(`${
|
|
9220
|
+
console.log(`${TAG22} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
|
|
9084
9221
|
const spawned = await managerRcSpawn({
|
|
9085
9222
|
sessionId,
|
|
9086
9223
|
initialMessage,
|
|
@@ -9100,21 +9237,21 @@ async function firePublicSessionEndReview(input) {
|
|
|
9100
9237
|
const dispatchedAt = Date.now();
|
|
9101
9238
|
if (consumed.has(input.sessionId)) {
|
|
9102
9239
|
console.log(
|
|
9103
|
-
`${
|
|
9240
|
+
`${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
9104
9241
|
);
|
|
9105
9242
|
return;
|
|
9106
9243
|
}
|
|
9107
9244
|
const jsonl = await fetchJsonl(input.sessionId);
|
|
9108
9245
|
if (!jsonl) {
|
|
9109
9246
|
console.log(
|
|
9110
|
-
`${
|
|
9247
|
+
`${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
|
|
9111
9248
|
);
|
|
9112
9249
|
return;
|
|
9113
9250
|
}
|
|
9114
9251
|
const operatorTurns = countOperatorTurns(jsonl);
|
|
9115
9252
|
if (operatorTurns === 0) {
|
|
9116
9253
|
console.log(
|
|
9117
|
-
`${
|
|
9254
|
+
`${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
|
|
9118
9255
|
);
|
|
9119
9256
|
return;
|
|
9120
9257
|
}
|
|
@@ -9128,19 +9265,19 @@ async function firePublicSessionEndReview(input) {
|
|
|
9128
9265
|
});
|
|
9129
9266
|
if (!consumeOnce(input.sessionId)) {
|
|
9130
9267
|
console.log(
|
|
9131
|
-
`${
|
|
9268
|
+
`${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
9132
9269
|
);
|
|
9133
9270
|
return;
|
|
9134
9271
|
}
|
|
9135
9272
|
const dispatched = await dispatchReviewer(input, initialMessage);
|
|
9136
9273
|
if (!dispatched.ok) {
|
|
9137
9274
|
console.error(
|
|
9138
|
-
`${
|
|
9275
|
+
`${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
|
|
9139
9276
|
);
|
|
9140
9277
|
return;
|
|
9141
9278
|
}
|
|
9142
9279
|
console.log(
|
|
9143
|
-
`${
|
|
9280
|
+
`${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
|
|
9144
9281
|
);
|
|
9145
9282
|
}
|
|
9146
9283
|
|
|
@@ -9354,7 +9491,7 @@ function makeFileDelivery(opts) {
|
|
|
9354
9491
|
}
|
|
9355
9492
|
|
|
9356
9493
|
// app/lib/whatsapp/inbound/file-delivery-bridge.ts
|
|
9357
|
-
var
|
|
9494
|
+
var TAG23 = "[whatsapp-adaptor]";
|
|
9358
9495
|
var SEND_USER_FILE2 = "SendUserFile";
|
|
9359
9496
|
var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
|
|
9360
9497
|
function platformRoot() {
|
|
@@ -9367,7 +9504,7 @@ function makeWhatsAppSendFile(entry) {
|
|
|
9367
9504
|
maxyAccountId = resolvePlatformAccountId();
|
|
9368
9505
|
} catch (err) {
|
|
9369
9506
|
console.error(
|
|
9370
|
-
`${
|
|
9507
|
+
`${TAG23} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
|
|
9371
9508
|
);
|
|
9372
9509
|
return { ok: false, error: "account-unresolved" };
|
|
9373
9510
|
}
|
|
@@ -9381,7 +9518,7 @@ function makeWhatsAppSendFile(entry) {
|
|
|
9381
9518
|
});
|
|
9382
9519
|
if (result.ok) return { ok: true };
|
|
9383
9520
|
console.error(
|
|
9384
|
-
`${
|
|
9521
|
+
`${TAG23} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
|
|
9385
9522
|
);
|
|
9386
9523
|
return { ok: false, error: result.error };
|
|
9387
9524
|
};
|
|
@@ -9389,7 +9526,7 @@ function makeWhatsAppSendFile(entry) {
|
|
|
9389
9526
|
function makeWhatsAppFileDelivery(entry) {
|
|
9390
9527
|
const shared = makeFileDelivery({
|
|
9391
9528
|
entry,
|
|
9392
|
-
tag:
|
|
9529
|
+
tag: TAG23,
|
|
9393
9530
|
channel: "whatsapp",
|
|
9394
9531
|
sendFile: makeWhatsAppSendFile(entry)
|
|
9395
9532
|
});
|
|
@@ -9424,7 +9561,7 @@ function makeWhatsAppFileDelivery(entry) {
|
|
|
9424
9561
|
if (!delivered) {
|
|
9425
9562
|
const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
|
|
9426
9563
|
console.error(
|
|
9427
|
-
`${
|
|
9564
|
+
`${TAG23} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
|
|
9428
9565
|
);
|
|
9429
9566
|
}
|
|
9430
9567
|
}
|
|
@@ -9436,7 +9573,7 @@ function makeWhatsAppFileDelivery(entry) {
|
|
|
9436
9573
|
import { realpathSync as realpathSync4 } from "fs";
|
|
9437
9574
|
import { readFile as readFile2, stat as stat3 } from "fs/promises";
|
|
9438
9575
|
import { resolve as resolve14, basename as basename5 } from "path";
|
|
9439
|
-
var
|
|
9576
|
+
var TAG24 = "[telegram:outbound]";
|
|
9440
9577
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
9441
9578
|
async function sendTelegramDocument(input) {
|
|
9442
9579
|
const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot4 } = input;
|
|
@@ -9452,16 +9589,16 @@ async function sendTelegramDocument(input) {
|
|
|
9452
9589
|
resolvedPath = realpathSync4(filePath);
|
|
9453
9590
|
const accountResolved = realpathSync4(accountDir);
|
|
9454
9591
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
9455
|
-
console.error(`${
|
|
9592
|
+
console.error(`${TAG24} document REJECTED reason=outside_account_directory`);
|
|
9456
9593
|
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
9457
9594
|
}
|
|
9458
9595
|
} catch (err) {
|
|
9459
9596
|
const code = err.code;
|
|
9460
9597
|
if (code === "ENOENT") {
|
|
9461
|
-
console.error(`${
|
|
9598
|
+
console.error(`${TAG24} document ENOENT path=${filePath}`);
|
|
9462
9599
|
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
9463
9600
|
}
|
|
9464
|
-
console.error(`${
|
|
9601
|
+
console.error(`${TAG24} document path error: ${String(err)}`);
|
|
9465
9602
|
return { ok: false, status: 500, error: String(err) };
|
|
9466
9603
|
}
|
|
9467
9604
|
const fileStat = await stat3(resolvedPath);
|
|
@@ -9494,13 +9631,13 @@ async function sendTelegramDocument(input) {
|
|
|
9494
9631
|
error = e instanceof Error ? e.message : String(e);
|
|
9495
9632
|
}
|
|
9496
9633
|
console.error(
|
|
9497
|
-
`${
|
|
9634
|
+
`${TAG24} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
|
|
9498
9635
|
);
|
|
9499
9636
|
return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
|
|
9500
9637
|
}
|
|
9501
9638
|
|
|
9502
9639
|
// app/lib/telegram/outbound/file-delivery.ts
|
|
9503
|
-
var
|
|
9640
|
+
var TAG25 = "[telegram:outbound]";
|
|
9504
9641
|
function platformRoot2() {
|
|
9505
9642
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
9506
9643
|
}
|
|
@@ -9512,11 +9649,11 @@ function makeTelegramSendFile(entry) {
|
|
|
9512
9649
|
botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
|
|
9513
9650
|
}
|
|
9514
9651
|
if (!botToken) {
|
|
9515
|
-
console.error(`${
|
|
9652
|
+
console.error(`${TAG25} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
|
|
9516
9653
|
return { ok: false, error: "no-bot-token" };
|
|
9517
9654
|
}
|
|
9518
9655
|
if (entry.replyTarget == null) {
|
|
9519
|
-
console.error(`${
|
|
9656
|
+
console.error(`${TAG25} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
|
|
9520
9657
|
return { ok: false, error: "no-reply-target" };
|
|
9521
9658
|
}
|
|
9522
9659
|
let maxyAccountId;
|
|
@@ -9524,7 +9661,7 @@ function makeTelegramSendFile(entry) {
|
|
|
9524
9661
|
maxyAccountId = resolvePlatformAccountId();
|
|
9525
9662
|
} catch (err) {
|
|
9526
9663
|
console.error(
|
|
9527
|
-
`${
|
|
9664
|
+
`${TAG25} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
|
|
9528
9665
|
);
|
|
9529
9666
|
return { ok: false, error: "account-unresolved" };
|
|
9530
9667
|
}
|
|
@@ -9542,7 +9679,7 @@ function makeTelegramSendFile(entry) {
|
|
|
9542
9679
|
function makeTelegramFileDelivery(entry) {
|
|
9543
9680
|
return makeFileDelivery({
|
|
9544
9681
|
entry,
|
|
9545
|
-
tag:
|
|
9682
|
+
tag: TAG25,
|
|
9546
9683
|
channel: "telegram",
|
|
9547
9684
|
sendFile: makeTelegramSendFile(entry)
|
|
9548
9685
|
});
|
|
@@ -9551,7 +9688,7 @@ function makeTelegramFileDelivery(entry) {
|
|
|
9551
9688
|
// app/lib/webchat/file-delivery.ts
|
|
9552
9689
|
import { realpathSync as realpathSync5 } from "fs";
|
|
9553
9690
|
import { resolve as resolve15 } from "path";
|
|
9554
|
-
var
|
|
9691
|
+
var TAG26 = "[webchat-adaptor]";
|
|
9555
9692
|
function platformRoot3() {
|
|
9556
9693
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
9557
9694
|
}
|
|
@@ -9562,7 +9699,7 @@ function makeWebchatSendFile(entry) {
|
|
|
9562
9699
|
maxyAccountId = resolvePlatformAccountId();
|
|
9563
9700
|
} catch (err) {
|
|
9564
9701
|
console.error(
|
|
9565
|
-
`${
|
|
9702
|
+
`${TAG26} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
|
|
9566
9703
|
);
|
|
9567
9704
|
return { ok: false, error: "account-unresolved" };
|
|
9568
9705
|
}
|
|
@@ -9571,14 +9708,14 @@ function makeWebchatSendFile(entry) {
|
|
|
9571
9708
|
const resolved = realpathSync5(filePath);
|
|
9572
9709
|
const accountResolved = realpathSync5(accountDir);
|
|
9573
9710
|
if (!resolved.startsWith(accountResolved + "/")) {
|
|
9574
|
-
console.error(`${
|
|
9711
|
+
console.error(`${TAG26} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
|
|
9575
9712
|
return { ok: false, error: "outside-account" };
|
|
9576
9713
|
}
|
|
9577
9714
|
return { ok: true };
|
|
9578
9715
|
} catch (err) {
|
|
9579
9716
|
const code = err.code;
|
|
9580
9717
|
console.error(
|
|
9581
|
-
`${
|
|
9718
|
+
`${TAG26} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
|
|
9582
9719
|
);
|
|
9583
9720
|
return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
|
|
9584
9721
|
}
|
|
@@ -9587,7 +9724,7 @@ function makeWebchatSendFile(entry) {
|
|
|
9587
9724
|
function makeWebchatFileDelivery(entry) {
|
|
9588
9725
|
return makeFileDelivery({
|
|
9589
9726
|
entry,
|
|
9590
|
-
tag:
|
|
9727
|
+
tag: TAG26,
|
|
9591
9728
|
channel: "webchat",
|
|
9592
9729
|
sendFile: makeWebchatSendFile(entry)
|
|
9593
9730
|
});
|
|
@@ -9955,14 +10092,14 @@ function routeTelegramUpdate(input) {
|
|
|
9955
10092
|
}
|
|
9956
10093
|
|
|
9957
10094
|
// server/routes/telegram.ts
|
|
9958
|
-
var
|
|
10095
|
+
var TAG27 = "[telegram-inbound]";
|
|
9959
10096
|
var DISPATCH_TIMEOUT_MS = 12e4;
|
|
9960
10097
|
function configDirName() {
|
|
9961
10098
|
const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
|
|
9962
|
-
const brandPath =
|
|
10099
|
+
const brandPath = join18(platformRoot4, "config", "brand.json");
|
|
9963
10100
|
if (existsSync11(brandPath)) {
|
|
9964
10101
|
try {
|
|
9965
|
-
return JSON.parse(
|
|
10102
|
+
return JSON.parse(readFileSync19(brandPath, "utf-8")).configDir ?? ".maxy";
|
|
9966
10103
|
} catch {
|
|
9967
10104
|
}
|
|
9968
10105
|
}
|
|
@@ -9990,18 +10127,18 @@ app7.post("/", async (c) => {
|
|
|
9990
10127
|
const botParam = c.req.query("bot");
|
|
9991
10128
|
const botType = botParam === "admin" ? "admin" : botParam === "public" ? "public" : null;
|
|
9992
10129
|
if (!botType) {
|
|
9993
|
-
console.error(`${
|
|
10130
|
+
console.error(`${TAG27} op=reject reason=missing-bot-param`);
|
|
9994
10131
|
return c.json({ ok: false }, 400);
|
|
9995
10132
|
}
|
|
9996
10133
|
const sp = secretPath(botType);
|
|
9997
10134
|
if (!existsSync11(sp)) {
|
|
9998
|
-
console.error(`${
|
|
10135
|
+
console.error(`${TAG27} op=reject reason=no-secret-file botType=${botType}`);
|
|
9999
10136
|
return c.json({ ok: false }, 401);
|
|
10000
10137
|
}
|
|
10001
|
-
const expected =
|
|
10138
|
+
const expected = readFileSync19(sp, "utf-8").trim();
|
|
10002
10139
|
const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
|
|
10003
10140
|
if (got !== expected) {
|
|
10004
|
-
console.error(`${
|
|
10141
|
+
console.error(`${TAG27} op=reject reason=bad-secret botType=${botType}`);
|
|
10005
10142
|
return c.json({ ok: false }, 401);
|
|
10006
10143
|
}
|
|
10007
10144
|
let update;
|
|
@@ -10012,25 +10149,25 @@ app7.post("/", async (c) => {
|
|
|
10012
10149
|
}
|
|
10013
10150
|
const account = resolveAccount();
|
|
10014
10151
|
if (!account) {
|
|
10015
|
-
console.error(`${
|
|
10152
|
+
console.error(`${TAG27} op=reject reason=no-account`);
|
|
10016
10153
|
return c.json({ ok: true }, 200);
|
|
10017
10154
|
}
|
|
10018
10155
|
const decision = routeTelegramUpdate({ update, botType, config: account.config.telegram ?? {} });
|
|
10019
10156
|
if (decision.kind === "ignore") {
|
|
10020
|
-
console.error(`${
|
|
10157
|
+
console.error(`${TAG27} op=ignore reason=${decision.reason}`);
|
|
10021
10158
|
return c.json({ ok: true }, 200);
|
|
10022
10159
|
}
|
|
10023
10160
|
if (decision.kind === "denied") {
|
|
10024
|
-
console.error(`${
|
|
10161
|
+
console.error(`${TAG27} op=denied reason=${decision.reason} agentType=${decision.agentType}`);
|
|
10025
10162
|
return c.json({ ok: true }, 200);
|
|
10026
10163
|
}
|
|
10027
10164
|
const role = decision.agentType === "admin" ? "admin" : "public";
|
|
10028
10165
|
const agentSlug = role === "admin" ? "admin" : resolveDefaultAgentSlug(account.accountDir);
|
|
10029
10166
|
if (!agentSlug) {
|
|
10030
|
-
console.error(`${
|
|
10167
|
+
console.error(`${TAG27} op=reject reason=no-default-agent`);
|
|
10031
10168
|
return c.json({ ok: true }, 200);
|
|
10032
10169
|
}
|
|
10033
|
-
console.error(`${
|
|
10170
|
+
console.error(`${TAG27} op=update accountId=${account.accountId} from=${decision.senderId}`);
|
|
10034
10171
|
const botToken = botType === "admin" ? account.config.telegram?.adminBotToken : account.config.telegram?.publicBotToken;
|
|
10035
10172
|
const { senderId, chatId, text } = decision;
|
|
10036
10173
|
void (async () => {
|
|
@@ -10047,67 +10184,67 @@ app7.post("/", async (c) => {
|
|
|
10047
10184
|
timeoutMs: DISPATCH_TIMEOUT_MS
|
|
10048
10185
|
});
|
|
10049
10186
|
if ("error" in result) {
|
|
10050
|
-
console.error(`${
|
|
10187
|
+
console.error(`${TAG27} op=dispatch-failed from=${senderId} error=${result.error}`);
|
|
10051
10188
|
return;
|
|
10052
10189
|
}
|
|
10053
10190
|
if (!botToken) {
|
|
10054
10191
|
const reason = account.config.telegram ? "no-bot-token" : "no-telegram-config";
|
|
10055
|
-
console.error(`${
|
|
10192
|
+
console.error(`${TAG27} op=reply-dropped reason=${reason} botType=${botType}`);
|
|
10056
10193
|
return;
|
|
10057
10194
|
}
|
|
10058
10195
|
const sent = await sendTelegram(botToken, chatId, result.turnText);
|
|
10059
|
-
console.error(`${
|
|
10196
|
+
console.error(`${TAG27} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
|
|
10060
10197
|
})();
|
|
10061
10198
|
return c.json({ ok: true }, 200);
|
|
10062
10199
|
});
|
|
10063
10200
|
var telegram_default = app7;
|
|
10064
10201
|
|
|
10065
10202
|
// server/routes/quickbooks.ts
|
|
10066
|
-
import { join as
|
|
10067
|
-
import { existsSync as existsSync12, readFileSync as
|
|
10068
|
-
var
|
|
10203
|
+
import { join as join19 } from "path";
|
|
10204
|
+
import { existsSync as existsSync12, readFileSync as readFileSync20, writeFileSync as writeFileSync8, mkdirSync as mkdirSync3, rmSync, renameSync as renameSync4 } from "fs";
|
|
10205
|
+
var TAG28 = "[quickbooks]";
|
|
10069
10206
|
var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
|
|
10070
10207
|
var STATE_RE = /^[A-Za-z0-9_-]+$/;
|
|
10071
10208
|
function pendingPath(accountDir, state) {
|
|
10072
|
-
return
|
|
10209
|
+
return join19(accountDir, "secrets", "quickbooks-pending", `${state}.json`);
|
|
10073
10210
|
}
|
|
10074
10211
|
function storePath(accountDir) {
|
|
10075
|
-
return
|
|
10212
|
+
return join19(accountDir, "secrets", "quickbooks.json");
|
|
10076
10213
|
}
|
|
10077
10214
|
async function completeConsent(args) {
|
|
10078
10215
|
const { accountDir, code, realmId } = args;
|
|
10079
10216
|
const state = args.state;
|
|
10080
10217
|
if (!state || !STATE_RE.test(state) || !existsSync12(pendingPath(accountDir, state))) {
|
|
10081
|
-
console.error(`${
|
|
10218
|
+
console.error(`${TAG28} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
|
|
10082
10219
|
return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
|
|
10083
10220
|
}
|
|
10084
10221
|
const claimFile = `${pendingPath(accountDir, state)}.claimed`;
|
|
10085
10222
|
try {
|
|
10086
10223
|
renameSync4(pendingPath(accountDir, state), claimFile);
|
|
10087
10224
|
} catch {
|
|
10088
|
-
console.error(`${
|
|
10225
|
+
console.error(`${TAG28} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
10089
10226
|
return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
|
|
10090
10227
|
}
|
|
10091
10228
|
try {
|
|
10092
10229
|
let pending;
|
|
10093
10230
|
try {
|
|
10094
|
-
pending = JSON.parse(
|
|
10231
|
+
pending = JSON.parse(readFileSync20(claimFile, "utf-8"));
|
|
10095
10232
|
} catch {
|
|
10096
|
-
console.error(`${
|
|
10233
|
+
console.error(`${TAG28} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
10097
10234
|
return { ok: false, status: 400, message: "Authorization state could not be read.", stateValid: false };
|
|
10098
10235
|
}
|
|
10099
10236
|
if (Date.now() > pending.expiresAt) {
|
|
10100
|
-
console.error(`${
|
|
10237
|
+
console.error(`${TAG28} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
10101
10238
|
return { ok: false, status: 400, message: "Authorization request expired. Generate a new consent link.", stateValid: false };
|
|
10102
10239
|
}
|
|
10103
|
-
console.error(`${
|
|
10240
|
+
console.error(`${TAG28} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
|
|
10104
10241
|
if (!code || !realmId) {
|
|
10105
10242
|
return { ok: false, status: 400, message: "Missing code or realmId in the callback.", stateValid: true };
|
|
10106
10243
|
}
|
|
10107
10244
|
if (!existsSync12(storePath(accountDir))) {
|
|
10108
10245
|
return { ok: false, status: 500, message: "No QuickBooks credentials are stored for this account.", stateValid: true };
|
|
10109
10246
|
}
|
|
10110
|
-
const store2 = JSON.parse(
|
|
10247
|
+
const store2 = JSON.parse(readFileSync20(storePath(accountDir), "utf-8"));
|
|
10111
10248
|
const form = new URLSearchParams({
|
|
10112
10249
|
grant_type: "authorization_code",
|
|
10113
10250
|
code,
|
|
@@ -10124,12 +10261,12 @@ async function completeConsent(args) {
|
|
|
10124
10261
|
});
|
|
10125
10262
|
const text = await res.text();
|
|
10126
10263
|
if (!res.ok) {
|
|
10127
|
-
console.error(`${
|
|
10264
|
+
console.error(`${TAG28} op=token-exchange realmId=${realmId} persisted=false`);
|
|
10128
10265
|
return { ok: false, status: 502, message: `Token exchange failed (${res.status}). Generate a new consent link to retry.`, stateValid: true };
|
|
10129
10266
|
}
|
|
10130
10267
|
const body = JSON.parse(text);
|
|
10131
10268
|
if (!body.refresh_token) {
|
|
10132
|
-
console.error(`${
|
|
10269
|
+
console.error(`${TAG28} op=token-exchange realmId=${realmId} persisted=false`);
|
|
10133
10270
|
return { ok: false, status: 502, message: "Token exchange returned no refresh token.", stateValid: true };
|
|
10134
10271
|
}
|
|
10135
10272
|
const now = Date.now();
|
|
@@ -10139,10 +10276,10 @@ async function completeConsent(args) {
|
|
|
10139
10276
|
refreshTokenExpiry: now + (body.x_refresh_token_expires_in ?? 864e4) * 1e3,
|
|
10140
10277
|
lastRefresh: now
|
|
10141
10278
|
};
|
|
10142
|
-
mkdirSync3(
|
|
10279
|
+
mkdirSync3(join19(accountDir, "secrets"), { recursive: true });
|
|
10143
10280
|
writeFileSync8(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
|
|
10144
|
-
const persisted = JSON.parse(
|
|
10145
|
-
console.error(`${
|
|
10281
|
+
const persisted = JSON.parse(readFileSync20(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
|
|
10282
|
+
console.error(`${TAG28} op=token-exchange realmId=${realmId} persisted=${persisted}`);
|
|
10146
10283
|
return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
|
|
10147
10284
|
} finally {
|
|
10148
10285
|
rmSync(claimFile, { force: true });
|
|
@@ -10155,7 +10292,7 @@ var app8 = new Hono();
|
|
|
10155
10292
|
app8.get("/callback", async (c) => {
|
|
10156
10293
|
const account = resolveAccount();
|
|
10157
10294
|
if (!account) {
|
|
10158
|
-
console.error(`${
|
|
10295
|
+
console.error(`${TAG28} op=callback stateValid=false realmId= reason=no-account`);
|
|
10159
10296
|
return c.html(page("QuickBooks", "This install has no account configured."), 500);
|
|
10160
10297
|
}
|
|
10161
10298
|
const result = await completeConsent({
|
|
@@ -10172,12 +10309,12 @@ var quickbooks_default = app8;
|
|
|
10172
10309
|
|
|
10173
10310
|
// server/routes/onboarding.ts
|
|
10174
10311
|
import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
|
|
10175
|
-
import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync14, readFileSync as
|
|
10312
|
+
import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync14, readFileSync as readFileSync22, unlinkSync as unlinkSync2 } from "fs";
|
|
10176
10313
|
import { createHash as createHash3, randomUUID as randomUUID8 } from "crypto";
|
|
10177
10314
|
|
|
10178
10315
|
// ../lib/admins-write/src/index.ts
|
|
10179
|
-
import { existsSync as existsSync13, readFileSync as
|
|
10180
|
-
import { dirname as dirname4, join as
|
|
10316
|
+
import { existsSync as existsSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync9, renameSync as renameSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync12, statSync as statSync7 } from "fs";
|
|
10317
|
+
import { dirname as dirname4, join as join20 } from "path";
|
|
10181
10318
|
function logLine(input, result) {
|
|
10182
10319
|
const userIdShort = input.userId.slice(0, 8);
|
|
10183
10320
|
console.error(
|
|
@@ -10195,7 +10332,7 @@ function writeAdminEntry(input) {
|
|
|
10195
10332
|
try {
|
|
10196
10333
|
let users = [];
|
|
10197
10334
|
if (existsSync13(input.usersFile)) {
|
|
10198
|
-
const raw =
|
|
10335
|
+
const raw = readFileSync21(input.usersFile, "utf-8").trim();
|
|
10199
10336
|
if (raw) {
|
|
10200
10337
|
const parsed = JSON.parse(raw);
|
|
10201
10338
|
if (!Array.isArray(parsed)) {
|
|
@@ -10219,11 +10356,11 @@ function writeAdminEntry(input) {
|
|
|
10219
10356
|
return result;
|
|
10220
10357
|
}
|
|
10221
10358
|
try {
|
|
10222
|
-
const accountJsonPath =
|
|
10359
|
+
const accountJsonPath = join20(input.accountDir, "account.json");
|
|
10223
10360
|
if (!existsSync13(accountJsonPath)) {
|
|
10224
10361
|
throw new Error(`account.json not found at ${accountJsonPath}`);
|
|
10225
10362
|
}
|
|
10226
|
-
const config = JSON.parse(
|
|
10363
|
+
const config = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
|
|
10227
10364
|
const admins = config.admins ?? [];
|
|
10228
10365
|
if (admins.some((a) => a.userId === input.userId)) {
|
|
10229
10366
|
result.accountJsonResult = "noop";
|
|
@@ -10249,7 +10386,7 @@ function checkAdminAuthInvariant(input) {
|
|
|
10249
10386
|
const usersUserIds = /* @__PURE__ */ new Set();
|
|
10250
10387
|
if (existsSync13(input.usersFile)) {
|
|
10251
10388
|
try {
|
|
10252
|
-
const raw =
|
|
10389
|
+
const raw = readFileSync21(input.usersFile, "utf-8").trim();
|
|
10253
10390
|
if (raw) {
|
|
10254
10391
|
const users = JSON.parse(raw);
|
|
10255
10392
|
for (const u of users) {
|
|
@@ -10274,17 +10411,17 @@ function checkAdminAuthInvariant(input) {
|
|
|
10274
10411
|
}
|
|
10275
10412
|
for (const entry of entries) {
|
|
10276
10413
|
if (entry.startsWith(".")) continue;
|
|
10277
|
-
const accountDir =
|
|
10414
|
+
const accountDir = join20(input.accountsDir, entry);
|
|
10278
10415
|
try {
|
|
10279
10416
|
if (!statSync7(accountDir).isDirectory()) continue;
|
|
10280
10417
|
} catch {
|
|
10281
10418
|
continue;
|
|
10282
10419
|
}
|
|
10283
|
-
const accountJsonPath =
|
|
10420
|
+
const accountJsonPath = join20(accountDir, "account.json");
|
|
10284
10421
|
if (!existsSync13(accountJsonPath)) continue;
|
|
10285
10422
|
let admins = [];
|
|
10286
10423
|
try {
|
|
10287
|
-
const config = JSON.parse(
|
|
10424
|
+
const config = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
|
|
10288
10425
|
admins = config.admins ?? [];
|
|
10289
10426
|
} catch (err) {
|
|
10290
10427
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -10325,7 +10462,7 @@ function hashPin2(pin) {
|
|
|
10325
10462
|
}
|
|
10326
10463
|
function readUsersFile2() {
|
|
10327
10464
|
if (!existsSync14(USERS_FILE)) return null;
|
|
10328
|
-
const raw =
|
|
10465
|
+
const raw = readFileSync22(USERS_FILE, "utf-8").trim();
|
|
10329
10466
|
if (!raw) return [];
|
|
10330
10467
|
return JSON.parse(raw);
|
|
10331
10468
|
}
|
|
@@ -10487,7 +10624,7 @@ app9.post("/set-pin", async (c) => {
|
|
|
10487
10624
|
let installOwner = null;
|
|
10488
10625
|
if (existsSync14(INSTALL_OWNER_FILE)) {
|
|
10489
10626
|
try {
|
|
10490
|
-
const raw =
|
|
10627
|
+
const raw = readFileSync22(INSTALL_OWNER_FILE, "utf-8").trim();
|
|
10491
10628
|
if (raw.length > 0) installOwner = raw;
|
|
10492
10629
|
} catch (err) {
|
|
10493
10630
|
console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -10569,8 +10706,8 @@ var onboarding_default = app9;
|
|
|
10569
10706
|
|
|
10570
10707
|
// server/routes/client-error.ts
|
|
10571
10708
|
import { appendFileSync, existsSync as existsSync15, renameSync as renameSync6, statSync as statSync8 } from "fs";
|
|
10572
|
-
import { join as
|
|
10573
|
-
var CLIENT_ERRORS_LOG =
|
|
10709
|
+
import { join as join21 } from "path";
|
|
10710
|
+
var CLIENT_ERRORS_LOG = join21(LOG_DIR, "client-errors.log");
|
|
10574
10711
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
10575
10712
|
var MAX_BODY_SIZE = 8 * 1024;
|
|
10576
10713
|
var MAX_STACK_LEN = 2e3;
|
|
@@ -10895,17 +11032,17 @@ app11.post("/", async (c) => {
|
|
|
10895
11032
|
var session_default = app11;
|
|
10896
11033
|
|
|
10897
11034
|
// server/routes/admin/logs.ts
|
|
10898
|
-
import { existsSync as existsSync17, readdirSync as readdirSync13, readFileSync as
|
|
11035
|
+
import { existsSync as existsSync17, readdirSync as readdirSync13, readFileSync as readFileSync23, statSync as statSync9 } from "fs";
|
|
10899
11036
|
import { resolve as resolve18, basename as basename6 } from "path";
|
|
10900
11037
|
|
|
10901
11038
|
// app/lib/logs-read-resolve.ts
|
|
10902
11039
|
import { existsSync as existsSync16 } from "fs";
|
|
10903
|
-
import { join as
|
|
11040
|
+
import { join as join22 } from "path";
|
|
10904
11041
|
function resolveSessionLogPaths(filename, logDirs) {
|
|
10905
11042
|
const tried = [filename];
|
|
10906
11043
|
const hits = [];
|
|
10907
11044
|
for (const dir of logDirs) {
|
|
10908
|
-
const fullPath =
|
|
11045
|
+
const fullPath = join22(dir, filename);
|
|
10909
11046
|
if (existsSync16(fullPath)) {
|
|
10910
11047
|
hits.push({ path: fullPath, dir });
|
|
10911
11048
|
}
|
|
@@ -10934,7 +11071,7 @@ app12.get("/", async (c) => {
|
|
|
10934
11071
|
const filePath = resolve18(dir, safe);
|
|
10935
11072
|
searched.push(filePath);
|
|
10936
11073
|
try {
|
|
10937
|
-
const buffer =
|
|
11074
|
+
const buffer = readFileSync23(filePath);
|
|
10938
11075
|
const onDiskBytes = statSync9(filePath).size;
|
|
10939
11076
|
const headers = {
|
|
10940
11077
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -10999,7 +11136,7 @@ app12.get("/", async (c) => {
|
|
|
10999
11136
|
console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
|
|
11000
11137
|
try {
|
|
11001
11138
|
const filename = basename6(hit.path);
|
|
11002
|
-
const buffer =
|
|
11139
|
+
const buffer = readFileSync23(hit.path);
|
|
11003
11140
|
const onDiskBytes = statSync9(hit.path).size;
|
|
11004
11141
|
const headers = {
|
|
11005
11142
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -11046,7 +11183,7 @@ app12.get("/", async (c) => {
|
|
|
11046
11183
|
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve18(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
11047
11184
|
seen.add(name);
|
|
11048
11185
|
try {
|
|
11049
|
-
const content =
|
|
11186
|
+
const content = readFileSync23(resolve18(dir, name));
|
|
11050
11187
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
11051
11188
|
logs[name] = tail.trim() || "(empty)";
|
|
11052
11189
|
} catch (err) {
|
|
@@ -11131,7 +11268,7 @@ var attachment_default = app14;
|
|
|
11131
11268
|
|
|
11132
11269
|
// server/routes/admin/agents.ts
|
|
11133
11270
|
import { resolve as resolve20 } from "path";
|
|
11134
|
-
import { readdirSync as readdirSync14, readFileSync as
|
|
11271
|
+
import { readdirSync as readdirSync14, readFileSync as readFileSync24, existsSync as existsSync19, rmSync as rmSync2 } from "fs";
|
|
11135
11272
|
var app15 = new Hono();
|
|
11136
11273
|
app15.get("/", (c) => {
|
|
11137
11274
|
const account = resolveAccount();
|
|
@@ -11147,7 +11284,7 @@ app15.get("/", (c) => {
|
|
|
11147
11284
|
const configPath2 = resolve20(agentsDir, entry.name, "config.json");
|
|
11148
11285
|
if (!existsSync19(configPath2)) continue;
|
|
11149
11286
|
try {
|
|
11150
|
-
const config = JSON.parse(
|
|
11287
|
+
const config = JSON.parse(readFileSync24(configPath2, "utf-8"));
|
|
11151
11288
|
agents.push({
|
|
11152
11289
|
slug: entry.name,
|
|
11153
11290
|
displayName: config.displayName ?? entry.name,
|
|
@@ -11906,8 +12043,8 @@ app16.put("/:id/label", requireAdminSession, async (c) => {
|
|
|
11906
12043
|
var sessions_default = app16;
|
|
11907
12044
|
|
|
11908
12045
|
// app/lib/claude-agent/spawn-context.ts
|
|
11909
|
-
import { existsSync as existsSync21, readFileSync as
|
|
11910
|
-
import { dirname as dirname5, resolve as resolve21, join as
|
|
12046
|
+
import { existsSync as existsSync21, readFileSync as readFileSync25, readdirSync as readdirSync15, statSync as statSync10 } from "fs";
|
|
12047
|
+
import { dirname as dirname5, resolve as resolve21, join as join23 } from "path";
|
|
11911
12048
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
11912
12049
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
11913
12050
|
try {
|
|
@@ -11948,14 +12085,14 @@ function frontmatterField(manifest, field2) {
|
|
|
11948
12085
|
function extractPluginToolDescriptions(pluginDir) {
|
|
11949
12086
|
const out = /* @__PURE__ */ new Map();
|
|
11950
12087
|
const candidates = [
|
|
11951
|
-
|
|
11952
|
-
|
|
12088
|
+
join23(pluginDir, "mcp/dist/index.js"),
|
|
12089
|
+
join23(pluginDir, "mcp/src/index.ts")
|
|
11953
12090
|
];
|
|
11954
12091
|
let raw = null;
|
|
11955
12092
|
for (const path2 of candidates) {
|
|
11956
12093
|
if (!existsSync21(path2)) continue;
|
|
11957
12094
|
try {
|
|
11958
|
-
raw =
|
|
12095
|
+
raw = readFileSync25(path2, "utf-8");
|
|
11959
12096
|
break;
|
|
11960
12097
|
} catch {
|
|
11961
12098
|
}
|
|
@@ -12007,7 +12144,7 @@ function listPluginDirs() {
|
|
|
12007
12144
|
const platformPlugins = resolve21(PLATFORM_ROOT, "plugins");
|
|
12008
12145
|
if (existsSync21(platformPlugins)) {
|
|
12009
12146
|
for (const name of readdirSync15(platformPlugins)) {
|
|
12010
|
-
const dir =
|
|
12147
|
+
const dir = join23(platformPlugins, name);
|
|
12011
12148
|
try {
|
|
12012
12149
|
if (statSync10(dir).isDirectory()) out.set(name, dir);
|
|
12013
12150
|
} catch {
|
|
@@ -12017,11 +12154,11 @@ function listPluginDirs() {
|
|
|
12017
12154
|
const premiumRoot = resolve21(dirname5(PLATFORM_ROOT), "premium-plugins");
|
|
12018
12155
|
if (existsSync21(premiumRoot)) {
|
|
12019
12156
|
for (const bundle of readdirSync15(premiumRoot)) {
|
|
12020
|
-
const bundlePlugins =
|
|
12157
|
+
const bundlePlugins = join23(premiumRoot, bundle, "plugins");
|
|
12021
12158
|
if (!existsSync21(bundlePlugins)) continue;
|
|
12022
12159
|
try {
|
|
12023
12160
|
for (const name of readdirSync15(bundlePlugins)) {
|
|
12024
|
-
const dir =
|
|
12161
|
+
const dir = join23(bundlePlugins, name);
|
|
12025
12162
|
try {
|
|
12026
12163
|
if (statSync10(dir).isDirectory()) out.set(name, dir);
|
|
12027
12164
|
} catch {
|
|
@@ -12037,7 +12174,7 @@ function readEnabledPlugins(accountId) {
|
|
|
12037
12174
|
const accountFile = resolve21(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
12038
12175
|
if (!existsSync21(accountFile)) return /* @__PURE__ */ new Set();
|
|
12039
12176
|
try {
|
|
12040
|
-
const parsed = JSON.parse(
|
|
12177
|
+
const parsed = JSON.parse(readFileSync25(accountFile, "utf-8"));
|
|
12041
12178
|
return new Set(
|
|
12042
12179
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
12043
12180
|
);
|
|
@@ -12049,7 +12186,7 @@ function readFrontmatter(path2) {
|
|
|
12049
12186
|
if (!existsSync21(path2)) return null;
|
|
12050
12187
|
let raw;
|
|
12051
12188
|
try {
|
|
12052
|
-
raw =
|
|
12189
|
+
raw = readFileSync25(path2, "utf-8");
|
|
12053
12190
|
} catch {
|
|
12054
12191
|
return null;
|
|
12055
12192
|
}
|
|
@@ -12064,7 +12201,7 @@ function computeActivePlugins(accountId) {
|
|
|
12064
12201
|
for (const name of Array.from(enabled).sort()) {
|
|
12065
12202
|
const dir = pluginDirs.get(name);
|
|
12066
12203
|
if (!dir) continue;
|
|
12067
|
-
const fm = readFrontmatter(
|
|
12204
|
+
const fm = readFrontmatter(join23(dir, "PLUGIN.md"));
|
|
12068
12205
|
if (!fm) continue;
|
|
12069
12206
|
const description = frontmatterField(`---
|
|
12070
12207
|
${fm}
|
|
@@ -12081,7 +12218,7 @@ ${fm}
|
|
|
12081
12218
|
`plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
|
|
12082
12219
|
);
|
|
12083
12220
|
if (toolNames.length > 0 && descMap.size === 0) {
|
|
12084
|
-
const hasSource = existsSync21(
|
|
12221
|
+
const hasSource = existsSync21(join23(dir, "mcp/dist/index.js")) || existsSync21(join23(dir, "mcp/src/index.ts"));
|
|
12085
12222
|
if (hasSource) {
|
|
12086
12223
|
console.warn(
|
|
12087
12224
|
`[spawn-context] WARN plugin=${name} mcp source present but tool-description regex matched zero entries \u2014 SDK upgrade may have changed the registration shape`
|
|
@@ -12091,7 +12228,7 @@ ${fm}
|
|
|
12091
12228
|
const skillPaths = parseSkillPathsFromFrontmatter(fm);
|
|
12092
12229
|
const skills = [];
|
|
12093
12230
|
for (const relPath of skillPaths) {
|
|
12094
|
-
const skillPath =
|
|
12231
|
+
const skillPath = join23(dir, relPath);
|
|
12095
12232
|
const skillFm = readFrontmatter(skillPath);
|
|
12096
12233
|
if (!skillFm) continue;
|
|
12097
12234
|
const skillName = frontmatterField(`---
|
|
@@ -12135,7 +12272,7 @@ function computeSpecialistDomains(accountId) {
|
|
|
12135
12272
|
const out = [];
|
|
12136
12273
|
for (const file of entries.sort()) {
|
|
12137
12274
|
if (!file.endsWith(".md")) continue;
|
|
12138
|
-
const fm = readFrontmatter(
|
|
12275
|
+
const fm = readFrontmatter(join23(specialistsDir, file));
|
|
12139
12276
|
if (!fm) continue;
|
|
12140
12277
|
const wrapped = `---
|
|
12141
12278
|
${fm}
|
|
@@ -12157,7 +12294,7 @@ function computeDormantPlugins(accountId) {
|
|
|
12157
12294
|
if (!existsSync21(accountFile)) return [];
|
|
12158
12295
|
let enabled;
|
|
12159
12296
|
try {
|
|
12160
|
-
const raw =
|
|
12297
|
+
const raw = readFileSync25(accountFile, "utf-8");
|
|
12161
12298
|
const parsed = JSON.parse(raw);
|
|
12162
12299
|
enabled = new Set(
|
|
12163
12300
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
@@ -12183,7 +12320,7 @@ function computeDormantPlugins(accountId) {
|
|
|
12183
12320
|
if (!existsSync21(manifestPath)) continue;
|
|
12184
12321
|
let manifest;
|
|
12185
12322
|
try {
|
|
12186
|
-
manifest =
|
|
12323
|
+
manifest = readFileSync25(manifestPath, "utf-8");
|
|
12187
12324
|
} catch {
|
|
12188
12325
|
continue;
|
|
12189
12326
|
}
|
|
@@ -12197,13 +12334,13 @@ function computeDormantPlugins(accountId) {
|
|
|
12197
12334
|
}
|
|
12198
12335
|
|
|
12199
12336
|
// server/routes/admin/claude-sessions.ts
|
|
12200
|
-
import { existsSync as existsSync22, readFileSync as
|
|
12337
|
+
import { existsSync as existsSync22, readFileSync as readFileSync26 } from "fs";
|
|
12201
12338
|
import { resolve as resolve22 } from "path";
|
|
12202
12339
|
function readTunnelState(brandConfigDir) {
|
|
12203
12340
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
12204
12341
|
if (!existsSync22(statePath)) return null;
|
|
12205
12342
|
try {
|
|
12206
|
-
const parsed = JSON.parse(
|
|
12343
|
+
const parsed = JSON.parse(readFileSync26(statePath, "utf-8"));
|
|
12207
12344
|
const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
|
|
12208
12345
|
const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
|
|
12209
12346
|
const domain = typeof parsed.domain === "string" ? parsed.domain : null;
|
|
@@ -12217,7 +12354,7 @@ function resolveTunnelUrl() {
|
|
|
12217
12354
|
const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
|
|
12218
12355
|
let brand;
|
|
12219
12356
|
try {
|
|
12220
|
-
brand = JSON.parse(
|
|
12357
|
+
brand = JSON.parse(readFileSync26(resolve22(platformRoot4, "config", "brand.json"), "utf-8"));
|
|
12221
12358
|
} catch {
|
|
12222
12359
|
return null;
|
|
12223
12360
|
}
|
|
@@ -12228,7 +12365,7 @@ function resolveTunnelUrl() {
|
|
|
12228
12365
|
if (!state) return null;
|
|
12229
12366
|
return `https://${hostname2}.${state.domain}`;
|
|
12230
12367
|
}
|
|
12231
|
-
var
|
|
12368
|
+
var TAG29 = "[claude-session-manager:wrapper]";
|
|
12232
12369
|
async function refuseIfClaudeAuthDead(c, route, sessionId) {
|
|
12233
12370
|
const auth = await ensureAuth();
|
|
12234
12371
|
if (auth.status !== "dead" && auth.status !== "missing") return null;
|
|
@@ -12246,12 +12383,12 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
12246
12383
|
const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
|
|
12247
12384
|
const ownerMs = Date.now() - ownerStart;
|
|
12248
12385
|
const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
|
|
12249
|
-
console.log(`${
|
|
12386
|
+
console.log(`${TAG29} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
|
|
12250
12387
|
const dormantPlugins = computeDormantPlugins(args.senderId);
|
|
12251
12388
|
const activePlugins = computeActivePlugins(args.senderId);
|
|
12252
12389
|
const specialistDomains = computeSpecialistDomains(args.senderId);
|
|
12253
12390
|
const tunnelUrl = resolveTunnelUrl();
|
|
12254
|
-
console.log(`${
|
|
12391
|
+
console.log(`${TAG29} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
|
|
12255
12392
|
const upstreamPayload = JSON.stringify({
|
|
12256
12393
|
senderId: args.senderId,
|
|
12257
12394
|
// Task 205 — pass userId through to the manager so it lands as
|
|
@@ -12278,24 +12415,24 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
12278
12415
|
// unshapely values.
|
|
12279
12416
|
conversationNodeId: args.conversationNodeId
|
|
12280
12417
|
});
|
|
12281
|
-
console.log(`${
|
|
12418
|
+
console.log(`${TAG29} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
|
|
12282
12419
|
const forwardStart = Date.now();
|
|
12283
12420
|
const upstream = await fetch(`${managerBase("claude-session-manager:wrapper")}/public-spawn`, {
|
|
12284
12421
|
method: "POST",
|
|
12285
12422
|
headers: { "content-type": "application/json" },
|
|
12286
12423
|
body: upstreamPayload
|
|
12287
12424
|
}).catch((err) => {
|
|
12288
|
-
console.error(`${
|
|
12425
|
+
console.error(`${TAG29} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
|
|
12289
12426
|
return null;
|
|
12290
12427
|
});
|
|
12291
12428
|
if (!upstream) return {
|
|
12292
12429
|
response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
|
|
12293
12430
|
claudeSessionId: null
|
|
12294
12431
|
};
|
|
12295
|
-
console.log(`${
|
|
12432
|
+
console.log(`${TAG29} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
|
|
12296
12433
|
if (args.initialMessage) {
|
|
12297
12434
|
const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
|
|
12298
|
-
console.log(`${
|
|
12435
|
+
console.log(`${TAG29} initial-message-inlined bytes=${inputBytes}`);
|
|
12299
12436
|
}
|
|
12300
12437
|
const bodyText = await upstream.text().catch(() => "");
|
|
12301
12438
|
let claudeSessionId = null;
|
|
@@ -12330,7 +12467,7 @@ app17.post("/", async (c) => {
|
|
|
12330
12467
|
if (refusal) return refusal;
|
|
12331
12468
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
12332
12469
|
if (!senderId) {
|
|
12333
|
-
console.error(`${
|
|
12470
|
+
console.error(`${TAG29} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
|
|
12334
12471
|
return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
12335
12472
|
}
|
|
12336
12473
|
const userId = getUserIdForSession(cacheKey) ?? void 0;
|
|
@@ -12339,7 +12476,7 @@ app17.post("/", async (c) => {
|
|
|
12339
12476
|
const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
|
|
12340
12477
|
const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
|
|
12341
12478
|
const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
|
|
12342
|
-
console.log(`${
|
|
12479
|
+
console.log(`${TAG29} 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"}`);
|
|
12343
12480
|
const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
|
|
12344
12481
|
const { response, claudeSessionId } = await performSpawnWithInitialMessage({
|
|
12345
12482
|
senderId,
|
|
@@ -12358,13 +12495,13 @@ app17.post("/", async (c) => {
|
|
|
12358
12495
|
claudeSessionId,
|
|
12359
12496
|
senderId
|
|
12360
12497
|
);
|
|
12361
|
-
console.log(`${
|
|
12498
|
+
console.log(`${TAG29} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
|
|
12362
12499
|
return response;
|
|
12363
12500
|
});
|
|
12364
12501
|
var claude_sessions_default = app17;
|
|
12365
12502
|
|
|
12366
12503
|
// server/routes/admin/log-ingest.ts
|
|
12367
|
-
var
|
|
12504
|
+
var TAG30 = "[log-ingest]";
|
|
12368
12505
|
var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
|
|
12369
12506
|
var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
12370
12507
|
var MAX_LINE_BYTES = 4096;
|
|
@@ -12375,7 +12512,7 @@ function isLoopbackAddr(addr) {
|
|
|
12375
12512
|
app18.post("/", async (c) => {
|
|
12376
12513
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
12377
12514
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
12378
|
-
console.error(`${
|
|
12515
|
+
console.error(`${TAG30} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
12379
12516
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
12380
12517
|
}
|
|
12381
12518
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -12384,7 +12521,7 @@ app18.post("/", async (c) => {
|
|
|
12384
12521
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
12385
12522
|
const offender = tokens.find((t) => !isLoopbackAddr(t));
|
|
12386
12523
|
if (offender !== void 0) {
|
|
12387
|
-
console.error(`${
|
|
12524
|
+
console.error(`${TAG30} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
12388
12525
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
12389
12526
|
}
|
|
12390
12527
|
}
|
|
@@ -12426,18 +12563,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
12426
12563
|
]);
|
|
12427
12564
|
var app19 = new Hono();
|
|
12428
12565
|
app19.post("/", async (c) => {
|
|
12429
|
-
const
|
|
12566
|
+
const TAG39 = "[admin:events]";
|
|
12430
12567
|
let body;
|
|
12431
12568
|
try {
|
|
12432
12569
|
body = await c.req.json();
|
|
12433
12570
|
} catch (err) {
|
|
12434
12571
|
const detail = err instanceof Error ? err.message : String(err);
|
|
12435
|
-
console.error(`${
|
|
12572
|
+
console.error(`${TAG39} reject reason=body-not-json detail=${detail}`);
|
|
12436
12573
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
12437
12574
|
}
|
|
12438
12575
|
const event = typeof body.event === "string" ? body.event : "";
|
|
12439
12576
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
12440
|
-
console.error(`${
|
|
12577
|
+
console.error(`${TAG39} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
12441
12578
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
12442
12579
|
}
|
|
12443
12580
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -12463,7 +12600,7 @@ import { createReadStream as createReadStream2, createWriteStream as createWrite
|
|
|
12463
12600
|
import { readdir as readdir3, readFile as readFile5, stat as stat5, mkdir as mkdir3, unlink as unlink2, rename } from "fs/promises";
|
|
12464
12601
|
import { realpathSync as realpathSync6 } from "fs";
|
|
12465
12602
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
12466
|
-
import { basename as basename8, dirname as dirname6, join as
|
|
12603
|
+
import { basename as basename8, dirname as dirname6, join as join25, resolve as resolve24, sep as sep6 } from "path";
|
|
12467
12604
|
import { Readable as Readable2 } from "stream";
|
|
12468
12605
|
|
|
12469
12606
|
// ../lib/graph-trash/src/index.ts
|
|
@@ -12781,7 +12918,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
12781
12918
|
|
|
12782
12919
|
// app/lib/file-index.ts
|
|
12783
12920
|
import * as fsp from "fs/promises";
|
|
12784
|
-
import { resolve as resolve23, relative as relative3, join as
|
|
12921
|
+
import { resolve as resolve23, relative as relative3, join as join24, basename as basename7, extname as extname2, sep as sep5 } from "path";
|
|
12785
12922
|
import { tmpdir as tmpdir2 } from "os";
|
|
12786
12923
|
import { execFile as execFile2 } from "child_process";
|
|
12787
12924
|
import { promisify as promisify2 } from "util";
|
|
@@ -12863,7 +13000,7 @@ async function extractPdf(absolute) {
|
|
|
12863
13000
|
return stdout.trim();
|
|
12864
13001
|
}
|
|
12865
13002
|
async function ocrPdf(absolute) {
|
|
12866
|
-
const outPdf =
|
|
13003
|
+
const outPdf = join24(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
|
|
12867
13004
|
try {
|
|
12868
13005
|
await execFileAsync2(
|
|
12869
13006
|
"ocrmypdf",
|
|
@@ -12925,7 +13062,7 @@ async function readDisplayName(absolute) {
|
|
|
12925
13062
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
12926
13063
|
if (base === `${stem}.meta.json`) return null;
|
|
12927
13064
|
try {
|
|
12928
|
-
const raw = await fsp.readFile(
|
|
13065
|
+
const raw = await fsp.readFile(join24(dir, `${stem}.meta.json`), "utf-8");
|
|
12929
13066
|
const meta = JSON.parse(raw);
|
|
12930
13067
|
const dn = meta.displayName ?? meta.filename;
|
|
12931
13068
|
return typeof dn === "string" && dn.length > 0 ? dn : null;
|
|
@@ -12946,7 +13083,7 @@ async function walkSubtree(root, dataRoot, out) {
|
|
|
12946
13083
|
for (const entry of entries) {
|
|
12947
13084
|
if (entry.isSymbolicLink()) continue;
|
|
12948
13085
|
if (entry.isDirectory() && entry.name === ".uploads-tmp") continue;
|
|
12949
|
-
const abs =
|
|
13086
|
+
const abs = join24(root, entry.name);
|
|
12950
13087
|
if (entry.isDirectory()) {
|
|
12951
13088
|
const sub = await walkSubtree(abs, dataRoot, out);
|
|
12952
13089
|
if (!sub.clean) clean = false;
|
|
@@ -13219,7 +13356,7 @@ var UPLOAD_TMP_DIR = ".uploads-tmp";
|
|
|
13219
13356
|
var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
13220
13357
|
async function readMeta(absDir, baseName) {
|
|
13221
13358
|
try {
|
|
13222
|
-
const raw = await readFile5(
|
|
13359
|
+
const raw = await readFile5(join25(absDir, `${baseName}.meta.json`), "utf8");
|
|
13223
13360
|
const parsed = JSON.parse(raw);
|
|
13224
13361
|
if (typeof parsed?.filename === "string") {
|
|
13225
13362
|
return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
|
|
@@ -13257,7 +13394,7 @@ async function readAccountNames() {
|
|
|
13257
13394
|
}
|
|
13258
13395
|
async function enrich(absolute, entry, accountNames) {
|
|
13259
13396
|
if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
|
|
13260
|
-
const meta = await readMeta(
|
|
13397
|
+
const meta = await readMeta(join25(absolute, entry.name), entry.name);
|
|
13261
13398
|
if (meta?.filename) {
|
|
13262
13399
|
entry.displayName = meta.filename;
|
|
13263
13400
|
entry.mimeType = meta.mimeType;
|
|
@@ -13421,7 +13558,7 @@ app20.get("/", requireAdminSession, async (c) => {
|
|
|
13421
13558
|
const childRel = relPath === "" || relPath === "." ? name : `${relPath}/${name}`;
|
|
13422
13559
|
const protectedEntry = isProtectedFromDeletion(childRel).protected;
|
|
13423
13560
|
try {
|
|
13424
|
-
const entryPath =
|
|
13561
|
+
const entryPath = join25(absolute, name);
|
|
13425
13562
|
const s = await stat5(entryPath);
|
|
13426
13563
|
entries.push({
|
|
13427
13564
|
name,
|
|
@@ -13562,7 +13699,7 @@ app20.post("/upload", requireAdminSession, async (c) => {
|
|
|
13562
13699
|
const base = resolveOwnAccountWrite(rawDir, accountId, "POST /api/admin/files/upload");
|
|
13563
13700
|
if (!base.ok) return c.json({ error: base.error }, base.status);
|
|
13564
13701
|
const relDir = relpath ? dirname6(relpath) : ".";
|
|
13565
|
-
const destRel = relDir === "." ? base.relative :
|
|
13702
|
+
const destRel = relDir === "." ? base.relative : join25(base.relative, relDir);
|
|
13566
13703
|
if (crossesForeignAccountPartition(destRel, accountId)) {
|
|
13567
13704
|
console.error(`[data] account-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
|
|
13568
13705
|
return c.json({ error: "Not found" }, 404);
|
|
@@ -13739,7 +13876,7 @@ app20.delete("/", requireAdminSession, async (c) => {
|
|
|
13739
13876
|
}
|
|
13740
13877
|
const dot = base.lastIndexOf(".");
|
|
13741
13878
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
13742
|
-
const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ?
|
|
13879
|
+
const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join25(dirname6(absolute), `${stem}.meta.json`) : null;
|
|
13743
13880
|
await unlink2(absolute);
|
|
13744
13881
|
if (sidecarPath) {
|
|
13745
13882
|
try {
|
|
@@ -16215,6 +16352,7 @@ var session_rename_default = app31;
|
|
|
16215
16352
|
|
|
16216
16353
|
// server/routes/admin/session-rc-spawn.ts
|
|
16217
16354
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
16355
|
+
var ACCOUNT_UUID_RE2 = /^[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}$/;
|
|
16218
16356
|
function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID11, adminUserId, authenticatedName) {
|
|
16219
16357
|
const operator = isOperatorHost(host, operatorDomains);
|
|
16220
16358
|
const resumeId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : void 0;
|
|
@@ -16225,6 +16363,7 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID11, ad
|
|
|
16225
16363
|
const pickedModel = kind === "new" && typeof body.model === "string" && isSelectableModel(body.model) ? body.model : null;
|
|
16226
16364
|
const pickedEffort = kind === "new" && typeof body.effort === "string" && isNewSessionEffortLevel(body.effort) ? body.effort : null;
|
|
16227
16365
|
const paramsSource = pickedModel !== null && pickedEffort !== null ? "picker" : "default";
|
|
16366
|
+
const targetAccountId = typeof body.targetAccountId === "string" && ACCOUNT_UUID_RE2.test(body.targetAccountId) ? body.targetAccountId : void 0;
|
|
16228
16367
|
const payload = {};
|
|
16229
16368
|
let sessionId;
|
|
16230
16369
|
if (webchatBind) {
|
|
@@ -16239,6 +16378,7 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID11, ad
|
|
|
16239
16378
|
if (pickedModel) payload.model = pickedModel;
|
|
16240
16379
|
if (pickedEffort) payload.effort = pickedEffort;
|
|
16241
16380
|
if (adminUserId) payload.adminUserId = adminUserId;
|
|
16381
|
+
if (targetAccountId) payload.targetAccountId = targetAccountId;
|
|
16242
16382
|
if (authenticatedName) payload.authenticatedName = authenticatedName;
|
|
16243
16383
|
} else {
|
|
16244
16384
|
sessionId = resumeId;
|
|
@@ -16549,14 +16689,14 @@ app34.get("/", async (c) => {
|
|
|
16549
16689
|
var system_stats_default = app34;
|
|
16550
16690
|
|
|
16551
16691
|
// server/routes/admin/health.ts
|
|
16552
|
-
import { existsSync as existsSync25, readFileSync as
|
|
16553
|
-
import { resolve as resolve26, join as
|
|
16692
|
+
import { existsSync as existsSync25, readFileSync as readFileSync27 } from "fs";
|
|
16693
|
+
import { resolve as resolve26, join as join26 } from "path";
|
|
16554
16694
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
|
|
16555
16695
|
var brandHostname = "maxy";
|
|
16556
|
-
var brandJsonPath =
|
|
16696
|
+
var brandJsonPath = join26(PLATFORM_ROOT6, "config", "brand.json");
|
|
16557
16697
|
if (existsSync25(brandJsonPath)) {
|
|
16558
16698
|
try {
|
|
16559
|
-
const brand = JSON.parse(
|
|
16699
|
+
const brand = JSON.parse(readFileSync27(brandJsonPath, "utf-8"));
|
|
16560
16700
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
16561
16701
|
} catch {
|
|
16562
16702
|
}
|
|
@@ -16566,7 +16706,7 @@ var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
|
16566
16706
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
16567
16707
|
function readVersion() {
|
|
16568
16708
|
if (!existsSync25(VERSION_FILE)) return "unknown";
|
|
16569
|
-
return
|
|
16709
|
+
return readFileSync27(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
16570
16710
|
}
|
|
16571
16711
|
async function probeConversationDb() {
|
|
16572
16712
|
let session;
|
|
@@ -16618,7 +16758,7 @@ var health_default2 = app35;
|
|
|
16618
16758
|
|
|
16619
16759
|
// server/routes/admin/linkedin-ingest.ts
|
|
16620
16760
|
import { randomUUID as randomUUID13 } from "crypto";
|
|
16621
|
-
var
|
|
16761
|
+
var TAG31 = "[linkedin-ingest-route]";
|
|
16622
16762
|
var UUID2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
16623
16763
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
16624
16764
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
@@ -16722,29 +16862,29 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
16722
16862
|
try {
|
|
16723
16863
|
body = await c.req.json();
|
|
16724
16864
|
} catch {
|
|
16725
|
-
console.error(
|
|
16865
|
+
console.error(TAG31 + " rejected status=400 reason=schema:body-not-json");
|
|
16726
16866
|
return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
|
|
16727
16867
|
}
|
|
16728
16868
|
const v = validate(body);
|
|
16729
16869
|
if (!v.ok) {
|
|
16730
|
-
console.error(
|
|
16870
|
+
console.error(TAG31 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
|
|
16731
16871
|
return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
|
|
16732
16872
|
}
|
|
16733
16873
|
const envelope = v.envelope;
|
|
16734
16874
|
const cacheKey = c.var.cacheKey ?? "";
|
|
16735
16875
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
16736
16876
|
if (!senderId) {
|
|
16737
|
-
console.error(
|
|
16877
|
+
console.error(TAG31 + " rejected status=500 reason=admin-account-not-resolved");
|
|
16738
16878
|
return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
|
|
16739
16879
|
}
|
|
16740
16880
|
const payloadBytes = JSON.stringify(envelope).length;
|
|
16741
16881
|
console.log(
|
|
16742
|
-
|
|
16882
|
+
TAG31 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
|
|
16743
16883
|
);
|
|
16744
16884
|
const initialMessage = buildInitialMessage(envelope);
|
|
16745
16885
|
const spawnStart = Date.now();
|
|
16746
16886
|
const sessionId = randomUUID13();
|
|
16747
|
-
console.log(
|
|
16887
|
+
console.log(TAG31 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
|
|
16748
16888
|
const spawned = await managerRcSpawn({
|
|
16749
16889
|
sessionId,
|
|
16750
16890
|
initialMessage,
|
|
@@ -16753,12 +16893,12 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
16753
16893
|
});
|
|
16754
16894
|
if ("error" in spawned) {
|
|
16755
16895
|
console.error(
|
|
16756
|
-
|
|
16896
|
+
TAG31 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
|
|
16757
16897
|
);
|
|
16758
16898
|
return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
|
|
16759
16899
|
}
|
|
16760
16900
|
console.log(
|
|
16761
|
-
|
|
16901
|
+
TAG31 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
|
|
16762
16902
|
);
|
|
16763
16903
|
return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
|
|
16764
16904
|
});
|
|
@@ -16766,7 +16906,7 @@ var linkedin_ingest_default = app36;
|
|
|
16766
16906
|
|
|
16767
16907
|
// server/routes/admin/post-turn-context.ts
|
|
16768
16908
|
import neo4j3 from "neo4j-driver";
|
|
16769
|
-
var
|
|
16909
|
+
var TAG32 = "[post-turn-context]";
|
|
16770
16910
|
var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
|
|
16771
16911
|
"embedding",
|
|
16772
16912
|
"passwordHash",
|
|
@@ -16808,7 +16948,7 @@ var app37 = new Hono();
|
|
|
16808
16948
|
app37.get("/", async (c) => {
|
|
16809
16949
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
16810
16950
|
if (!isLoopbackAddr2(remoteAddr)) {
|
|
16811
|
-
console.error(`${
|
|
16951
|
+
console.error(`${TAG32} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
16812
16952
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
16813
16953
|
}
|
|
16814
16954
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -16817,7 +16957,7 @@ app37.get("/", async (c) => {
|
|
|
16817
16957
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
16818
16958
|
const offender = tokens.find((t) => !isLoopbackAddr2(t));
|
|
16819
16959
|
if (offender !== void 0) {
|
|
16820
|
-
console.error(`${
|
|
16960
|
+
console.error(`${TAG32} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
16821
16961
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
16822
16962
|
}
|
|
16823
16963
|
}
|
|
@@ -16849,7 +16989,7 @@ app37.get("/", async (c) => {
|
|
|
16849
16989
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
16850
16990
|
const total = Date.now() - started;
|
|
16851
16991
|
console.log(
|
|
16852
|
-
`${
|
|
16992
|
+
`${TAG32} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
|
|
16853
16993
|
);
|
|
16854
16994
|
return c.json({
|
|
16855
16995
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
@@ -16858,7 +16998,7 @@ app37.get("/", async (c) => {
|
|
|
16858
16998
|
const elapsed = Date.now() - started;
|
|
16859
16999
|
const message = err instanceof Error ? err.message : String(err);
|
|
16860
17000
|
console.error(
|
|
16861
|
-
`${
|
|
17001
|
+
`${TAG32} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
|
|
16862
17002
|
);
|
|
16863
17003
|
return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
|
|
16864
17004
|
} finally {
|
|
@@ -16971,7 +17111,7 @@ function formatPreviousContext(writes) {
|
|
|
16971
17111
|
}
|
|
16972
17112
|
|
|
16973
17113
|
// server/routes/admin/public-session-context.ts
|
|
16974
|
-
var
|
|
17114
|
+
var TAG33 = "[public-session-context]";
|
|
16975
17115
|
function isLoopbackAddr3(addr) {
|
|
16976
17116
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
16977
17117
|
}
|
|
@@ -16979,7 +17119,7 @@ var app38 = new Hono();
|
|
|
16979
17119
|
app38.get("/", async (c) => {
|
|
16980
17120
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
16981
17121
|
if (!isLoopbackAddr3(remoteAddr)) {
|
|
16982
|
-
console.error(`${
|
|
17122
|
+
console.error(`${TAG33} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
16983
17123
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
16984
17124
|
}
|
|
16985
17125
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -16988,7 +17128,7 @@ app38.get("/", async (c) => {
|
|
|
16988
17128
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
16989
17129
|
const offender = tokens.find((t) => !isLoopbackAddr3(t));
|
|
16990
17130
|
if (offender !== void 0) {
|
|
16991
|
-
console.error(`${
|
|
17131
|
+
console.error(`${TAG33} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
16992
17132
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
16993
17133
|
}
|
|
16994
17134
|
}
|
|
@@ -17002,14 +17142,14 @@ app38.get("/", async (c) => {
|
|
|
17002
17142
|
const writes = await fetchSliceWrites(session, sliceToken, accountId);
|
|
17003
17143
|
const total = Date.now() - started;
|
|
17004
17144
|
console.log(
|
|
17005
|
-
`${
|
|
17145
|
+
`${TAG33} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
|
|
17006
17146
|
);
|
|
17007
17147
|
return c.json({ writes });
|
|
17008
17148
|
} catch (err) {
|
|
17009
17149
|
const elapsed = Date.now() - started;
|
|
17010
17150
|
const message = err instanceof Error ? err.message : String(err);
|
|
17011
17151
|
console.error(
|
|
17012
|
-
`${
|
|
17152
|
+
`${TAG33} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
|
|
17013
17153
|
);
|
|
17014
17154
|
return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
|
|
17015
17155
|
} finally {
|
|
@@ -17019,7 +17159,7 @@ app38.get("/", async (c) => {
|
|
|
17019
17159
|
var public_session_context_default = app38;
|
|
17020
17160
|
|
|
17021
17161
|
// server/routes/admin/public-session-exit.ts
|
|
17022
|
-
var
|
|
17162
|
+
var TAG34 = "[public-session-exit-route]";
|
|
17023
17163
|
function isLoopbackAddr4(addr) {
|
|
17024
17164
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
17025
17165
|
}
|
|
@@ -17027,7 +17167,7 @@ var app39 = new Hono();
|
|
|
17027
17167
|
app39.post("/", async (c) => {
|
|
17028
17168
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17029
17169
|
if (!isLoopbackAddr4(remoteAddr)) {
|
|
17030
|
-
console.error(`${
|
|
17170
|
+
console.error(`${TAG34} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17031
17171
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
17032
17172
|
}
|
|
17033
17173
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17036,7 +17176,7 @@ app39.post("/", async (c) => {
|
|
|
17036
17176
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17037
17177
|
const offender = tokens.find((t) => !isLoopbackAddr4(t));
|
|
17038
17178
|
if (offender !== void 0) {
|
|
17039
|
-
console.error(`${
|
|
17179
|
+
console.error(`${TAG34} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17040
17180
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
17041
17181
|
}
|
|
17042
17182
|
}
|
|
@@ -17054,7 +17194,7 @@ app39.post("/", async (c) => {
|
|
|
17054
17194
|
var public_session_exit_default = app39;
|
|
17055
17195
|
|
|
17056
17196
|
// server/routes/admin/access-session-evict.ts
|
|
17057
|
-
var
|
|
17197
|
+
var TAG35 = "[access-session-evict]";
|
|
17058
17198
|
function isLoopbackAddr5(addr) {
|
|
17059
17199
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
17060
17200
|
}
|
|
@@ -17062,7 +17202,7 @@ var app40 = new Hono();
|
|
|
17062
17202
|
app40.post("/", async (c) => {
|
|
17063
17203
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17064
17204
|
if (!isLoopbackAddr5(remoteAddr)) {
|
|
17065
|
-
console.error(`${
|
|
17205
|
+
console.error(`${TAG35} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17066
17206
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
17067
17207
|
}
|
|
17068
17208
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17071,7 +17211,7 @@ app40.post("/", async (c) => {
|
|
|
17071
17211
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17072
17212
|
const offender = tokens.find((t) => !isLoopbackAddr5(t));
|
|
17073
17213
|
if (offender !== void 0) {
|
|
17074
|
-
console.error(`${
|
|
17214
|
+
console.error(`${TAG35} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17075
17215
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
17076
17216
|
}
|
|
17077
17217
|
}
|
|
@@ -17084,13 +17224,13 @@ app40.post("/", async (c) => {
|
|
|
17084
17224
|
const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
|
|
17085
17225
|
if (!grantId) return c.json({ error: "grantId required" }, 400);
|
|
17086
17226
|
const dropped = evictAccessSessionsByGrant(grantId);
|
|
17087
|
-
console.log(`${
|
|
17227
|
+
console.log(`${TAG35} grantId=${grantId} dropped=${dropped}`);
|
|
17088
17228
|
return c.json({ ok: true, dropped });
|
|
17089
17229
|
});
|
|
17090
17230
|
var access_session_evict_default = app40;
|
|
17091
17231
|
|
|
17092
17232
|
// server/routes/admin/enrol-person.ts
|
|
17093
|
-
var
|
|
17233
|
+
var TAG36 = "[enrol]";
|
|
17094
17234
|
function isLoopbackAddr6(addr) {
|
|
17095
17235
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
17096
17236
|
}
|
|
@@ -17099,7 +17239,7 @@ var app41 = new Hono();
|
|
|
17099
17239
|
app41.post("/", async (c) => {
|
|
17100
17240
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17101
17241
|
if (!isLoopbackAddr6(remoteAddr)) {
|
|
17102
|
-
console.error(`${
|
|
17242
|
+
console.error(`${TAG36} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17103
17243
|
return c.json({ error: "enrol-person-loopback-only" }, 403);
|
|
17104
17244
|
}
|
|
17105
17245
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17108,7 +17248,7 @@ app41.post("/", async (c) => {
|
|
|
17108
17248
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17109
17249
|
const offender = tokens.find((t) => !isLoopbackAddr6(t));
|
|
17110
17250
|
if (offender !== void 0) {
|
|
17111
|
-
console.error(`${
|
|
17251
|
+
console.error(`${TAG36} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17112
17252
|
return c.json({ error: "enrol-person-loopback-only" }, 403);
|
|
17113
17253
|
}
|
|
17114
17254
|
}
|
|
@@ -17133,7 +17273,7 @@ app41.post("/", async (c) => {
|
|
|
17133
17273
|
}
|
|
17134
17274
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
17135
17275
|
if (!accountId) {
|
|
17136
|
-
console.error(`${
|
|
17276
|
+
console.error(`${TAG36} op=person result=no-account-id`);
|
|
17137
17277
|
return c.json({ error: "no-account-id" }, 500);
|
|
17138
17278
|
}
|
|
17139
17279
|
let personId;
|
|
@@ -17141,7 +17281,7 @@ app41.post("/", async (c) => {
|
|
|
17141
17281
|
personId = await enrolPerson({ accountId, phone, email, agentSlug });
|
|
17142
17282
|
} catch (err) {
|
|
17143
17283
|
console.error(
|
|
17144
|
-
`${
|
|
17284
|
+
`${TAG36} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
17145
17285
|
);
|
|
17146
17286
|
return c.json({ error: "enrol-failed" }, 500);
|
|
17147
17287
|
}
|
|
@@ -17151,11 +17291,11 @@ app41.post("/", async (c) => {
|
|
|
17151
17291
|
if (g) grant = { grantId: g.grantId, status: g.status, contactValue: g.contactValue };
|
|
17152
17292
|
} catch (err) {
|
|
17153
17293
|
console.error(
|
|
17154
|
-
`${
|
|
17294
|
+
`${TAG36} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
17155
17295
|
);
|
|
17156
17296
|
}
|
|
17157
17297
|
console.log(
|
|
17158
|
-
`${
|
|
17298
|
+
`${TAG36} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
|
|
17159
17299
|
);
|
|
17160
17300
|
return c.json({ personId, grant }, 200);
|
|
17161
17301
|
});
|
|
@@ -17231,7 +17371,7 @@ app43.route("/enrol-person", enrol_person_default);
|
|
|
17231
17371
|
var admin_default = app43;
|
|
17232
17372
|
|
|
17233
17373
|
// server/routes/access/verify-token.ts
|
|
17234
|
-
var
|
|
17374
|
+
var TAG37 = "[access-verify]";
|
|
17235
17375
|
var MINT_TAG = "[access-session-mint]";
|
|
17236
17376
|
var COOKIE_NAME = "__access_session";
|
|
17237
17377
|
var app44 = new Hono();
|
|
@@ -17250,39 +17390,39 @@ app44.post("/", async (c) => {
|
|
|
17250
17390
|
}
|
|
17251
17391
|
const rateMsg = checkAccessRateLimit(ip, agentSlug);
|
|
17252
17392
|
if (rateMsg) {
|
|
17253
|
-
console.error(`${
|
|
17393
|
+
console.error(`${TAG37} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
|
|
17254
17394
|
return c.json({ error: rateMsg }, 429);
|
|
17255
17395
|
}
|
|
17256
17396
|
const grant = await findGrantByMagicToken(token);
|
|
17257
17397
|
if (!grant) {
|
|
17258
17398
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17259
|
-
console.error(`${
|
|
17399
|
+
console.error(`${TAG37} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
|
|
17260
17400
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
17261
17401
|
}
|
|
17262
17402
|
if (grant.agentSlug !== agentSlug) {
|
|
17263
17403
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17264
17404
|
console.error(
|
|
17265
|
-
`${
|
|
17405
|
+
`${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
|
|
17266
17406
|
);
|
|
17267
17407
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
17268
17408
|
}
|
|
17269
17409
|
if (grant.status === "expired" || grant.status === "revoked") {
|
|
17270
17410
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17271
17411
|
console.error(
|
|
17272
|
-
`${
|
|
17412
|
+
`${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
|
|
17273
17413
|
);
|
|
17274
17414
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
17275
17415
|
}
|
|
17276
17416
|
if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
|
|
17277
17417
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17278
17418
|
console.error(
|
|
17279
|
-
`${
|
|
17419
|
+
`${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
|
|
17280
17420
|
);
|
|
17281
17421
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
17282
17422
|
}
|
|
17283
17423
|
if (!grant.sliceToken) {
|
|
17284
17424
|
console.error(
|
|
17285
|
-
`${
|
|
17425
|
+
`${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
|
|
17286
17426
|
);
|
|
17287
17427
|
return c.json({ error: "grant-misconfigured" }, 500);
|
|
17288
17428
|
}
|
|
@@ -17298,12 +17438,12 @@ app44.post("/", async (c) => {
|
|
|
17298
17438
|
await consumeMagicTokenAndActivate(grant.grantId);
|
|
17299
17439
|
} catch (err) {
|
|
17300
17440
|
console.error(
|
|
17301
|
-
`${
|
|
17441
|
+
`${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
17302
17442
|
);
|
|
17303
17443
|
return c.json({ error: "verification-failed" }, 500);
|
|
17304
17444
|
}
|
|
17305
17445
|
clearAccessRateLimit(ip, agentSlug);
|
|
17306
|
-
console.log(`${
|
|
17446
|
+
console.log(`${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
|
|
17307
17447
|
console.log(
|
|
17308
17448
|
`${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
|
|
17309
17449
|
);
|
|
@@ -17379,7 +17519,7 @@ async function sendMagicLinkEmail(payload) {
|
|
|
17379
17519
|
}
|
|
17380
17520
|
|
|
17381
17521
|
// server/routes/access/request-magic-link.ts
|
|
17382
|
-
var
|
|
17522
|
+
var TAG38 = "[access-request-link]";
|
|
17383
17523
|
var app45 = new Hono();
|
|
17384
17524
|
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
17385
17525
|
app45.post("/", async (c) => {
|
|
@@ -17396,18 +17536,18 @@ app45.post("/", async (c) => {
|
|
|
17396
17536
|
}
|
|
17397
17537
|
const rateMsg = checkRequestLinkRateLimit(contactValue);
|
|
17398
17538
|
if (rateMsg) {
|
|
17399
|
-
console.error(`${
|
|
17539
|
+
console.error(`${TAG38} contactValue=${maskContact(contactValue)} result=rate-limited`);
|
|
17400
17540
|
return c.json({ error: rateMsg }, 429);
|
|
17401
17541
|
}
|
|
17402
17542
|
recordRequestLinkAttempt(contactValue);
|
|
17403
17543
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
17404
17544
|
if (!accountId) {
|
|
17405
|
-
console.error(`${
|
|
17545
|
+
console.error(`${TAG38} contactValue=${maskContact(contactValue)} result=no-account-id`);
|
|
17406
17546
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
17407
17547
|
}
|
|
17408
17548
|
const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
|
|
17409
17549
|
if (!grant) {
|
|
17410
|
-
console.log(`${
|
|
17550
|
+
console.log(`${TAG38} contactValue=${maskContact(contactValue)} result=notfound`);
|
|
17411
17551
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
17412
17552
|
}
|
|
17413
17553
|
let token;
|
|
@@ -17415,7 +17555,7 @@ app45.post("/", async (c) => {
|
|
|
17415
17555
|
token = await generateNewMagicToken(grant.grantId);
|
|
17416
17556
|
} catch (err) {
|
|
17417
17557
|
console.error(
|
|
17418
|
-
`${
|
|
17558
|
+
`${TAG38} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
17419
17559
|
);
|
|
17420
17560
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
17421
17561
|
}
|
|
@@ -17447,12 +17587,12 @@ app45.post("/", async (c) => {
|
|
|
17447
17587
|
});
|
|
17448
17588
|
if (!sendResult.ok) {
|
|
17449
17589
|
console.error(
|
|
17450
|
-
`${
|
|
17590
|
+
`${TAG38} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
|
|
17451
17591
|
);
|
|
17452
17592
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
17453
17593
|
}
|
|
17454
17594
|
console.log(
|
|
17455
|
-
`${
|
|
17595
|
+
`${TAG38} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
|
|
17456
17596
|
);
|
|
17457
17597
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
17458
17598
|
});
|
|
@@ -17465,7 +17605,7 @@ app46.route("/request-magic-link", request_magic_link_default);
|
|
|
17465
17605
|
var access_default = app46;
|
|
17466
17606
|
|
|
17467
17607
|
// server/routes/sites.ts
|
|
17468
|
-
import { existsSync as existsSync26, readFileSync as
|
|
17608
|
+
import { existsSync as existsSync26, readFileSync as readFileSync28, realpathSync as realpathSync7, statSync as statSync11 } from "fs";
|
|
17469
17609
|
import { resolve as resolve28 } from "path";
|
|
17470
17610
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
17471
17611
|
var MIME = {
|
|
@@ -17569,7 +17709,7 @@ app47.get("/:rel{.*}", (c) => {
|
|
|
17569
17709
|
}
|
|
17570
17710
|
let body;
|
|
17571
17711
|
try {
|
|
17572
|
-
body =
|
|
17712
|
+
body = readFileSync28(realPath);
|
|
17573
17713
|
} catch (err) {
|
|
17574
17714
|
const code = err?.code;
|
|
17575
17715
|
if (code === "EISDIR") {
|
|
@@ -17605,7 +17745,7 @@ var sites_default = app47;
|
|
|
17605
17745
|
|
|
17606
17746
|
// app/lib/visitor-token.ts
|
|
17607
17747
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
17608
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
17748
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync29, writeFileSync as writeFileSync11 } from "fs";
|
|
17609
17749
|
import { dirname as dirname7 } from "path";
|
|
17610
17750
|
var TOKEN_PREFIX = "v1.";
|
|
17611
17751
|
var SECRET_BYTES = 32;
|
|
@@ -17614,7 +17754,7 @@ var cachedSecret = null;
|
|
|
17614
17754
|
function getSecret() {
|
|
17615
17755
|
if (cachedSecret) return cachedSecret;
|
|
17616
17756
|
try {
|
|
17617
|
-
const hex2 =
|
|
17757
|
+
const hex2 = readFileSync29(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
17618
17758
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
17619
17759
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
17620
17760
|
return cachedSecret;
|
|
@@ -17628,7 +17768,7 @@ function getSecret() {
|
|
|
17628
17768
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
17629
17769
|
} catch {
|
|
17630
17770
|
}
|
|
17631
|
-
const hex =
|
|
17771
|
+
const hex = readFileSync29(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
17632
17772
|
cachedSecret = Buffer.from(hex, "hex");
|
|
17633
17773
|
return cachedSecret;
|
|
17634
17774
|
}
|
|
@@ -17681,8 +17821,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
17681
17821
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
17682
17822
|
|
|
17683
17823
|
// app/lib/brand-config.ts
|
|
17684
|
-
import { existsSync as existsSync27, readFileSync as
|
|
17685
|
-
import { join as
|
|
17824
|
+
import { existsSync as existsSync27, readFileSync as readFileSync30 } from "fs";
|
|
17825
|
+
import { join as join27 } from "path";
|
|
17686
17826
|
var cached2 = null;
|
|
17687
17827
|
var cachedAttempted = false;
|
|
17688
17828
|
function readBrandConfig() {
|
|
@@ -17690,10 +17830,10 @@ function readBrandConfig() {
|
|
|
17690
17830
|
cachedAttempted = true;
|
|
17691
17831
|
const platformRoot4 = process.env.MAXY_PLATFORM_ROOT;
|
|
17692
17832
|
if (!platformRoot4) return null;
|
|
17693
|
-
const brandPath =
|
|
17833
|
+
const brandPath = join27(platformRoot4, "config", "brand.json");
|
|
17694
17834
|
if (!existsSync27(brandPath)) return null;
|
|
17695
17835
|
try {
|
|
17696
|
-
cached2 = JSON.parse(
|
|
17836
|
+
cached2 = JSON.parse(readFileSync30(brandPath, "utf-8"));
|
|
17697
17837
|
return cached2;
|
|
17698
17838
|
} catch {
|
|
17699
17839
|
return null;
|
|
@@ -18494,7 +18634,7 @@ function startGraphHealthTimer() {
|
|
|
18494
18634
|
// app/lib/file-watcher.ts
|
|
18495
18635
|
import * as fsp2 from "fs/promises";
|
|
18496
18636
|
import { resolve as resolve30, sep as sep8 } from "path";
|
|
18497
|
-
var
|
|
18637
|
+
var ACCOUNT_UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
18498
18638
|
var DEFAULT_COALESCE_MS = 500;
|
|
18499
18639
|
var ROOTS = ["accounts"];
|
|
18500
18640
|
function _routeEvent(rootName, filename) {
|
|
@@ -18502,7 +18642,7 @@ function _routeEvent(rootName, filename) {
|
|
|
18502
18642
|
const segs = filename.split(sep8).filter(Boolean);
|
|
18503
18643
|
if (segs.length < 2) return null;
|
|
18504
18644
|
const accountId = segs[0];
|
|
18505
|
-
if (!
|
|
18645
|
+
if (!ACCOUNT_UUID_RE3.test(accountId)) return null;
|
|
18506
18646
|
return { accountId, relativePath: `${rootName}/${segs.join("/")}` };
|
|
18507
18647
|
}
|
|
18508
18648
|
async function startFileWatcher(opts = {}) {
|
|
@@ -18597,7 +18737,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
18597
18737
|
// app/lib/migrate-uploads.ts
|
|
18598
18738
|
import { mkdir as mkdir5, readdir as readdir5, rename as rename2, rm as rm3 } from "fs/promises";
|
|
18599
18739
|
import { dirname as dirname8, relative as relative5, resolve as resolve31 } from "path";
|
|
18600
|
-
var
|
|
18740
|
+
var ACCOUNT_UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
18601
18741
|
async function walkFiles(dir) {
|
|
18602
18742
|
const out = [];
|
|
18603
18743
|
let entries;
|
|
@@ -18673,7 +18813,7 @@ async function migrateUploads(opts = {}) {
|
|
|
18673
18813
|
try {
|
|
18674
18814
|
for (const entry of topEntries) {
|
|
18675
18815
|
const name = entry.name;
|
|
18676
|
-
if (
|
|
18816
|
+
if (ACCOUNT_UUID_RE4.test(name)) {
|
|
18677
18817
|
moved += await relocateTree(
|
|
18678
18818
|
resolve31(oldRoot, name),
|
|
18679
18819
|
resolve31(dataRoot, "accounts", name, "uploads"),
|
|
@@ -18720,7 +18860,7 @@ async function migrateUploads(opts = {}) {
|
|
|
18720
18860
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
18721
18861
|
import { readdir as readdir6, readFile as readFile7, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
|
|
18722
18862
|
import { existsSync as existsSync29 } from "fs";
|
|
18723
|
-
import { join as
|
|
18863
|
+
import { join as join28 } from "path";
|
|
18724
18864
|
var ADMIN_ROLE2 = "admin";
|
|
18725
18865
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
18726
18866
|
var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
|
|
@@ -18765,7 +18905,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
18765
18905
|
}
|
|
18766
18906
|
for (const slug of slugs) {
|
|
18767
18907
|
if (!slug.isDirectory()) continue;
|
|
18768
|
-
const slugDir =
|
|
18908
|
+
const slugDir = join28(projectsRoot, slug.name);
|
|
18769
18909
|
let entries;
|
|
18770
18910
|
try {
|
|
18771
18911
|
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
@@ -18774,9 +18914,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
18774
18914
|
}
|
|
18775
18915
|
for (const entry of entries) {
|
|
18776
18916
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
18777
|
-
out.push(
|
|
18917
|
+
out.push(join28(slugDir, entry.name));
|
|
18778
18918
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
18779
|
-
const subDir =
|
|
18919
|
+
const subDir = join28(slugDir, entry.name);
|
|
18780
18920
|
let subs;
|
|
18781
18921
|
try {
|
|
18782
18922
|
subs = await readdir6(subDir, { withFileTypes: true });
|
|
@@ -18784,7 +18924,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
18784
18924
|
continue;
|
|
18785
18925
|
}
|
|
18786
18926
|
for (const s of subs) {
|
|
18787
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
18927
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join28(subDir, s.name));
|
|
18788
18928
|
}
|
|
18789
18929
|
}
|
|
18790
18930
|
}
|
|
@@ -18796,7 +18936,7 @@ function shortId(path2) {
|
|
|
18796
18936
|
return base.slice(0, 8);
|
|
18797
18937
|
}
|
|
18798
18938
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
18799
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
18939
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join28(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
18800
18940
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
18801
18941
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
18802
18942
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -19622,6 +19762,26 @@ function createWebchatChannelRoutes(deps) {
|
|
|
19622
19762
|
deps.onReceived?.(key, messageId);
|
|
19623
19763
|
return c.json({ ok: true });
|
|
19624
19764
|
});
|
|
19765
|
+
app53.post("/webchat-channel/turn-end", async (c) => {
|
|
19766
|
+
const body = await c.req.json().catch(() => null);
|
|
19767
|
+
const key = body?.key;
|
|
19768
|
+
const sessionId = body?.sessionId;
|
|
19769
|
+
const replied = body?.replied;
|
|
19770
|
+
if (typeof key !== "string" || typeof sessionId !== "string" || typeof replied !== "boolean") {
|
|
19771
|
+
return c.json({ error: "key, sessionId and replied required" }, 400);
|
|
19772
|
+
}
|
|
19773
|
+
const k = key.slice(0, 8);
|
|
19774
|
+
const sid = sessionId.slice(0, 8);
|
|
19775
|
+
if (replied) {
|
|
19776
|
+
return c.json({ ok: true, delivered: "reply" });
|
|
19777
|
+
}
|
|
19778
|
+
const finalBytes = typeof body?.finalBytes === "number" && Number.isFinite(body.finalBytes) ? body.finalBytes : 0;
|
|
19779
|
+
if (finalBytes <= 0) {
|
|
19780
|
+
return c.json({ ok: true, delivered: "none" });
|
|
19781
|
+
}
|
|
19782
|
+
console.error(`[webchat-native] op=turn-undelivered channel=webchat key=${k} sessionId=${sid} bytes=${finalBytes}`);
|
|
19783
|
+
return c.json({ ok: true, delivered: "undelivered" });
|
|
19784
|
+
});
|
|
19625
19785
|
return app53;
|
|
19626
19786
|
}
|
|
19627
19787
|
|
|
@@ -20114,7 +20274,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
20114
20274
|
|
|
20115
20275
|
// ../lib/entitlement/src/index.ts
|
|
20116
20276
|
import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
|
|
20117
|
-
import { existsSync as existsSync30, readFileSync as
|
|
20277
|
+
import { existsSync as existsSync30, readFileSync as readFileSync31, statSync as statSync12 } from "fs";
|
|
20118
20278
|
import { resolve as resolve32 } from "path";
|
|
20119
20279
|
|
|
20120
20280
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -20178,7 +20338,7 @@ function resolveEntitlement(brand, account) {
|
|
|
20178
20338
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
20179
20339
|
let pubkeyPem;
|
|
20180
20340
|
try {
|
|
20181
|
-
pubkeyPem =
|
|
20341
|
+
pubkeyPem = readFileSync31(pubkeyPath(brand), "utf-8");
|
|
20182
20342
|
} catch (err) {
|
|
20183
20343
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
20184
20344
|
reason: "pubkey-missing"
|
|
@@ -20192,7 +20352,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
20192
20352
|
}
|
|
20193
20353
|
let envelope;
|
|
20194
20354
|
try {
|
|
20195
|
-
envelope = JSON.parse(
|
|
20355
|
+
envelope = JSON.parse(readFileSync31(entitlementPath, "utf-8"));
|
|
20196
20356
|
} catch {
|
|
20197
20357
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
20198
20358
|
}
|
|
@@ -20353,14 +20513,14 @@ function clientFrom(c) {
|
|
|
20353
20513
|
);
|
|
20354
20514
|
}
|
|
20355
20515
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
20356
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
20516
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join29(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
20357
20517
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
20358
20518
|
if (BRAND_JSON_PATH && !existsSync31(BRAND_JSON_PATH)) {
|
|
20359
20519
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
20360
20520
|
}
|
|
20361
20521
|
if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
|
|
20362
20522
|
try {
|
|
20363
|
-
const parsed = JSON.parse(
|
|
20523
|
+
const parsed = JSON.parse(readFileSync32(BRAND_JSON_PATH, "utf-8"));
|
|
20364
20524
|
BRAND = { ...BRAND, ...parsed };
|
|
20365
20525
|
} catch (err) {
|
|
20366
20526
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -20379,11 +20539,11 @@ var brandLoginOpts = {
|
|
|
20379
20539
|
bodyFont: BRAND.defaultFonts?.body,
|
|
20380
20540
|
logoContainsName: !!BRAND.logoContainsName
|
|
20381
20541
|
};
|
|
20382
|
-
var ALIAS_DOMAINS_PATH =
|
|
20542
|
+
var ALIAS_DOMAINS_PATH = join29(homedir3(), BRAND.configDir, "alias-domains.json");
|
|
20383
20543
|
function loadAliasDomains() {
|
|
20384
20544
|
try {
|
|
20385
20545
|
if (!existsSync31(ALIAS_DOMAINS_PATH)) return null;
|
|
20386
|
-
const parsed = JSON.parse(
|
|
20546
|
+
const parsed = JSON.parse(readFileSync32(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
20387
20547
|
if (!Array.isArray(parsed)) {
|
|
20388
20548
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
20389
20549
|
return null;
|
|
@@ -20411,7 +20571,7 @@ var app52 = new Hono();
|
|
|
20411
20571
|
var nativeFileFollowers = /* @__PURE__ */ new Map();
|
|
20412
20572
|
var waGateway = new WaGateway({
|
|
20413
20573
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
20414
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ??
|
|
20574
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join29(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
20415
20575
|
// Task 751 — file delivery on the native channel: resolve the platform
|
|
20416
20576
|
// account + path validation here and funnel through the shared send core.
|
|
20417
20577
|
sendDocument: async ({ senderId, accountId, filePath, caption }) => {
|
|
@@ -20427,7 +20587,7 @@ var waGateway = new WaGateway({
|
|
|
20427
20587
|
caption,
|
|
20428
20588
|
accountId,
|
|
20429
20589
|
maxyAccountId,
|
|
20430
|
-
platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ??
|
|
20590
|
+
platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join29(__dirname, ".."))
|
|
20431
20591
|
});
|
|
20432
20592
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
20433
20593
|
},
|
|
@@ -20902,7 +21062,7 @@ app52.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
20902
21062
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
20903
21063
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
20904
21064
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
20905
|
-
const body =
|
|
21065
|
+
const body = readFileSync32(filePath);
|
|
20906
21066
|
return c.body(body, 200, {
|
|
20907
21067
|
"Content-Type": contentType,
|
|
20908
21068
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -20932,7 +21092,7 @@ app52.get("/generated/:filename", (c) => {
|
|
|
20932
21092
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
20933
21093
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
20934
21094
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
20935
|
-
const body =
|
|
21095
|
+
const body = readFileSync32(filePath);
|
|
20936
21096
|
return c.body(body, 200, {
|
|
20937
21097
|
"Content-Type": contentType,
|
|
20938
21098
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -20947,19 +21107,29 @@ var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
|
20947
21107
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
20948
21108
|
if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
|
|
20949
21109
|
try {
|
|
20950
|
-
const fullBrand = JSON.parse(
|
|
21110
|
+
const fullBrand = JSON.parse(readFileSync32(BRAND_JSON_PATH, "utf-8"));
|
|
20951
21111
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
20952
21112
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
20953
21113
|
} catch {
|
|
20954
21114
|
}
|
|
20955
21115
|
}
|
|
21116
|
+
var DEFAULT_PRIMARY_CONTAINER = { label: "Project", term: "Projects" };
|
|
21117
|
+
var rawPrimaryContainer = BRAND.primaryContainer;
|
|
21118
|
+
var primaryContainerValid = !!rawPrimaryContainer && typeof rawPrimaryContainer.label === "string" && typeof rawPrimaryContainer.term === "string";
|
|
21119
|
+
var brandPrimaryContainer = primaryContainerValid ? rawPrimaryContainer : DEFAULT_PRIMARY_CONTAINER;
|
|
21120
|
+
if (primaryContainerValid) {
|
|
21121
|
+
console.log(`[brand] op=inject primaryContainer=${brandPrimaryContainer.label}/${brandPrimaryContainer.term}`);
|
|
21122
|
+
} else {
|
|
21123
|
+
console.log(`[brand] op=primaryContainer-default reason=${rawPrimaryContainer ? "malformed" : "absent"} brand=${BRAND.hostname}`);
|
|
21124
|
+
}
|
|
20956
21125
|
var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
20957
21126
|
productName: BRAND.productName,
|
|
20958
21127
|
hostname: BRAND.hostname,
|
|
20959
21128
|
marketingUrl: BRAND.marketingUrl,
|
|
20960
21129
|
logo: brandLogoPath,
|
|
20961
21130
|
icon: brandIconPath,
|
|
20962
|
-
logoContainsName: !!BRAND.logoContainsName
|
|
21131
|
+
logoContainsName: !!BRAND.logoContainsName,
|
|
21132
|
+
primaryContainer: brandPrimaryContainer
|
|
20963
21133
|
})}</script>`;
|
|
20964
21134
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
20965
21135
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
@@ -20967,7 +21137,7 @@ var brandIconFsPath = resolve33(process.cwd(), "public", brandIconPath.replace(/
|
|
|
20967
21137
|
var brandIconExists = existsSync31(brandIconFsPath);
|
|
20968
21138
|
var SW_SOURCE = (() => {
|
|
20969
21139
|
try {
|
|
20970
|
-
return
|
|
21140
|
+
return readFileSync32(resolve33(process.cwd(), "public", "sw.js"), "utf-8");
|
|
20971
21141
|
} catch {
|
|
20972
21142
|
return null;
|
|
20973
21143
|
}
|
|
@@ -20975,9 +21145,9 @@ var SW_SOURCE = (() => {
|
|
|
20975
21145
|
function readInstalledVersion() {
|
|
20976
21146
|
try {
|
|
20977
21147
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
20978
|
-
const versionFile =
|
|
21148
|
+
const versionFile = join29(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
20979
21149
|
if (!existsSync31(versionFile)) return "unknown";
|
|
20980
|
-
const content =
|
|
21150
|
+
const content = readFileSync32(versionFile, "utf-8").trim();
|
|
20981
21151
|
return content || "unknown";
|
|
20982
21152
|
} catch {
|
|
20983
21153
|
return "unknown";
|
|
@@ -21018,7 +21188,7 @@ var clientErrorReporterScript = `<script>
|
|
|
21018
21188
|
function cachedHtml(file) {
|
|
21019
21189
|
let html = htmlCache.get(file);
|
|
21020
21190
|
if (!html) {
|
|
21021
|
-
html =
|
|
21191
|
+
html = readFileSync32(resolve33(process.cwd(), "public", file), "utf-8");
|
|
21022
21192
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
21023
21193
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
21024
21194
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -21035,13 +21205,13 @@ ${clientErrorReporterScript}
|
|
|
21035
21205
|
return html;
|
|
21036
21206
|
}
|
|
21037
21207
|
function loadBrandingCache(agentSlug) {
|
|
21038
|
-
const configDir2 =
|
|
21208
|
+
const configDir2 = join29(homedir3(), BRAND.configDir);
|
|
21039
21209
|
try {
|
|
21040
21210
|
const accountId = getDefaultAccountId();
|
|
21041
21211
|
if (!accountId) return null;
|
|
21042
|
-
const cachePath =
|
|
21212
|
+
const cachePath = join29(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
21043
21213
|
if (!existsSync31(cachePath)) return null;
|
|
21044
|
-
return JSON.parse(
|
|
21214
|
+
return JSON.parse(readFileSync32(cachePath, "utf-8"));
|
|
21045
21215
|
} catch {
|
|
21046
21216
|
return null;
|
|
21047
21217
|
}
|
|
@@ -21136,7 +21306,7 @@ app52.use("/vnc-popout.html", logViewerFetch);
|
|
|
21136
21306
|
app52.get("/vnc-popout.html", (c) => {
|
|
21137
21307
|
let html = htmlCache.get("vnc-popout.html");
|
|
21138
21308
|
if (!html) {
|
|
21139
|
-
html =
|
|
21309
|
+
html = readFileSync32(resolve33(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
21140
21310
|
const name = escapeHtml(BRAND.productName);
|
|
21141
21311
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
21142
21312
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -21347,7 +21517,7 @@ async function runAdminUserReconcileTick() {
|
|
|
21347
21517
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
21348
21518
|
return;
|
|
21349
21519
|
}
|
|
21350
|
-
const usersRaw =
|
|
21520
|
+
const usersRaw = readFileSync32(USERS_FILE, "utf-8").trim();
|
|
21351
21521
|
if (!usersRaw) {
|
|
21352
21522
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
21353
21523
|
return;
|
|
@@ -21461,7 +21631,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
21461
21631
|
}
|
|
21462
21632
|
init({
|
|
21463
21633
|
configDir: configDirForWhatsApp,
|
|
21464
|
-
platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ??
|
|
21634
|
+
platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join29(__dirname, "..")),
|
|
21465
21635
|
accountConfig: bootAccountConfig,
|
|
21466
21636
|
onMessage: async (msg) => {
|
|
21467
21637
|
if (msg.isOwnerMirror) {
|