@rubytech/create-realagent 1.0.815 → 1.0.817
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +4 -2
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +2 -2
- package/payload/platform/plugins/cloudflare/PLUGIN.md +2 -2
- package/payload/platform/plugins/docs/references/cloudflare.md +3 -5
- package/payload/platform/plugins/docs/references/deployment.md +12 -12
- package/payload/platform/plugins/docs/references/internals.md +24 -24
- package/payload/platform/plugins/docs/references/memory-guide.md +1 -1
- package/payload/platform/plugins/docs/references/outlook-guide.md +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +8 -8
- package/payload/platform/plugins/docs/references/troubleshooting.md +41 -45
- package/payload/platform/plugins/memory/PLUGIN.md +4 -4
- package/payload/platform/plugins/whatsapp/PLUGIN.md +10 -4
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +80 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp-import/PLUGIN.md +4 -4
- package/payload/platform/templates/agents/admin/IDENTITY.md +2 -0
- package/payload/platform/templates/specialists/agents/personal-assistant.md +2 -2
- package/payload/server/chunk-P3HTEK33.js +10074 -0
- package/payload/server/chunk-UYLZDEMC.js +1114 -0
- package/payload/server/chunk-Y3UQFQM7.js +10067 -0
- package/payload/server/client-pool-BMPFHXHB.js +31 -0
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/public/assets/{Checkbox-DZxF6s72.js → Checkbox-CTGhpDKq.js} +1 -1
- package/payload/server/public/assets/{admin-CTb65MiO.js → admin-2w0XSMC6.js} +20 -20
- package/payload/server/public/assets/data-Y77FLKjs.js +1 -0
- package/payload/server/public/assets/graph-C4-jEPDE.js +1 -0
- package/payload/server/public/assets/{jsx-runtime-Cb4WEnIV.css → jsx-runtime-D4WovFYk.css} +1 -1
- package/payload/server/public/assets/{page-BLanFGXC.js → page-DkBfWy4C.js} +1 -1
- package/payload/server/public/assets/{page-DuwlF8N5.js → page-zuI00fuC.js} +1 -1
- package/payload/server/public/assets/{public-BqeUfasT.js → public-BdVIVpv8.js} +1 -1
- package/payload/server/public/assets/{useAdminFetch-DLGqK3Fs.js → useAdminFetch-DmHu0oCx.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-CHPkBGVV.js → useVoiceRecorder-CSc_hxjV.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +8 -8
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +243 -409
- package/payload/server/public/assets/data-BO12TydY.js +0 -1
- package/payload/server/public/assets/graph-XIePTyWQ.js +0 -1
- /package/payload/server/public/assets/{jsx-runtime-BZMOvG0f.js → jsx-runtime-DkaAusaX.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE,
|
|
7
7
|
TELEGRAM_WEBHOOK_SECRET_FILE,
|
|
8
8
|
USERS_FILE,
|
|
9
|
-
actionLogPath,
|
|
10
9
|
autoDeliverPremiumPlugins,
|
|
11
10
|
buildX11Env,
|
|
12
11
|
callOauthLlm,
|
|
@@ -30,7 +29,6 @@ import {
|
|
|
30
29
|
launchAction,
|
|
31
30
|
load,
|
|
32
31
|
logPath,
|
|
33
|
-
reconcileCloudflareSetupFromLog,
|
|
34
32
|
recordFailedAttempt,
|
|
35
33
|
render,
|
|
36
34
|
renderLoginPage,
|
|
@@ -53,7 +51,7 @@ import {
|
|
|
53
51
|
vncLog,
|
|
54
52
|
waitForExit,
|
|
55
53
|
writeChromiumWrapper
|
|
56
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-P3HTEK33.js";
|
|
57
55
|
import {
|
|
58
56
|
agentLogStream,
|
|
59
57
|
clearSessionHistory,
|
|
@@ -81,7 +79,7 @@ import {
|
|
|
81
79
|
sigtermFlushStreamLogs,
|
|
82
80
|
unregisterSession,
|
|
83
81
|
validateSession
|
|
84
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-UYLZDEMC.js";
|
|
85
83
|
import {
|
|
86
84
|
ACCOUNTS_DIR,
|
|
87
85
|
GREETING_DIRECTIVE,
|
|
@@ -620,8 +618,8 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
620
618
|
};
|
|
621
619
|
|
|
622
620
|
// server/index.ts
|
|
623
|
-
import { readFileSync as
|
|
624
|
-
import { resolve as resolve25, join as
|
|
621
|
+
import { readFileSync as readFileSync18, existsSync as existsSync24, watchFile } from "fs";
|
|
622
|
+
import { resolve as resolve25, join as join10, basename as basename7 } from "path";
|
|
625
623
|
import { homedir as homedir2 } from "os";
|
|
626
624
|
|
|
627
625
|
// app/lib/agent-slug-pattern.ts
|
|
@@ -807,52 +805,6 @@ function defaultRules() {
|
|
|
807
805
|
scope: "session",
|
|
808
806
|
suggestedAction: "The WebFetch SPA preflight has fired more than once in this conversation. Either the agent is ignoring the loud-failure directive (retrying WebFetch after seeing WEBFETCH_CANNOT_READ_JS_SPA), or multiple SPA URLs are being asked about. Read the conversation's stream log for the [tool-use] / [tool-result] sequence around each occurrence \u2014 if the agent dispatched WebFetch on the same URL or substituted Playwright silently, revisit the IDENTITY.md `Tool Failure Discipline` paragraph that names structured-error handling."
|
|
809
807
|
},
|
|
810
|
-
{
|
|
811
|
-
// Task 867 — fires when setup-tunnel.sh emits step=done but no
|
|
812
|
-
// `[persist] role=user … Cloudflare setup completed (actionId: <id>)`
|
|
813
|
-
// line appears within 60s. Covers the three failure modes of the
|
|
814
|
-
// action-relay-queue plumbing: queue-write failed, boot-drain consumer
|
|
815
|
-
// skipped the record, or the agent's hoisted persist threw. The
|
|
816
|
-
// followup pattern is intentionally narrow to the cloudflare-setup
|
|
817
|
-
// relay shape (not a general "any user persist") so a benign user
|
|
818
|
-
// typing in chat does not satisfy the followup. logSource=any so
|
|
819
|
-
// the rule sees both the script tee (in stream logs / server.log)
|
|
820
|
-
// and the [persist] line (server.log).
|
|
821
|
-
id: "cloudflare-setup-relay-not-acknowledged",
|
|
822
|
-
name: "Cloudflare-setup completed but the chat relay never acknowledged",
|
|
823
|
-
type: "absent-followup",
|
|
824
|
-
logSource: "any",
|
|
825
|
-
pattern: "\\[script:setup-tunnel\\] step=done",
|
|
826
|
-
followupPattern: "\\[persist\\] .*role=user.* Cloudflare setup completed \\(actionId:",
|
|
827
|
-
followupWindowMs: 6e4,
|
|
828
|
-
thresholdCount: 0,
|
|
829
|
-
thresholdWindowMinutes: 0,
|
|
830
|
-
suggestedAction: "[Task 867] cloudflare-setup completed but the post-action relay never reached the chat history. Check `[action-relay-queue] phase=enqueued` (queue write), `[action-completion-relay] phase=consumed` (boot-drain ran), and `[persist] role=user \u2026 Cloudflare setup completed` (graph write). One of those is missing; the matching grep recipe is in the Task 867 brief Observability section."
|
|
831
|
-
},
|
|
832
|
-
{
|
|
833
|
-
// Task 879 §C — onboarding turn-completion contract: any assistant
|
|
834
|
-
// turn that narrates a step transition with a non-question-terminated
|
|
835
|
-
// phrase ("Moving to step 9 — operator persona...", "Step 8 done.")
|
|
836
|
-
// must be followed within 30s by either a `render-component` call
|
|
837
|
-
// (the next step's UI surfaces) or `onboarding-complete-step`
|
|
838
|
-
// (deterministic step advance). The trigger pattern matches the
|
|
839
|
-
// `[onboarding-step-narration]` line written by stream-parser at the
|
|
840
|
-
// assistant text block emit site (only for non-question phrases).
|
|
841
|
-
// Operator's symptom in Task 879's evidence: "Moving to step 9..."
|
|
842
|
-
// followed by 4 minutes of silence until the operator nudged with
|
|
843
|
-
// "yeah, so?" — exact failure shape this rule catches.
|
|
844
|
-
id: "assistant-step-advance-deadend",
|
|
845
|
-
name: "Onboarding step-advance narration without follow-up tool call",
|
|
846
|
-
type: "absent-followup",
|
|
847
|
-
logSource: "session",
|
|
848
|
-
pattern: "\\[onboarding-step-narration\\]",
|
|
849
|
-
followupPattern: "\\[render-component\\]|\\[tool-use\\][^\\n]*name=mcp__admin__onboarding-complete-step",
|
|
850
|
-
followupWindowMs: 3e4,
|
|
851
|
-
thresholdCount: 0,
|
|
852
|
-
thresholdWindowMinutes: 0,
|
|
853
|
-
scope: "session",
|
|
854
|
-
suggestedAction: '[Task 879 \xA7C] An onboarding turn narrated a step transition (e.g. "Moving to step N", "Step N done.") but did not render a component or call `onboarding-complete-step` within 30s. The operator is left with a dead-end paragraph and no actionable surface. Check the `platform/plugins/admin/skills/onboarding/SKILL.md` Turn-completion contract section \u2014 the agent must end any step-advance turn with `render-component` or a `?`-terminated question, never bare prose.'
|
|
855
|
-
},
|
|
856
808
|
{
|
|
857
809
|
// Task 538: fires when a [spawn] line appears in a conversation's stream
|
|
858
810
|
// log but no subprocess-lifecycle marker follows within 10s. The three
|
|
@@ -2749,19 +2701,25 @@ function maybeRestoreCredsFromBackup(authDir) {
|
|
|
2749
2701
|
function readSelfId(authDir) {
|
|
2750
2702
|
try {
|
|
2751
2703
|
const credsPath = resolveCredsPath(authDir);
|
|
2752
|
-
if (!fsSync.existsSync(credsPath)) return { e164: null, jid: null };
|
|
2704
|
+
if (!fsSync.existsSync(credsPath)) return { e164: null, jid: null, lid: null };
|
|
2753
2705
|
const raw = fsSync.readFileSync(credsPath, "utf-8");
|
|
2754
2706
|
const parsed = JSON.parse(raw);
|
|
2755
2707
|
const jid = parsed?.me?.id ?? null;
|
|
2708
|
+
const lidRaw = parsed?.me?.lid ?? null;
|
|
2756
2709
|
let e164 = null;
|
|
2757
2710
|
if (jid) {
|
|
2758
2711
|
const match = jid.match(/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i);
|
|
2759
2712
|
if (match) e164 = match[1];
|
|
2760
2713
|
}
|
|
2761
|
-
|
|
2714
|
+
let lid = null;
|
|
2715
|
+
if (lidRaw) {
|
|
2716
|
+
const lidMatch = lidRaw.match(/^(\d+)(?::\d+)?@lid$/i);
|
|
2717
|
+
if (lidMatch) lid = `${lidMatch[1]}@lid`;
|
|
2718
|
+
}
|
|
2719
|
+
return { e164, jid, lid };
|
|
2762
2720
|
} catch (err) {
|
|
2763
2721
|
console.warn(`${TAG2} readSelfId failed authDir=${authDir}: ${String(err)}`);
|
|
2764
|
-
return { e164: null, jid: null };
|
|
2722
|
+
return { e164: null, jid: null, lid: null };
|
|
2765
2723
|
}
|
|
2766
2724
|
}
|
|
2767
2725
|
async function clearAuth(authDir) {
|
|
@@ -3246,6 +3204,20 @@ function phonesMatch(a, b) {
|
|
|
3246
3204
|
function isLidJid(jid) {
|
|
3247
3205
|
return WHATSAPP_LID_RE.test(jid);
|
|
3248
3206
|
}
|
|
3207
|
+
function withSelfLidShortcut(base, selfLid, selfJid) {
|
|
3208
|
+
if (!selfLid || !selfJid) return base;
|
|
3209
|
+
return {
|
|
3210
|
+
getPNForLID: async (lid) => {
|
|
3211
|
+
if (lid === selfLid) return selfJid;
|
|
3212
|
+
try {
|
|
3213
|
+
return await base?.getPNForLID(lid);
|
|
3214
|
+
} catch (err) {
|
|
3215
|
+
console.error(`[whatsapp:normalize] base lidMapping.getPNForLID threw lid=${lid}: ${String(err)}`);
|
|
3216
|
+
return void 0;
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
};
|
|
3220
|
+
}
|
|
3249
3221
|
async function resolveJidToE164(jid, lidMapping) {
|
|
3250
3222
|
if (!jid) return null;
|
|
3251
3223
|
const userMatch = jid.match(WHATSAPP_USER_JID_RE);
|
|
@@ -4367,6 +4339,7 @@ async function startConnection(accountId) {
|
|
|
4367
4339
|
connected: false,
|
|
4368
4340
|
selfPhone: null,
|
|
4369
4341
|
selfJid: null,
|
|
4342
|
+
selfLid: null,
|
|
4370
4343
|
reconnectAttempts: 0,
|
|
4371
4344
|
abortController: new AbortController(),
|
|
4372
4345
|
debouncer: null,
|
|
@@ -4430,7 +4403,13 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
4430
4403
|
}
|
|
4431
4404
|
await stopConnection(accountId);
|
|
4432
4405
|
const selfId = readSelfId(authDir);
|
|
4433
|
-
const
|
|
4406
|
+
const rawLidMapping = sock.signalRepository?.lidMapping ?? null;
|
|
4407
|
+
const lidMapping = withSelfLidShortcut(rawLidMapping, selfId.lid, selfId.jid);
|
|
4408
|
+
if (selfId.lid) {
|
|
4409
|
+
console.error(`[whatsapp:lid] resolved self-lid accountId=${accountId} selfLid=${selfId.lid} source=creds`);
|
|
4410
|
+
} else {
|
|
4411
|
+
console.error(`[whatsapp:lid] FAIL self-lid-resolution accountId=${accountId} reason=creds-missing-lid`);
|
|
4412
|
+
}
|
|
4434
4413
|
const conn = {
|
|
4435
4414
|
accountId,
|
|
4436
4415
|
platformAccountId,
|
|
@@ -4440,6 +4419,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
4440
4419
|
connected: true,
|
|
4441
4420
|
selfPhone: selfId.e164,
|
|
4442
4421
|
selfJid: selfId.jid,
|
|
4422
|
+
selfLid: selfId.lid,
|
|
4443
4423
|
reconnectAttempts: 0,
|
|
4444
4424
|
lastConnectedAt: Date.now(),
|
|
4445
4425
|
abortController: new AbortController(),
|
|
@@ -4463,8 +4443,36 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
4463
4443
|
});
|
|
4464
4444
|
monitorInbound(conn);
|
|
4465
4445
|
watchForDisconnect(conn);
|
|
4446
|
+
attachSelfIdRefreshOnCredsUpdate(conn);
|
|
4466
4447
|
console.error(`${TAG13} registered login socket for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
4467
4448
|
}
|
|
4449
|
+
function attachSelfIdRefreshOnCredsUpdate(conn) {
|
|
4450
|
+
if (!conn.sock) return;
|
|
4451
|
+
const sock = conn.sock;
|
|
4452
|
+
const handler = () => {
|
|
4453
|
+
setTimeout(() => {
|
|
4454
|
+
try {
|
|
4455
|
+
const fresh = readSelfId(conn.authDir);
|
|
4456
|
+
const lidChanged = fresh.lid !== conn.selfLid;
|
|
4457
|
+
conn.selfPhone = fresh.e164;
|
|
4458
|
+
conn.selfJid = fresh.jid;
|
|
4459
|
+
conn.selfLid = fresh.lid;
|
|
4460
|
+
const rawLidMapping = sock?.signalRepository?.lidMapping ?? null;
|
|
4461
|
+
conn.lidMapping = withSelfLidShortcut(rawLidMapping, fresh.lid, fresh.jid);
|
|
4462
|
+
if (lidChanged) {
|
|
4463
|
+
if (fresh.lid) {
|
|
4464
|
+
console.error(`[whatsapp:lid] resolved self-lid accountId=${conn.accountId} selfLid=${fresh.lid} source=creds-update`);
|
|
4465
|
+
} else {
|
|
4466
|
+
console.error(`[whatsapp:lid] FAIL self-lid-resolution accountId=${conn.accountId} reason=creds-update-cleared-lid`);
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
} catch (err) {
|
|
4470
|
+
console.error(`[whatsapp:lid] FAIL self-lid-refresh accountId=${conn.accountId} reason=${String(err).slice(0, 80)}`);
|
|
4471
|
+
}
|
|
4472
|
+
}, 1e3).unref?.();
|
|
4473
|
+
};
|
|
4474
|
+
sock.ev.on("creds.update", handler);
|
|
4475
|
+
}
|
|
4468
4476
|
function reloadConfig(accountConfig) {
|
|
4469
4477
|
loadConfig(accountConfig);
|
|
4470
4478
|
}
|
|
@@ -4528,9 +4536,16 @@ async function connectWithReconnect(conn) {
|
|
|
4528
4536
|
conn.connected = true;
|
|
4529
4537
|
conn.selfPhone = selfId.e164;
|
|
4530
4538
|
conn.selfJid = selfId.jid;
|
|
4539
|
+
conn.selfLid = selfId.lid;
|
|
4531
4540
|
conn.lastConnectedAt = connectedAt;
|
|
4532
4541
|
conn.lastError = void 0;
|
|
4533
|
-
|
|
4542
|
+
const rawLidMapping = sock.signalRepository?.lidMapping ?? null;
|
|
4543
|
+
conn.lidMapping = withSelfLidShortcut(rawLidMapping, selfId.lid, selfId.jid);
|
|
4544
|
+
if (selfId.lid) {
|
|
4545
|
+
console.error(`[whatsapp:lid] resolved self-lid accountId=${conn.accountId} selfLid=${selfId.lid} source=creds`);
|
|
4546
|
+
} else {
|
|
4547
|
+
console.error(`[whatsapp:lid] FAIL self-lid-resolution accountId=${conn.accountId} reason=creds-missing-lid`);
|
|
4548
|
+
}
|
|
4534
4549
|
console.error(`${TAG13} connected account=${conn.accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
4535
4550
|
try {
|
|
4536
4551
|
await sock.sendPresenceUpdate("available");
|
|
@@ -4550,6 +4565,7 @@ async function connectWithReconnect(conn) {
|
|
|
4550
4565
|
conn.debouncer = null;
|
|
4551
4566
|
}
|
|
4552
4567
|
monitorInbound(conn);
|
|
4568
|
+
attachSelfIdRefreshOnCredsUpdate(conn);
|
|
4553
4569
|
await waitForDisconnectEvent(conn);
|
|
4554
4570
|
uptimeMs = Date.now() - connectedAt;
|
|
4555
4571
|
conn.connected = false;
|
|
@@ -4900,8 +4916,16 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4900
4916
|
senderPhone,
|
|
4901
4917
|
selfPhone,
|
|
4902
4918
|
groupJid: remoteJid,
|
|
4919
|
+
// Three-way mention recognition (Sub-fix 4): legacy phone-JID equality,
|
|
4920
|
+
// legacy E.164-via-jidToE164 equality, plus the new self-LID equality so
|
|
4921
|
+
// mentions in LID-addressed groups (deliver as `<self-lid>@lid`) match.
|
|
4922
|
+
// Pre-LID groups continue matching the first two — additive, never blocks.
|
|
4923
|
+
// Each clause guards against null on its right operand; a creds-parse
|
|
4924
|
+
// failure leaves all three of selfJid/selfLid/selfPhone null and
|
|
4925
|
+
// jidToE164 of a malformed mention also returns null — without the
|
|
4926
|
+
// guards, `null === null` would spuriously match every nullified field.
|
|
4903
4927
|
isMentioned: extracted.mentionedJids.some(
|
|
4904
|
-
(jid) => jid === conn.selfJid || jidToE164(jid) === conn.selfPhone
|
|
4928
|
+
(jid) => conn.selfJid !== null && jid === conn.selfJid || conn.selfLid !== null && jid === conn.selfLid || conn.selfPhone !== null && jidToE164(jid) === conn.selfPhone
|
|
4905
4929
|
),
|
|
4906
4930
|
config: whatsAppConfig,
|
|
4907
4931
|
accountConfig: whatsAppConfig.accounts?.[conn.accountId]
|
|
@@ -7801,6 +7825,93 @@ app7.get("/messages", (c) => {
|
|
|
7801
7825
|
return c.json({ error: String(err) }, 500);
|
|
7802
7826
|
}
|
|
7803
7827
|
});
|
|
7828
|
+
app7.get("/conversation-graph-state", async (c) => {
|
|
7829
|
+
try {
|
|
7830
|
+
const directSessionKey = c.req.query("sessionKey");
|
|
7831
|
+
const jid = c.req.query("jid");
|
|
7832
|
+
const accountIdQuery = c.req.query("accountId");
|
|
7833
|
+
let sessionKey;
|
|
7834
|
+
if (directSessionKey) {
|
|
7835
|
+
sessionKey = directSessionKey;
|
|
7836
|
+
} else {
|
|
7837
|
+
if (!jid) {
|
|
7838
|
+
return c.json({ error: "Provide either sessionKey or jid (with optional accountId)." }, 400);
|
|
7839
|
+
}
|
|
7840
|
+
const accountId = validateAccountId(accountIdQuery ?? null);
|
|
7841
|
+
if (isGroupJid(jid)) {
|
|
7842
|
+
sessionKey = deriveSessionKey({
|
|
7843
|
+
agentType: "public",
|
|
7844
|
+
accountId,
|
|
7845
|
+
senderPhone: "",
|
|
7846
|
+
isGroup: true,
|
|
7847
|
+
groupJid: jid
|
|
7848
|
+
});
|
|
7849
|
+
} else {
|
|
7850
|
+
const e164 = jid.replace(/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i, "$1");
|
|
7851
|
+
if (!/^\d+$/.test(e164)) {
|
|
7852
|
+
return c.json({ error: `Cannot derive sessionKey from jid=${jid} \u2014 not a recognised user JID. Pass sessionKey explicitly.` }, 400);
|
|
7853
|
+
}
|
|
7854
|
+
sessionKey = deriveSessionKey({
|
|
7855
|
+
agentType: "public",
|
|
7856
|
+
accountId,
|
|
7857
|
+
senderPhone: e164,
|
|
7858
|
+
isGroup: false
|
|
7859
|
+
});
|
|
7860
|
+
}
|
|
7861
|
+
}
|
|
7862
|
+
const cypher = `
|
|
7863
|
+
MATCH (c:Conversation {sessionKey: $sessionKey})
|
|
7864
|
+
OPTIONAL MATCH (m:Message:WhatsAppMessage)-[:PART_OF]->(c)
|
|
7865
|
+
RETURN
|
|
7866
|
+
c.conversationId AS conversationId,
|
|
7867
|
+
m.messageId AS messageId,
|
|
7868
|
+
m.dateSent AS dateSent,
|
|
7869
|
+
m.fromMe AS fromMe,
|
|
7870
|
+
m.senderTelephone AS senderTelephone,
|
|
7871
|
+
m.body AS body
|
|
7872
|
+
ORDER BY dateSent ASC
|
|
7873
|
+
`;
|
|
7874
|
+
let session = null;
|
|
7875
|
+
const t0 = Date.now();
|
|
7876
|
+
let conversationId = null;
|
|
7877
|
+
const rows = [];
|
|
7878
|
+
try {
|
|
7879
|
+
session = getSession();
|
|
7880
|
+
const result = await session.run(cypher, { sessionKey });
|
|
7881
|
+
for (const rec of result.records) {
|
|
7882
|
+
if (conversationId === null) {
|
|
7883
|
+
conversationId = rec.get("conversationId") ?? null;
|
|
7884
|
+
}
|
|
7885
|
+
const messageId = rec.get("messageId");
|
|
7886
|
+
if (!messageId) continue;
|
|
7887
|
+
const dateSentRaw = rec.get("dateSent");
|
|
7888
|
+
rows.push({
|
|
7889
|
+
messageId,
|
|
7890
|
+
dateSent: dateSentRaw ? String(dateSentRaw) : null,
|
|
7891
|
+
fromMe: rec.get("fromMe") === true,
|
|
7892
|
+
senderTelephone: rec.get("senderTelephone") ?? null,
|
|
7893
|
+
body: rec.get("body") ?? null
|
|
7894
|
+
});
|
|
7895
|
+
}
|
|
7896
|
+
} catch (err) {
|
|
7897
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7898
|
+
console.error(`${TAG18} conversation-graph-state ERR sessionKey=${sessionKey} reason=${msg}`);
|
|
7899
|
+
return c.json({ error: `Graph query failed: ${msg}`, sessionKey, cypher: cypher.trim() }, 500);
|
|
7900
|
+
}
|
|
7901
|
+
const ms = Date.now() - t0;
|
|
7902
|
+
console.error(`[mcp:whatsapp] tool=whatsapp-conversation-graph-state sessionKey=${sessionKey} graphRows=${rows.length} conversationId=${conversationId ?? "null"} ms=${ms}`);
|
|
7903
|
+
return c.json({
|
|
7904
|
+
sessionKey,
|
|
7905
|
+
conversationId,
|
|
7906
|
+
graphRows: rows,
|
|
7907
|
+
cypher: cypher.trim(),
|
|
7908
|
+
ms
|
|
7909
|
+
});
|
|
7910
|
+
} catch (err) {
|
|
7911
|
+
console.error(`${TAG18} conversation-graph-state error: ${String(err)}`);
|
|
7912
|
+
return c.json({ error: String(err) }, 500);
|
|
7913
|
+
}
|
|
7914
|
+
});
|
|
7804
7915
|
app7.get("/group-info", async (c) => {
|
|
7805
7916
|
const accountId = validateAccountId(c.req.query("accountId") ?? null);
|
|
7806
7917
|
const jid = c.req.query("jid");
|
|
@@ -8216,7 +8327,7 @@ app9.post("/", async (c) => {
|
|
|
8216
8327
|
const safe = typeof v === "string" ? truncate(v, 200) : typeof v === "number" || typeof v === "boolean" ? String(v) : JSON.stringify(v).slice(0, 200);
|
|
8217
8328
|
return `${k}=${safe}`;
|
|
8218
8329
|
}).join(" ");
|
|
8219
|
-
const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set([
|
|
8330
|
+
const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set([]);
|
|
8220
8331
|
if (TAGGED_PREFIX_SOURCES.has(source)) {
|
|
8221
8332
|
console.log(
|
|
8222
8333
|
`[${source}] ts=${ts} ip=${ip} version=${version || "unknown"}${extra ? " " + extra : ""}`
|
|
@@ -8438,39 +8549,6 @@ app10.post("/", async (c) => {
|
|
|
8438
8549
|
const payload = await createAdminSession(selected.accountId, selected.config.thinkingView, userId, userName, selected.role, avatar);
|
|
8439
8550
|
return c.json(payload);
|
|
8440
8551
|
});
|
|
8441
|
-
app10.post("/rebind", async (c) => {
|
|
8442
|
-
let body;
|
|
8443
|
-
try {
|
|
8444
|
-
body = await c.req.json();
|
|
8445
|
-
} catch {
|
|
8446
|
-
return c.json({ ok: false, error: "invalid_json" }, 400);
|
|
8447
|
-
}
|
|
8448
|
-
const sessionKey = typeof body.session_key === "string" ? body.session_key : "";
|
|
8449
|
-
const conversationId = typeof body.lastKnownConversationId === "string" ? body.lastKnownConversationId : "";
|
|
8450
|
-
if (!sessionKey || !conversationId) {
|
|
8451
|
-
return c.json({ ok: false, error: "session_key and lastKnownConversationId required" }, 400);
|
|
8452
|
-
}
|
|
8453
|
-
const sk8 = sessionKey.slice(0, 8);
|
|
8454
|
-
const cid8 = conversationId.slice(0, 8);
|
|
8455
|
-
const bridge = await tryCookieBridgeForConversation(c, sessionKey, conversationId);
|
|
8456
|
-
if (!bridge.ok) {
|
|
8457
|
-
console.log(`[admin/session/rebind] sessionKey=${sk8}\u2026 result=bridge-rejected reason=${bridge.reason} conversationId=${cid8}\u2026`);
|
|
8458
|
-
const status = bridge.reason === "conversation-not-found" ? 404 : bridge.reason === "account-mismatch" ? 403 : 401;
|
|
8459
|
-
return c.json({ ok: false, error: bridge.reason }, status);
|
|
8460
|
-
}
|
|
8461
|
-
const existing = getConversationIdForSession(sessionKey);
|
|
8462
|
-
if (existing && existing !== conversationId) {
|
|
8463
|
-
console.log(`[admin/session/rebind] sessionKey=${sk8}\u2026 result=conflict conversationId=${existing.slice(0, 8)}\u2026 requested=${cid8}\u2026`);
|
|
8464
|
-
return c.json({ ok: false, error: "conflict", conversationId: existing }, 409);
|
|
8465
|
-
}
|
|
8466
|
-
const bound = setConversationIdForSession(sessionKey, conversationId);
|
|
8467
|
-
if (!bound) {
|
|
8468
|
-
console.error(`[admin/session/rebind] sessionKey=${sk8}\u2026 result=bind-failed conversationId=${cid8}\u2026 (post-bridge session-store missing \u2014 programmer error)`);
|
|
8469
|
-
return c.json({ ok: false, error: "bind_failed" }, 500);
|
|
8470
|
-
}
|
|
8471
|
-
console.log(`[admin/session/rebind] sessionKey=${sk8}\u2026 result=ok conversationId=${cid8}\u2026`);
|
|
8472
|
-
return c.json({ ok: true, conversationId });
|
|
8473
|
-
});
|
|
8474
8552
|
var session_default2 = app10;
|
|
8475
8553
|
|
|
8476
8554
|
// server/routes/admin/chat.ts
|
|
@@ -8765,7 +8843,7 @@ var app11 = new Hono();
|
|
|
8765
8843
|
app11.post("/cancel", requireAdminSession, async (c) => {
|
|
8766
8844
|
const session_key = c.var.sessionKey;
|
|
8767
8845
|
try {
|
|
8768
|
-
const { interruptClient: interruptClient2 } = await import("./client-pool-
|
|
8846
|
+
const { interruptClient: interruptClient2 } = await import("./client-pool-BMPFHXHB.js");
|
|
8769
8847
|
await interruptClient2(session_key);
|
|
8770
8848
|
return c.json({ ok: true });
|
|
8771
8849
|
} catch (err) {
|
|
@@ -9701,13 +9779,14 @@ app17.delete("/:id", requireAdminSession, async (c) => {
|
|
|
9701
9779
|
return c.json({ error: "Failed to delete session" }, 500);
|
|
9702
9780
|
}
|
|
9703
9781
|
});
|
|
9704
|
-
app17.
|
|
9782
|
+
app17.post("/:id/resume", async (c) => {
|
|
9705
9783
|
const conversationId = c.req.param("id");
|
|
9706
9784
|
const sessionKey = c.req.query("session_key") ?? "";
|
|
9707
9785
|
if (!sessionKey) {
|
|
9708
9786
|
console.error(`[session] middleware-reject status=400 code=session-missing reason="session_key required" path=${c.req.path}`);
|
|
9709
9787
|
return c.json({ error: "session_key required", code: "session-missing" }, 400);
|
|
9710
9788
|
}
|
|
9789
|
+
let bridged = false;
|
|
9711
9790
|
let result = validateSession(sessionKey, "admin");
|
|
9712
9791
|
if (!result.ok) {
|
|
9713
9792
|
if (result.reason === "session-not-registered") {
|
|
@@ -9720,6 +9799,7 @@ app17.get("/:id/messages", async (c) => {
|
|
|
9720
9799
|
console.error(`[session] middleware-reject status=401 code=session-not-registered reason="cookie-bridge-rejected:${bridge.reason}" path=${c.req.path} sessionKey=${tail}\u2026`);
|
|
9721
9800
|
return c.json({ error: "Invalid or expired admin session", code: "session-not-registered" }, 401);
|
|
9722
9801
|
}
|
|
9802
|
+
bridged = true;
|
|
9723
9803
|
result = validateSession(sessionKey, "admin");
|
|
9724
9804
|
if (!result.ok) {
|
|
9725
9805
|
const tail = sessionKey.slice(0, 8);
|
|
@@ -9734,34 +9814,12 @@ app17.get("/:id/messages", async (c) => {
|
|
|
9734
9814
|
}
|
|
9735
9815
|
}
|
|
9736
9816
|
const accountId = getAccountIdForSession(sessionKey);
|
|
9737
|
-
if (!accountId) return c.json({ error: "Account not found for session" }, 401);
|
|
9738
|
-
const owned = await verifyConversationOwnership(conversationId, accountId);
|
|
9739
|
-
if (!owned) return c.json({ error: "Conversation not found" }, 404);
|
|
9740
|
-
try {
|
|
9741
|
-
const messages = await getRecentMessages(conversationId, 50);
|
|
9742
|
-
const sanitised = messages.map((m) => ({
|
|
9743
|
-
...m,
|
|
9744
|
-
attachments: m.attachments.map((a) => ({
|
|
9745
|
-
attachmentId: a.attachmentId,
|
|
9746
|
-
filename: a.filename,
|
|
9747
|
-
mimeType: a.mimeType,
|
|
9748
|
-
sizeBytes: a.sizeBytes,
|
|
9749
|
-
ordinal: a.ordinal
|
|
9750
|
-
}))
|
|
9751
|
-
}));
|
|
9752
|
-
return c.json({ messages: sanitised });
|
|
9753
|
-
} catch (err) {
|
|
9754
|
-
console.error(`[sessions-messages] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
9755
|
-
return c.json({ error: "Failed to fetch messages" }, 500);
|
|
9756
|
-
}
|
|
9757
|
-
});
|
|
9758
|
-
app17.post("/:id/resume", requireAdminSession, async (c) => {
|
|
9759
|
-
const conversationId = c.req.param("id");
|
|
9760
|
-
const sessionKey = c.var.sessionKey;
|
|
9761
|
-
const accountId = getAccountIdForSession(sessionKey);
|
|
9762
9817
|
const userId = getUserIdForSession(sessionKey);
|
|
9763
|
-
if (!accountId
|
|
9764
|
-
return c.json({ error: "Session missing account
|
|
9818
|
+
if (!accountId) {
|
|
9819
|
+
return c.json({ error: "Session missing account context" }, 400);
|
|
9820
|
+
}
|
|
9821
|
+
if (!userId && !bridged) {
|
|
9822
|
+
return c.json({ error: "Session missing user context" }, 400);
|
|
9765
9823
|
}
|
|
9766
9824
|
const updatedAt = await verifyAndGetConversationUpdatedAt(conversationId, accountId);
|
|
9767
9825
|
if (updatedAt === null) return c.json({ error: "Conversation not found" }, 404);
|
|
@@ -9816,8 +9874,9 @@ app17.post("/:id/resume", requireAdminSession, async (c) => {
|
|
|
9816
9874
|
});
|
|
9817
9875
|
const textRuns = rehydrated.reduce((n, m) => n + (m.events?.filter((e) => e.type === "text").length ?? 0), 0);
|
|
9818
9876
|
const userMessageCount = rehydrated.filter((m) => m.role !== "assistant").length;
|
|
9877
|
+
const reason = bridged ? "post-restart" : "page-refresh";
|
|
9819
9878
|
try {
|
|
9820
|
-
appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-resume] sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} ${tag} loadedMessages=${messages.length} componentCount=${totalComponents} userAttachmentCount=${totalAttachments}
|
|
9879
|
+
appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-resume] reason=${reason} sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} ${tag} loadedMessages=${messages.length} componentCount=${totalComponents} userAttachmentCount=${totalAttachments}
|
|
9821
9880
|
`);
|
|
9822
9881
|
if (totalComponents > 0) {
|
|
9823
9882
|
appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [component-rehydrate] conversationId=${conversationId.slice(0, 8)} count=${totalComponents} valid=${totalValid} invalid=${totalInvalid} textRuns=${textRuns}
|
|
@@ -9830,7 +9889,7 @@ app17.post("/:id/resume", requireAdminSession, async (c) => {
|
|
|
9830
9889
|
} catch {
|
|
9831
9890
|
}
|
|
9832
9891
|
const age = formatAge(updatedAt);
|
|
9833
|
-
console.log(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} conversationId=${conversationId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} attachments=${totalAttachments} sessionKey=${sessionKey.slice(0, 8)}\u2026`);
|
|
9892
|
+
console.log(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} reason=${reason} conversationId=${conversationId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} attachments=${totalAttachments} sessionKey=${sessionKey.slice(0, 8)}\u2026`);
|
|
9834
9893
|
return c.json({ conversationId, messages: rehydrated });
|
|
9835
9894
|
});
|
|
9836
9895
|
app17.post("/:id/label", requireAdminSession, async (c) => {
|
|
@@ -9957,13 +10016,13 @@ async function cdpNavigateNewTab(url, opts = {}) {
|
|
|
9957
10016
|
// server/routes/admin/device-browser.ts
|
|
9958
10017
|
var app19 = new Hono();
|
|
9959
10018
|
app19.post("/navigate", async (c) => {
|
|
9960
|
-
const
|
|
10019
|
+
const TAG19 = "[device-url:click]";
|
|
9961
10020
|
let body;
|
|
9962
10021
|
try {
|
|
9963
10022
|
body = await c.req.json();
|
|
9964
10023
|
} catch (err) {
|
|
9965
10024
|
const detail = err instanceof Error ? err.message : String(err);
|
|
9966
|
-
console.error(`${
|
|
10025
|
+
console.error(`${TAG19} reject reason=body-not-json detail=${detail} browser=fallback navigateResult=error`);
|
|
9967
10026
|
return c.json(
|
|
9968
10027
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "Request body was not valid JSON" },
|
|
9969
10028
|
400
|
|
@@ -9973,7 +10032,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9973
10032
|
const intent = typeof body.intent === "string" ? body.intent : "";
|
|
9974
10033
|
const hostname2 = typeof body.hostname === "string" ? body.hostname : "";
|
|
9975
10034
|
if (!url) {
|
|
9976
|
-
console.error(`${
|
|
10035
|
+
console.error(`${TAG19} reject reason=missing-url intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`);
|
|
9977
10036
|
return c.json(
|
|
9978
10037
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "url field is required" },
|
|
9979
10038
|
400
|
|
@@ -9983,7 +10042,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9983
10042
|
try {
|
|
9984
10043
|
parsed = new URL(url);
|
|
9985
10044
|
} catch {
|
|
9986
|
-
console.error(`${
|
|
10045
|
+
console.error(`${TAG19} reject reason=url-malformed intent=${JSON.stringify(intent)} url=${url} browser=fallback navigateResult=error`);
|
|
9987
10046
|
return c.json(
|
|
9988
10047
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "url is not a valid URL" },
|
|
9989
10048
|
400
|
|
@@ -9991,7 +10050,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9991
10050
|
}
|
|
9992
10051
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
9993
10052
|
console.error(
|
|
9994
|
-
`${
|
|
10053
|
+
`${TAG19} reject reason=scheme-not-allowed scheme=${parsed.protocol} intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`
|
|
9995
10054
|
);
|
|
9996
10055
|
return c.json(
|
|
9997
10056
|
{
|
|
@@ -10007,7 +10066,7 @@ app19.post("/navigate", async (c) => {
|
|
|
10007
10066
|
const cdpOk = await ensureCdp(transport);
|
|
10008
10067
|
if (!cdpOk) {
|
|
10009
10068
|
console.error(
|
|
10010
|
-
`${
|
|
10069
|
+
`${TAG19} intent=${JSON.stringify(intent)} browser=fallback navigateResult=cdp-unreachable hostname=${JSON.stringify(hostname2)}`
|
|
10011
10070
|
);
|
|
10012
10071
|
return c.json(
|
|
10013
10072
|
{
|
|
@@ -10023,7 +10082,7 @@ app19.post("/navigate", async (c) => {
|
|
|
10023
10082
|
const browser = outcome.result === "ok" ? "vnc" : "fallback";
|
|
10024
10083
|
const detailStr = outcome.detail ? ` detail=${JSON.stringify(outcome.detail.length > 230 ? outcome.detail.slice(0, 227) + "..." : outcome.detail)}` : "";
|
|
10025
10084
|
console.error(
|
|
10026
|
-
`${
|
|
10085
|
+
`${TAG19} intent=${JSON.stringify(intent)} browser=${browser} navigateResult=${outcome.result} hostname=${JSON.stringify(hostname2)} targetId=${outcome.targetId ?? "none"}${detailStr}`
|
|
10027
10086
|
);
|
|
10028
10087
|
if (outcome.result !== "ok") {
|
|
10029
10088
|
return c.json(
|
|
@@ -10054,18 +10113,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
10054
10113
|
]);
|
|
10055
10114
|
var app20 = new Hono();
|
|
10056
10115
|
app20.post("/", async (c) => {
|
|
10057
|
-
const
|
|
10116
|
+
const TAG19 = "[admin:events]";
|
|
10058
10117
|
let body;
|
|
10059
10118
|
try {
|
|
10060
10119
|
body = await c.req.json();
|
|
10061
10120
|
} catch (err) {
|
|
10062
10121
|
const detail = err instanceof Error ? err.message : String(err);
|
|
10063
|
-
console.error(`${
|
|
10122
|
+
console.error(`${TAG19} reject reason=body-not-json detail=${detail}`);
|
|
10064
10123
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
10065
10124
|
}
|
|
10066
10125
|
const event = typeof body.event === "string" ? body.event : "";
|
|
10067
10126
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
10068
|
-
console.error(`${
|
|
10127
|
+
console.error(`${TAG19} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
10069
10128
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
10070
10129
|
}
|
|
10071
10130
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -10089,7 +10148,7 @@ var events_default = app20;
|
|
|
10089
10148
|
// server/routes/admin/cloudflare.ts
|
|
10090
10149
|
import { homedir } from "os";
|
|
10091
10150
|
import { resolve as resolve18 } from "path";
|
|
10092
|
-
import { readFileSync as
|
|
10151
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
10093
10152
|
|
|
10094
10153
|
// app/lib/dns-label.ts
|
|
10095
10154
|
var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
@@ -10127,131 +10186,14 @@ function addAliasDomain(hostname2) {
|
|
|
10127
10186
|
writeFileSync9(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
|
|
10128
10187
|
}
|
|
10129
10188
|
|
|
10130
|
-
// app/lib/action-relay-queue.ts
|
|
10131
|
-
import {
|
|
10132
|
-
existsSync as existsSync20,
|
|
10133
|
-
mkdirSync as mkdirSync9,
|
|
10134
|
-
readdirSync as readdirSync6,
|
|
10135
|
-
readFileSync as readFileSync16,
|
|
10136
|
-
unlinkSync as unlinkSync2,
|
|
10137
|
-
writeFileSync as writeFileSync10
|
|
10138
|
-
} from "fs";
|
|
10139
|
-
import { join as join9 } from "path";
|
|
10140
|
-
var TAG19 = "[action-relay-queue]";
|
|
10141
|
-
var FILE_PREFIX = "action-completion-relay-";
|
|
10142
|
-
var FILE_SUFFIX = ".json";
|
|
10143
|
-
function queueDir(accountDir) {
|
|
10144
|
-
return join9(accountDir, "queue");
|
|
10145
|
-
}
|
|
10146
|
-
function relayFilePath(accountDir, actionId) {
|
|
10147
|
-
return join9(queueDir(accountDir), `${FILE_PREFIX}${actionId}${FILE_SUFFIX}`);
|
|
10148
|
-
}
|
|
10149
|
-
function enqueueActionCompletionRelay(opts) {
|
|
10150
|
-
const dir = queueDir(opts.accountDir);
|
|
10151
|
-
mkdirSync9(dir, { recursive: true });
|
|
10152
|
-
const filePath = relayFilePath(opts.accountDir, opts.actionId);
|
|
10153
|
-
const record = {
|
|
10154
|
-
actionId: opts.actionId,
|
|
10155
|
-
conversationId: opts.conversationId,
|
|
10156
|
-
message: opts.message,
|
|
10157
|
-
queuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10158
|
-
};
|
|
10159
|
-
const body = JSON.stringify(record);
|
|
10160
|
-
try {
|
|
10161
|
-
writeFileSync10(filePath, body, { flag: "wx", encoding: "utf-8" });
|
|
10162
|
-
console.log(
|
|
10163
|
-
`${TAG19} phase=enqueued actionId=${opts.actionId} conversationId=${opts.conversationId} path=${filePath} bytes=${Buffer.byteLength(body, "utf-8")}`
|
|
10164
|
-
);
|
|
10165
|
-
return { enqueued: true, path: filePath };
|
|
10166
|
-
} catch (e) {
|
|
10167
|
-
if (e.code === "EEXIST") {
|
|
10168
|
-
console.log(
|
|
10169
|
-
`${TAG19} phase=enqueue-skipped actionId=${opts.actionId} reason=already-queued`
|
|
10170
|
-
);
|
|
10171
|
-
return { enqueued: false, reason: "already-queued", path: filePath };
|
|
10172
|
-
}
|
|
10173
|
-
throw e;
|
|
10174
|
-
}
|
|
10175
|
-
}
|
|
10176
|
-
function consumeActionCompletionRelays(accountDir) {
|
|
10177
|
-
const dir = queueDir(accountDir);
|
|
10178
|
-
if (!existsSync20(dir)) return [];
|
|
10179
|
-
let entries;
|
|
10180
|
-
try {
|
|
10181
|
-
entries = readdirSync6(dir);
|
|
10182
|
-
} catch (e) {
|
|
10183
|
-
console.error(
|
|
10184
|
-
`${TAG19} phase=readdir-failed dir=${dir} error=${e instanceof Error ? e.message : String(e)}`
|
|
10185
|
-
);
|
|
10186
|
-
return [];
|
|
10187
|
-
}
|
|
10188
|
-
const records = [];
|
|
10189
|
-
for (const name of entries) {
|
|
10190
|
-
if (!name.startsWith(FILE_PREFIX) || !name.endsWith(FILE_SUFFIX)) continue;
|
|
10191
|
-
const filePath = join9(dir, name);
|
|
10192
|
-
let raw;
|
|
10193
|
-
try {
|
|
10194
|
-
raw = readFileSync16(filePath, "utf-8");
|
|
10195
|
-
} catch (e) {
|
|
10196
|
-
console.error(
|
|
10197
|
-
`${TAG19} phase=read-failed file=${name} error=${e instanceof Error ? e.message : String(e)}`
|
|
10198
|
-
);
|
|
10199
|
-
continue;
|
|
10200
|
-
}
|
|
10201
|
-
let parsed;
|
|
10202
|
-
try {
|
|
10203
|
-
parsed = JSON.parse(raw);
|
|
10204
|
-
} catch (e) {
|
|
10205
|
-
console.error(
|
|
10206
|
-
`${TAG19} phase=parse-failed file=${name} error=${e instanceof Error ? e.message : String(e)}`
|
|
10207
|
-
);
|
|
10208
|
-
continue;
|
|
10209
|
-
}
|
|
10210
|
-
if (!isActionCompletionRelay(parsed)) {
|
|
10211
|
-
console.error(
|
|
10212
|
-
`${TAG19} phase=shape-invalid file=${name} keys=${parsed && typeof parsed === "object" ? Object.keys(parsed).join(",") : "non-object"}`
|
|
10213
|
-
);
|
|
10214
|
-
continue;
|
|
10215
|
-
}
|
|
10216
|
-
records.push({ rec: parsed, filePath });
|
|
10217
|
-
}
|
|
10218
|
-
records.sort((a, b) => a.rec.queuedAt.localeCompare(b.rec.queuedAt));
|
|
10219
|
-
const out = [];
|
|
10220
|
-
const now = Date.now();
|
|
10221
|
-
for (const { rec, filePath } of records) {
|
|
10222
|
-
const queuedAtMs = Date.parse(rec.queuedAt);
|
|
10223
|
-
const ageMs = Number.isFinite(queuedAtMs) ? now - queuedAtMs : 0;
|
|
10224
|
-
out.push({ ...rec, ageMs, filePath });
|
|
10225
|
-
}
|
|
10226
|
-
return out;
|
|
10227
|
-
}
|
|
10228
|
-
function deleteConsumedRelay(filePath) {
|
|
10229
|
-
try {
|
|
10230
|
-
unlinkSync2(filePath);
|
|
10231
|
-
} catch (e) {
|
|
10232
|
-
const code = e.code;
|
|
10233
|
-
if (code !== "ENOENT") {
|
|
10234
|
-
console.error(
|
|
10235
|
-
`${TAG19} phase=unlink-failed file=${filePath} error=${e instanceof Error ? e.message : String(e)}`
|
|
10236
|
-
);
|
|
10237
|
-
}
|
|
10238
|
-
}
|
|
10239
|
-
}
|
|
10240
|
-
function isActionCompletionRelay(value) {
|
|
10241
|
-
if (!value || typeof value !== "object") return false;
|
|
10242
|
-
const v = value;
|
|
10243
|
-
return typeof v.actionId === "string" && typeof v.conversationId === "string" && typeof v.message === "string" && typeof v.queuedAt === "string";
|
|
10244
|
-
}
|
|
10245
|
-
|
|
10246
10189
|
// server/routes/admin/cloudflare.ts
|
|
10247
|
-
import { existsSync as existsSyncFs, readFileSync as readFileSyncFs } from "fs";
|
|
10248
10190
|
var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
10249
10191
|
var DOMAINS_TIMEOUT_MS = 40 * 1e3;
|
|
10250
10192
|
function loadBrandInfo() {
|
|
10251
10193
|
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
|
|
10252
10194
|
const brandPath = resolve18(platformRoot2, "config", "brand.json");
|
|
10253
10195
|
try {
|
|
10254
|
-
const parsed = JSON.parse(
|
|
10196
|
+
const parsed = JSON.parse(readFileSync16(brandPath, "utf-8"));
|
|
10255
10197
|
const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
|
|
10256
10198
|
const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
|
|
10257
10199
|
return { hostname: hostname2, configDir: configDir2 };
|
|
@@ -10547,79 +10489,13 @@ actionId: ${actionId}`,
|
|
|
10547
10489
|
};
|
|
10548
10490
|
return ok(success);
|
|
10549
10491
|
});
|
|
10550
|
-
var RELAY_MAX_BODY = 8 * 1024;
|
|
10551
|
-
var RELAY_MAX_MESSAGE = 2048;
|
|
10552
|
-
var ACTION_ID_RE = /^[a-z0-9-]+$/;
|
|
10553
|
-
app21.post("/relay-completion", requireAdminSession, async (c) => {
|
|
10554
|
-
const sessionKey = c.var.sessionKey;
|
|
10555
|
-
let raw;
|
|
10556
|
-
try {
|
|
10557
|
-
raw = await c.req.text();
|
|
10558
|
-
} catch {
|
|
10559
|
-
return c.json({ ok: false, reason: "invalid-body" }, 400);
|
|
10560
|
-
}
|
|
10561
|
-
if (Buffer.byteLength(raw, "utf-8") > RELAY_MAX_BODY) {
|
|
10562
|
-
return c.json({ ok: false, reason: "body-too-large" }, 413);
|
|
10563
|
-
}
|
|
10564
|
-
let body;
|
|
10565
|
-
try {
|
|
10566
|
-
body = JSON.parse(raw);
|
|
10567
|
-
} catch {
|
|
10568
|
-
return c.json({ ok: false, reason: "invalid-json" }, 400);
|
|
10569
|
-
}
|
|
10570
|
-
if (!body || typeof body !== "object") {
|
|
10571
|
-
return c.json({ ok: false, reason: "invalid-body" }, 400);
|
|
10572
|
-
}
|
|
10573
|
-
const { actionId, message } = body;
|
|
10574
|
-
if (typeof actionId !== "string" || !ACTION_ID_RE.test(actionId) || actionId.length > 200) {
|
|
10575
|
-
console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=invalid-actionid sessionKey=${sessionKey.slice(-8)}`);
|
|
10576
|
-
return c.json({ ok: false, reason: "invalid-actionid" }, 400);
|
|
10577
|
-
}
|
|
10578
|
-
if (typeof message !== "string" || message.length === 0 || message.length > RELAY_MAX_MESSAGE) {
|
|
10579
|
-
return c.json({ ok: false, reason: "invalid-message" }, 400);
|
|
10580
|
-
}
|
|
10581
|
-
const conversationId = getConversationIdForSession(sessionKey);
|
|
10582
|
-
if (!conversationId) {
|
|
10583
|
-
console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=missing-conversation-id sessionKey=${sessionKey.slice(-8)}`);
|
|
10584
|
-
return c.json({ ok: false, reason: "missing-conversation-id" }, 400);
|
|
10585
|
-
}
|
|
10586
|
-
const account = resolveAccount();
|
|
10587
|
-
if (!account) {
|
|
10588
|
-
console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=missing-account-dir actionId=${actionId}`);
|
|
10589
|
-
return c.json({ ok: false, reason: "missing-account-dir" }, 500);
|
|
10590
|
-
}
|
|
10591
|
-
const logPath2 = actionLogPath(actionId);
|
|
10592
|
-
let auditOutcome = "log-absent";
|
|
10593
|
-
if (existsSyncFs(logPath2)) {
|
|
10594
|
-
try {
|
|
10595
|
-
const lines = readFileSyncFs(logPath2, "utf-8").split("\n");
|
|
10596
|
-
const reconciled = reconcileCloudflareSetupFromLog(lines);
|
|
10597
|
-
auditOutcome = reconciled ? reconciled.kind : "log-incomplete";
|
|
10598
|
-
} catch (e) {
|
|
10599
|
-
auditOutcome = `read-failed:${e instanceof Error ? e.message : String(e)}`;
|
|
10600
|
-
}
|
|
10601
|
-
}
|
|
10602
|
-
console.log(`[cloudflare-relay-completion] phase=enqueue-attempt actionId=${actionId} auditOutcome=${auditOutcome} conversationId=${conversationId}`);
|
|
10603
|
-
try {
|
|
10604
|
-
enqueueActionCompletionRelay({
|
|
10605
|
-
accountDir: account.accountDir,
|
|
10606
|
-
actionId,
|
|
10607
|
-
conversationId,
|
|
10608
|
-
message
|
|
10609
|
-
});
|
|
10610
|
-
} catch (e) {
|
|
10611
|
-
console.error(`[cloudflare-relay-completion] phase=enqueue-failed actionId=${actionId} error=${e instanceof Error ? e.message : String(e)}`);
|
|
10612
|
-
return c.json({ ok: false, reason: "enqueue-failed" }, 500);
|
|
10613
|
-
}
|
|
10614
|
-
return c.body(null, 204);
|
|
10615
|
-
});
|
|
10616
10492
|
var cloudflare_default = app21;
|
|
10617
10493
|
|
|
10618
10494
|
// server/routes/admin/files.ts
|
|
10619
10495
|
import { createReadStream as createReadStream3 } from "fs";
|
|
10620
10496
|
import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
10621
10497
|
import { realpathSync as realpathSync4 } from "fs";
|
|
10622
|
-
import { basename as basename6, dirname as dirname8, join as
|
|
10498
|
+
import { basename as basename6, dirname as dirname8, join as join9, resolve as resolve20, sep as sep2 } from "path";
|
|
10623
10499
|
import { Readable as Readable2 } from "stream";
|
|
10624
10500
|
|
|
10625
10501
|
// app/lib/data-path.ts
|
|
@@ -10977,7 +10853,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
10977
10853
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10978
10854
|
async function readMeta(absDir, baseName) {
|
|
10979
10855
|
try {
|
|
10980
|
-
const raw = await readFile4(
|
|
10856
|
+
const raw = await readFile4(join9(absDir, `${baseName}.meta.json`), "utf8");
|
|
10981
10857
|
const parsed = JSON.parse(raw);
|
|
10982
10858
|
if (typeof parsed?.filename === "string") {
|
|
10983
10859
|
return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
|
|
@@ -11015,7 +10891,7 @@ async function readAccountNames() {
|
|
|
11015
10891
|
}
|
|
11016
10892
|
async function enrich(absolute, entry, accountNames) {
|
|
11017
10893
|
if (entry.kind === "directory" && UUID_RE4.test(entry.name)) {
|
|
11018
|
-
const meta = await readMeta(
|
|
10894
|
+
const meta = await readMeta(join9(absolute, entry.name), entry.name);
|
|
11019
10895
|
if (meta?.filename) {
|
|
11020
10896
|
entry.displayName = meta.filename;
|
|
11021
10897
|
entry.mimeType = meta.mimeType;
|
|
@@ -11074,7 +10950,7 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
11074
10950
|
continue;
|
|
11075
10951
|
}
|
|
11076
10952
|
try {
|
|
11077
|
-
const entryPath =
|
|
10953
|
+
const entryPath = join9(absolute, name);
|
|
11078
10954
|
const s = await stat4(entryPath);
|
|
11079
10955
|
entries.push({
|
|
11080
10956
|
name,
|
|
@@ -11247,7 +11123,7 @@ app22.delete("/", requireAdminSession, async (c) => {
|
|
|
11247
11123
|
}
|
|
11248
11124
|
const dot = base.lastIndexOf(".");
|
|
11249
11125
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
11250
|
-
const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ?
|
|
11126
|
+
const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ? join9(dirname8(absolute), `${stem}.meta.json`) : null;
|
|
11251
11127
|
await unlink2(absolute);
|
|
11252
11128
|
if (sidecarPath) {
|
|
11253
11129
|
try {
|
|
@@ -12728,7 +12604,7 @@ var adherence_default = app30;
|
|
|
12728
12604
|
import neo4j3 from "neo4j-driver";
|
|
12729
12605
|
import { readFile as readFile5, readdir as readdir3, stat as stat5 } from "fs/promises";
|
|
12730
12606
|
import { resolve as resolve21, relative as relative2, isAbsolute } from "path";
|
|
12731
|
-
import { existsSync as
|
|
12607
|
+
import { existsSync as existsSync20 } from "fs";
|
|
12732
12608
|
var LIMIT = 50;
|
|
12733
12609
|
var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
|
|
12734
12610
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
@@ -12876,7 +12752,7 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
12876
12752
|
async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
12877
12753
|
const names = /* @__PURE__ */ new Set();
|
|
12878
12754
|
for (const dir of [overrideDir, bundledDir]) {
|
|
12879
|
-
if (!
|
|
12755
|
+
if (!existsSync20(dir)) continue;
|
|
12880
12756
|
try {
|
|
12881
12757
|
const entries = await readdir3(dir);
|
|
12882
12758
|
for (const entry of entries) {
|
|
@@ -12891,7 +12767,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
12891
12767
|
}
|
|
12892
12768
|
async function readAgentTemplateRow(inp) {
|
|
12893
12769
|
let chosenPath = null;
|
|
12894
|
-
if (
|
|
12770
|
+
if (existsSync20(inp.overridePath)) {
|
|
12895
12771
|
try {
|
|
12896
12772
|
validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
|
|
12897
12773
|
chosenPath = inp.overridePath;
|
|
@@ -12902,7 +12778,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
12902
12778
|
);
|
|
12903
12779
|
return null;
|
|
12904
12780
|
}
|
|
12905
|
-
} else if (
|
|
12781
|
+
} else if (existsSync20(inp.bundledPath)) {
|
|
12906
12782
|
if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
|
|
12907
12783
|
console.error(
|
|
12908
12784
|
`[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
|
|
@@ -12944,7 +12820,7 @@ var sidebar_artefacts_default = app31;
|
|
|
12944
12820
|
// server/routes/admin/sidebar-artefact-save.ts
|
|
12945
12821
|
import { mkdir as mkdir4, readdir as readdir4, stat as stat6, writeFile as writeFile5 } from "fs/promises";
|
|
12946
12822
|
import { resolve as resolve22 } from "path";
|
|
12947
|
-
import { existsSync as
|
|
12823
|
+
import { existsSync as existsSync21 } from "fs";
|
|
12948
12824
|
var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
|
|
12949
12825
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
12950
12826
|
var app32 = new Hono();
|
|
@@ -13008,7 +12884,7 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
13008
12884
|
}
|
|
13009
12885
|
if (UUID_RE5.test(id)) {
|
|
13010
12886
|
const dir = resolve22(ATTACHMENTS_ROOT, accountId, id);
|
|
13011
|
-
if (!
|
|
12887
|
+
if (!existsSync21(dir)) {
|
|
13012
12888
|
return { kind: "reject", status: 400, reason: "not-found" };
|
|
13013
12889
|
}
|
|
13014
12890
|
try {
|
|
@@ -13032,7 +12908,7 @@ var sidebar_artefact_save_default = app32;
|
|
|
13032
12908
|
|
|
13033
12909
|
// server/routes/admin/sidebar-artefact-content.ts
|
|
13034
12910
|
import { readFile as readFile6, readdir as readdir5 } from "fs/promises";
|
|
13035
|
-
import { existsSync as
|
|
12911
|
+
import { existsSync as existsSync22 } from "fs";
|
|
13036
12912
|
import { resolve as resolve23 } from "path";
|
|
13037
12913
|
var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
13038
12914
|
var app33 = new Hono();
|
|
@@ -13046,7 +12922,7 @@ app33.get("/", requireAdminSession, async (c) => {
|
|
|
13046
12922
|
return new Response("Not found", { status: 404 });
|
|
13047
12923
|
}
|
|
13048
12924
|
const dir = resolve23(ATTACHMENTS_ROOT, accountId, id);
|
|
13049
|
-
if (!
|
|
12925
|
+
if (!existsSync22(dir)) {
|
|
13050
12926
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
13051
12927
|
return new Response("Not found", { status: 404 });
|
|
13052
12928
|
}
|
|
@@ -13108,7 +12984,7 @@ app34.route("/sidebar-artefact-content", sidebar_artefact_content_default);
|
|
|
13108
12984
|
var admin_default = app34;
|
|
13109
12985
|
|
|
13110
12986
|
// server/routes/sites.ts
|
|
13111
|
-
import { existsSync as
|
|
12987
|
+
import { existsSync as existsSync23, readFileSync as readFileSync17, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
|
|
13112
12988
|
import { resolve as resolve24 } from "path";
|
|
13113
12989
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
13114
12990
|
var MIME = {
|
|
@@ -13175,7 +13051,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
13175
13051
|
}
|
|
13176
13052
|
let stat7;
|
|
13177
13053
|
try {
|
|
13178
|
-
stat7 =
|
|
13054
|
+
stat7 = existsSync23(filePath) ? statSync8(filePath) : null;
|
|
13179
13055
|
} catch {
|
|
13180
13056
|
stat7 = null;
|
|
13181
13057
|
}
|
|
@@ -13188,7 +13064,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
13188
13064
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
13189
13065
|
return c.text("Forbidden", 403);
|
|
13190
13066
|
}
|
|
13191
|
-
if (!
|
|
13067
|
+
if (!existsSync23(filePath)) {
|
|
13192
13068
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
13193
13069
|
return c.text("Not found", 404);
|
|
13194
13070
|
}
|
|
@@ -13207,7 +13083,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
13207
13083
|
}
|
|
13208
13084
|
let body;
|
|
13209
13085
|
try {
|
|
13210
|
-
body =
|
|
13086
|
+
body = readFileSync17(realPath);
|
|
13211
13087
|
} catch (err) {
|
|
13212
13088
|
const code = err?.code;
|
|
13213
13089
|
if (code === "EISDIR") {
|
|
@@ -13339,14 +13215,14 @@ function clientFrom(c) {
|
|
|
13339
13215
|
);
|
|
13340
13216
|
}
|
|
13341
13217
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
13342
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT7 ?
|
|
13218
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join10(PLATFORM_ROOT7, "config", "brand.json") : "";
|
|
13343
13219
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
|
|
13344
|
-
if (BRAND_JSON_PATH && !
|
|
13220
|
+
if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
|
|
13345
13221
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
13346
13222
|
}
|
|
13347
|
-
if (BRAND_JSON_PATH &&
|
|
13223
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
13348
13224
|
try {
|
|
13349
|
-
const parsed = JSON.parse(
|
|
13225
|
+
const parsed = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
|
|
13350
13226
|
BRAND = { ...BRAND, ...parsed };
|
|
13351
13227
|
} catch (err) {
|
|
13352
13228
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -13365,11 +13241,11 @@ var brandLoginOpts = {
|
|
|
13365
13241
|
bodyFont: BRAND.defaultFonts?.body,
|
|
13366
13242
|
logoContainsName: !!BRAND.logoContainsName
|
|
13367
13243
|
};
|
|
13368
|
-
var ALIAS_DOMAINS_PATH2 =
|
|
13244
|
+
var ALIAS_DOMAINS_PATH2 = join10(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
13369
13245
|
function loadAliasDomains() {
|
|
13370
13246
|
try {
|
|
13371
|
-
if (!
|
|
13372
|
-
const parsed = JSON.parse(
|
|
13247
|
+
if (!existsSync24(ALIAS_DOMAINS_PATH2)) return null;
|
|
13248
|
+
const parsed = JSON.parse(readFileSync18(ALIAS_DOMAINS_PATH2, "utf-8"));
|
|
13373
13249
|
if (!Array.isArray(parsed)) {
|
|
13374
13250
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
13375
13251
|
return null;
|
|
@@ -13715,14 +13591,14 @@ app36.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
13715
13591
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
13716
13592
|
return c.text("Forbidden", 403);
|
|
13717
13593
|
}
|
|
13718
|
-
if (!
|
|
13594
|
+
if (!existsSync24(filePath)) {
|
|
13719
13595
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
13720
13596
|
return c.text("Not found", 404);
|
|
13721
13597
|
}
|
|
13722
13598
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
13723
13599
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
13724
13600
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
13725
|
-
const body =
|
|
13601
|
+
const body = readFileSync18(filePath);
|
|
13726
13602
|
return c.body(body, 200, {
|
|
13727
13603
|
"Content-Type": contentType,
|
|
13728
13604
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -13745,14 +13621,14 @@ app36.get("/generated/:filename", (c) => {
|
|
|
13745
13621
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
13746
13622
|
return c.text("Forbidden", 403);
|
|
13747
13623
|
}
|
|
13748
|
-
if (!
|
|
13624
|
+
if (!existsSync24(filePath)) {
|
|
13749
13625
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
13750
13626
|
return c.text("Not found", 404);
|
|
13751
13627
|
}
|
|
13752
13628
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
13753
13629
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
13754
13630
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
13755
|
-
const body =
|
|
13631
|
+
const body = readFileSync18(filePath);
|
|
13756
13632
|
return c.body(body, 200, {
|
|
13757
13633
|
"Content-Type": contentType,
|
|
13758
13634
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -13762,9 +13638,9 @@ app36.route("/sites", sites_default);
|
|
|
13762
13638
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
13763
13639
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
13764
13640
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
13765
|
-
if (BRAND_JSON_PATH &&
|
|
13641
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
13766
13642
|
try {
|
|
13767
|
-
const fullBrand = JSON.parse(
|
|
13643
|
+
const fullBrand = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
|
|
13768
13644
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
13769
13645
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
13770
13646
|
} catch {
|
|
@@ -13781,9 +13657,9 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
13781
13657
|
function readInstalledVersion() {
|
|
13782
13658
|
try {
|
|
13783
13659
|
if (!PLATFORM_ROOT7) return "unknown";
|
|
13784
|
-
const versionFile =
|
|
13785
|
-
if (!
|
|
13786
|
-
const content =
|
|
13660
|
+
const versionFile = join10(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
|
|
13661
|
+
if (!existsSync24(versionFile)) return "unknown";
|
|
13662
|
+
const content = readFileSync18(versionFile, "utf-8").trim();
|
|
13787
13663
|
return content || "unknown";
|
|
13788
13664
|
} catch {
|
|
13789
13665
|
return "unknown";
|
|
@@ -13824,7 +13700,7 @@ var clientErrorReporterScript = `<script>
|
|
|
13824
13700
|
function cachedHtml(file) {
|
|
13825
13701
|
let html = htmlCache.get(file);
|
|
13826
13702
|
if (!html) {
|
|
13827
|
-
html =
|
|
13703
|
+
html = readFileSync18(resolve25(process.cwd(), "public", file), "utf-8");
|
|
13828
13704
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
13829
13705
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
13830
13706
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -13840,26 +13716,26 @@ ${clientErrorReporterScript}
|
|
|
13840
13716
|
}
|
|
13841
13717
|
var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
13842
13718
|
function loadBrandingCache(agentSlug) {
|
|
13843
|
-
const configDir2 =
|
|
13719
|
+
const configDir2 = join10(homedir2(), BRAND.configDir);
|
|
13844
13720
|
try {
|
|
13845
|
-
const accountJsonPath =
|
|
13846
|
-
if (!
|
|
13847
|
-
const account = JSON.parse(
|
|
13721
|
+
const accountJsonPath = join10(configDir2, "account.json");
|
|
13722
|
+
if (!existsSync24(accountJsonPath)) return null;
|
|
13723
|
+
const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
|
|
13848
13724
|
const accountId = account.accountId;
|
|
13849
13725
|
if (!accountId) return null;
|
|
13850
|
-
const cachePath =
|
|
13851
|
-
if (!
|
|
13852
|
-
return JSON.parse(
|
|
13726
|
+
const cachePath = join10(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
13727
|
+
if (!existsSync24(cachePath)) return null;
|
|
13728
|
+
return JSON.parse(readFileSync18(cachePath, "utf-8"));
|
|
13853
13729
|
} catch {
|
|
13854
13730
|
return null;
|
|
13855
13731
|
}
|
|
13856
13732
|
}
|
|
13857
13733
|
function resolveDefaultSlug() {
|
|
13858
13734
|
try {
|
|
13859
|
-
const configDir2 =
|
|
13860
|
-
const accountJsonPath =
|
|
13861
|
-
if (!
|
|
13862
|
-
const account = JSON.parse(
|
|
13735
|
+
const configDir2 = join10(homedir2(), BRAND.configDir);
|
|
13736
|
+
const accountJsonPath = join10(configDir2, "account.json");
|
|
13737
|
+
if (!existsSync24(accountJsonPath)) return null;
|
|
13738
|
+
const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
|
|
13863
13739
|
return account.defaultAgent || null;
|
|
13864
13740
|
} catch {
|
|
13865
13741
|
return null;
|
|
@@ -13932,7 +13808,7 @@ app36.use("/vnc-popout.html", logViewerFetch);
|
|
|
13932
13808
|
app36.get("/vnc-popout.html", (c) => {
|
|
13933
13809
|
let html = htmlCache.get("vnc-popout.html");
|
|
13934
13810
|
if (!html) {
|
|
13935
|
-
html =
|
|
13811
|
+
html = readFileSync18(resolve25(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
13936
13812
|
const name = escapeHtml(BRAND.productName);
|
|
13937
13813
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
13938
13814
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -14022,8 +13898,8 @@ try {
|
|
|
14022
13898
|
(async () => {
|
|
14023
13899
|
try {
|
|
14024
13900
|
let userId = "";
|
|
14025
|
-
if (
|
|
14026
|
-
const users = JSON.parse(
|
|
13901
|
+
if (existsSync24(USERS_FILE)) {
|
|
13902
|
+
const users = JSON.parse(readFileSync18(USERS_FILE, "utf-8").trim() || "[]");
|
|
14027
13903
|
userId = users[0]?.userId ?? "";
|
|
14028
13904
|
}
|
|
14029
13905
|
await backfillNullUserIdConversations(userId);
|
|
@@ -14083,48 +13959,6 @@ for (const dir of bootEnabled) {
|
|
|
14083
13959
|
bootDelivered.push(dir);
|
|
14084
13960
|
}
|
|
14085
13961
|
console.error(`[plugins] readiness enabled=${bootEnabled.length} delivered=${bootDelivered.length} dist-missing=[${bootDistMissing.join(",")}] missing=[${bootMissing.join(",")}]`);
|
|
14086
|
-
(async () => {
|
|
14087
|
-
if (!bootAccount) {
|
|
14088
|
-
console.log("[action-completion-relay] phase=consumed-skip reason=no-account");
|
|
14089
|
-
return;
|
|
14090
|
-
}
|
|
14091
|
-
let records;
|
|
14092
|
-
try {
|
|
14093
|
-
records = consumeActionCompletionRelays(bootAccount.accountDir);
|
|
14094
|
-
} catch (err) {
|
|
14095
|
-
console.error(`[action-completion-relay] phase=consume-failed error=${err instanceof Error ? err.message : String(err)}`);
|
|
14096
|
-
return;
|
|
14097
|
-
}
|
|
14098
|
-
if (records.length === 0) {
|
|
14099
|
-
console.log(`[action-completion-relay] phase=consumed-empty accountId=${bootAccount.accountId.slice(0, 8)}\u2026`);
|
|
14100
|
-
return;
|
|
14101
|
-
}
|
|
14102
|
-
console.log(`[action-completion-relay] phase=consume-batch count=${records.length} accountId=${bootAccount.accountId.slice(0, 8)}\u2026`);
|
|
14103
|
-
for (const rec of records) {
|
|
14104
|
-
const sessionKey = `cloudflare-relay-boot:${rec.actionId}`;
|
|
14105
|
-
let outcome = "injected";
|
|
14106
|
-
let dispatchError;
|
|
14107
|
-
try {
|
|
14108
|
-
registerSession(sessionKey, "admin", bootAccount.accountId);
|
|
14109
|
-
setConversationIdForSession(sessionKey, rec.conversationId);
|
|
14110
|
-
for await (const _ev of invokeAgent({ type: "admin" }, rec.message, sessionKey)) {
|
|
14111
|
-
}
|
|
14112
|
-
} catch (err) {
|
|
14113
|
-
outcome = "dispatch-failed";
|
|
14114
|
-
dispatchError = err instanceof Error ? err.message : String(err);
|
|
14115
|
-
} finally {
|
|
14116
|
-
unregisterSession(sessionKey);
|
|
14117
|
-
}
|
|
14118
|
-
if (outcome === "injected") {
|
|
14119
|
-
deleteConsumedRelay(rec.filePath);
|
|
14120
|
-
console.log(`[action-completion-relay] phase=consumed actionId=${rec.actionId} conversationId=${rec.conversationId} ageMs=${rec.ageMs} outcome=injected`);
|
|
14121
|
-
} else {
|
|
14122
|
-
console.error(`[action-completion-relay] phase=consumed actionId=${rec.actionId} conversationId=${rec.conversationId} ageMs=${rec.ageMs} outcome=${outcome} reason=${JSON.stringify(dispatchError ?? "")} fileRetained=${JSON.stringify(rec.filePath)}`);
|
|
14123
|
-
}
|
|
14124
|
-
}
|
|
14125
|
-
})().catch((err) => {
|
|
14126
|
-
console.error(`[action-completion-relay] boot-drain rejected: ${err instanceof Error ? err.message : String(err)}`);
|
|
14127
|
-
});
|
|
14128
13962
|
if (bootAccountConfig?.whatsapp) {
|
|
14129
13963
|
console.error(`[whatsapp:boot] loading whatsapp config from account.json publicAgent=${bootPublicAgent ?? "none"}`);
|
|
14130
13964
|
} else {
|
|
@@ -14132,7 +13966,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
14132
13966
|
}
|
|
14133
13967
|
init({
|
|
14134
13968
|
configDir: configDirForWhatsApp,
|
|
14135
|
-
platformRoot: resolve25(process.env.MAXY_PLATFORM_ROOT ??
|
|
13969
|
+
platformRoot: resolve25(process.env.MAXY_PLATFORM_ROOT ?? join10(__dirname, "..")),
|
|
14136
13970
|
accountConfig: bootAccountConfig,
|
|
14137
13971
|
onMessage: async (msg) => {
|
|
14138
13972
|
try {
|