@rubytech/create-realagent 1.0.807 → 1.0.809
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +2 -0
- package/payload/platform/plugins/docs/references/cloudflare.md +1 -0
- package/payload/platform/plugins/docs/references/memory-guide.md +4 -0
- package/payload/platform/plugins/docs/references/troubleshooting.md +19 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +86 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +19 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts +23 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js +401 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.d.ts +28 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.js +34 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.js.map +1 -0
- package/payload/platform/plugins/memory/references/schema-base.md +12 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +3 -1
- package/payload/platform/plugins/whatsapp-import/bin/ingest.mjs +225 -346
- package/payload/platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh +28 -10
- package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.d.ts +21 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.js +41 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.js.map +1 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/filter.d.ts +29 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/filter.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/filter.js +123 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/filter.js.map +1 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/index.d.ts +4 -0
- package/payload/platform/plugins/whatsapp-import/lib/dist/index.d.ts.map +1 -1
- package/payload/platform/plugins/whatsapp-import/lib/dist/index.js +9 -1
- package/payload/platform/plugins/whatsapp-import/lib/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/filter-gate.test.ts +170 -0
- package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/ingest-idempotence.test.ts +141 -0
- package/payload/platform/plugins/whatsapp-import/lib/src/derive-keys.ts +59 -0
- package/payload/platform/plugins/whatsapp-import/lib/src/filter.ts +136 -0
- package/payload/platform/plugins/whatsapp-import/lib/src/index.ts +12 -0
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/SKILL.md +80 -25
- package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import-enrich/SKILL.md +22 -3
- package/payload/platform/templates/agents/admin/IDENTITY.md +1 -0
- package/payload/platform/templates/agents/admin/SOUL.md +2 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +9 -4
- package/payload/server/chunk-CRWLE6BZ.js +3511 -0
- package/payload/server/chunk-V3VLAL7N.js +10009 -0
- package/payload/server/client-pool-N2Y57223.js +31 -0
- package/payload/server/maxy-edge.js +5 -4
- package/payload/server/public/assets/admin-Bwrd2DBq.js +352 -0
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +596 -250
- package/payload/server/public/assets/admin-CTM9Vb-j.js +0 -352
package/payload/server/server.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE,
|
|
7
7
|
TELEGRAM_WEBHOOK_SECRET_FILE,
|
|
8
8
|
USERS_FILE,
|
|
9
|
+
actionLogPath,
|
|
9
10
|
autoDeliverPremiumPlugins,
|
|
10
11
|
buildX11Env,
|
|
11
12
|
callOauthLlm,
|
|
@@ -29,6 +30,7 @@ import {
|
|
|
29
30
|
launchAction,
|
|
30
31
|
load,
|
|
31
32
|
logPath,
|
|
33
|
+
reconcileCloudflareSetupFromLog,
|
|
32
34
|
recordFailedAttempt,
|
|
33
35
|
render,
|
|
34
36
|
renderLoginPage,
|
|
@@ -50,7 +52,7 @@ import {
|
|
|
50
52
|
vncLog,
|
|
51
53
|
waitForExit,
|
|
52
54
|
writeChromiumWrapper
|
|
53
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-V3VLAL7N.js";
|
|
54
56
|
import {
|
|
55
57
|
ACCOUNTS_DIR,
|
|
56
58
|
GREETING_DIRECTIVE,
|
|
@@ -114,7 +116,7 @@ import {
|
|
|
114
116
|
verifyAndGetConversationUpdatedAt,
|
|
115
117
|
verifyConversationOwnership,
|
|
116
118
|
writeAdminUserAndPerson
|
|
117
|
-
} from "./chunk-
|
|
119
|
+
} from "./chunk-CRWLE6BZ.js";
|
|
118
120
|
import {
|
|
119
121
|
__commonJS,
|
|
120
122
|
__toESM
|
|
@@ -617,15 +619,15 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
617
619
|
};
|
|
618
620
|
|
|
619
621
|
// server/index.ts
|
|
620
|
-
import { readFileSync as
|
|
621
|
-
import { resolve as
|
|
622
|
+
import { readFileSync as readFileSync19, existsSync as existsSync25, watchFile } from "fs";
|
|
623
|
+
import { resolve as resolve25, join as join11, basename as basename7 } from "path";
|
|
622
624
|
import { homedir as homedir2 } from "os";
|
|
623
625
|
|
|
624
626
|
// app/lib/agent-slug-pattern.ts
|
|
625
627
|
var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
|
|
626
628
|
|
|
627
629
|
// server/routes/health.ts
|
|
628
|
-
import { existsSync as existsSync6, readFileSync as
|
|
630
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
629
631
|
import { createConnection } from "net";
|
|
630
632
|
|
|
631
633
|
// app/lib/network.ts
|
|
@@ -804,6 +806,28 @@ function defaultRules() {
|
|
|
804
806
|
scope: "session",
|
|
805
807
|
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."
|
|
806
808
|
},
|
|
809
|
+
{
|
|
810
|
+
// Task 867 — fires when setup-tunnel.sh emits step=done but no
|
|
811
|
+
// `[persist] role=user … Cloudflare setup completed (actionId: <id>)`
|
|
812
|
+
// line appears within 60s. Covers the three failure modes of the
|
|
813
|
+
// action-relay-queue plumbing: queue-write failed, boot-drain consumer
|
|
814
|
+
// skipped the record, or the agent's hoisted persist threw. The
|
|
815
|
+
// followup pattern is intentionally narrow to the cloudflare-setup
|
|
816
|
+
// relay shape (not a general "any user persist") so a benign user
|
|
817
|
+
// typing in chat does not satisfy the followup. logSource=any so
|
|
818
|
+
// the rule sees both the script tee (in stream logs / server.log)
|
|
819
|
+
// and the [persist] line (server.log).
|
|
820
|
+
id: "cloudflare-setup-relay-not-acknowledged",
|
|
821
|
+
name: "Cloudflare-setup completed but the chat relay never acknowledged",
|
|
822
|
+
type: "absent-followup",
|
|
823
|
+
logSource: "any",
|
|
824
|
+
pattern: "\\[script:setup-tunnel\\] step=done",
|
|
825
|
+
followupPattern: "\\[persist\\] .*role=user.* Cloudflare setup completed \\(actionId:",
|
|
826
|
+
followupWindowMs: 6e4,
|
|
827
|
+
thresholdCount: 0,
|
|
828
|
+
thresholdWindowMinutes: 0,
|
|
829
|
+
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."
|
|
830
|
+
},
|
|
807
831
|
{
|
|
808
832
|
// Task 538: fires when a [spawn] line appears in a conversation's stream
|
|
809
833
|
// log but no subprocess-lifecycle marker follows within 10s. The three
|
|
@@ -2812,7 +2836,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
2812
2836
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
2813
2837
|
console.error(`${TAG3} draining credential save queue\u2026`);
|
|
2814
2838
|
const timer2 = new Promise(
|
|
2815
|
-
(
|
|
2839
|
+
(resolve26) => setTimeout(() => resolve26("timeout"), timeoutMs)
|
|
2816
2840
|
);
|
|
2817
2841
|
const result = await Promise.race([
|
|
2818
2842
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -2940,11 +2964,11 @@ async function createWaSocket(opts) {
|
|
|
2940
2964
|
return sock;
|
|
2941
2965
|
}
|
|
2942
2966
|
async function waitForConnection(sock) {
|
|
2943
|
-
return new Promise((
|
|
2967
|
+
return new Promise((resolve26, reject) => {
|
|
2944
2968
|
const handler = (update) => {
|
|
2945
2969
|
if (update.connection === "open") {
|
|
2946
2970
|
sock.ev.off("connection.update", handler);
|
|
2947
|
-
|
|
2971
|
+
resolve26();
|
|
2948
2972
|
}
|
|
2949
2973
|
if (update.connection === "close") {
|
|
2950
2974
|
sock.ev.off("connection.update", handler);
|
|
@@ -3058,14 +3082,14 @@ ${inspected}`;
|
|
|
3058
3082
|
return inspect2(err, INSPECT_OPTS2);
|
|
3059
3083
|
}
|
|
3060
3084
|
function withTimeout(label, promise, timeoutMs) {
|
|
3061
|
-
return new Promise((
|
|
3085
|
+
return new Promise((resolve26, reject) => {
|
|
3062
3086
|
const timer2 = setTimeout(() => {
|
|
3063
3087
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
3064
3088
|
}, timeoutMs);
|
|
3065
3089
|
promise.then(
|
|
3066
3090
|
(value) => {
|
|
3067
3091
|
clearTimeout(timer2);
|
|
3068
|
-
|
|
3092
|
+
resolve26(value);
|
|
3069
3093
|
},
|
|
3070
3094
|
(err) => {
|
|
3071
3095
|
clearTimeout(timer2);
|
|
@@ -3586,8 +3610,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
3586
3610
|
const { givenName, familyName } = splitName(input.pushName);
|
|
3587
3611
|
const prev = sessionWriteLocks.get(input.sessionKey);
|
|
3588
3612
|
let release;
|
|
3589
|
-
const mine = new Promise((
|
|
3590
|
-
release =
|
|
3613
|
+
const mine = new Promise((resolve26) => {
|
|
3614
|
+
release = resolve26;
|
|
3591
3615
|
});
|
|
3592
3616
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
3593
3617
|
sessionWriteLocks.set(input.sessionKey, chained);
|
|
@@ -3605,7 +3629,7 @@ async function persistWhatsAppMessage(input) {
|
|
|
3605
3629
|
existingM IS NOT NULL AS messageExisted
|
|
3606
3630
|
MERGE (s:Person {telephone: $senderTelephone})
|
|
3607
3631
|
ON CREATE SET
|
|
3608
|
-
s.accountId = $
|
|
3632
|
+
s.accountId = $platformAccountId,
|
|
3609
3633
|
s.givenName = $givenName,
|
|
3610
3634
|
s.familyName = $familyName,
|
|
3611
3635
|
s.source = 'whatsapp-live',
|
|
@@ -3617,7 +3641,7 @@ async function persistWhatsAppMessage(input) {
|
|
|
3617
3641
|
MERGE (m:Message {messageId: $messageId})
|
|
3618
3642
|
ON CREATE SET
|
|
3619
3643
|
m:WhatsAppMessage,
|
|
3620
|
-
m.accountId = $
|
|
3644
|
+
m.accountId = $platformAccountId,
|
|
3621
3645
|
m.conversationId = c.conversationId,
|
|
3622
3646
|
m.source = 'whatsapp',
|
|
3623
3647
|
m.createdByAgent = 'whatsapp-live',
|
|
@@ -3663,7 +3687,7 @@ async function persistWhatsAppMessage(input) {
|
|
|
3663
3687
|
const params = {
|
|
3664
3688
|
sessionKey: input.sessionKey,
|
|
3665
3689
|
messageId,
|
|
3666
|
-
|
|
3690
|
+
platformAccountId: input.platformAccountId,
|
|
3667
3691
|
senderTelephone,
|
|
3668
3692
|
senderName: input.pushName ?? null,
|
|
3669
3693
|
givenName,
|
|
@@ -3680,7 +3704,7 @@ async function persistWhatsAppMessage(input) {
|
|
|
3680
3704
|
const result = await session.run(cypher, params);
|
|
3681
3705
|
const ms = Date.now() - t0;
|
|
3682
3706
|
if (result.records.length === 0) {
|
|
3683
|
-
console.error(`${TAG7} skip reason=conversation-not-found accountId=${input.
|
|
3707
|
+
console.error(`${TAG7} skip reason=conversation-not-found accountId=${input.platformAccountId} sessionKey=${input.sessionKey} messageId=${messageId}`);
|
|
3684
3708
|
return null;
|
|
3685
3709
|
}
|
|
3686
3710
|
const rec = result.records[0];
|
|
@@ -3701,14 +3725,14 @@ async function persistWhatsAppMessage(input) {
|
|
|
3701
3725
|
console.error(`${TAG7} next-skip reason=no-prior msgId=${messageId}`);
|
|
3702
3726
|
}
|
|
3703
3727
|
console.error(
|
|
3704
|
-
`${TAG7} write messageId=${messageId} sessionKey=${input.sessionKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} ms=${ms}`
|
|
3728
|
+
`${TAG7} write messageId=${messageId} accountId=${input.platformAccountId} sessionKey=${input.sessionKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} ms=${ms}`
|
|
3705
3729
|
);
|
|
3706
3730
|
return { messageId, created: true, senderElementId };
|
|
3707
3731
|
} catch (err) {
|
|
3708
3732
|
const ms = Date.now() - t0;
|
|
3709
3733
|
const reason = sanitizeReason(err);
|
|
3710
3734
|
console.error(
|
|
3711
|
-
`${TAG7} FAIL accountId=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
|
|
3735
|
+
`${TAG7} FAIL accountId=${input.platformAccountId} waname=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
|
|
3712
3736
|
);
|
|
3713
3737
|
return null;
|
|
3714
3738
|
} finally {
|
|
@@ -3746,7 +3770,7 @@ async function ensureWhatsAppConversation(input) {
|
|
|
3746
3770
|
const t0 = Date.now();
|
|
3747
3771
|
try {
|
|
3748
3772
|
const result = await ensureConversation(
|
|
3749
|
-
input.
|
|
3773
|
+
input.platformAccountId,
|
|
3750
3774
|
input.agentType,
|
|
3751
3775
|
input.sessionKey
|
|
3752
3776
|
);
|
|
@@ -3771,6 +3795,52 @@ async function ensureWhatsAppConversation(input) {
|
|
|
3771
3795
|
}
|
|
3772
3796
|
}
|
|
3773
3797
|
|
|
3798
|
+
// app/lib/whatsapp/platform-account-id.ts
|
|
3799
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
3800
|
+
import { resolve as resolve6 } from "path";
|
|
3801
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3802
|
+
var cached = null;
|
|
3803
|
+
var cachedAccountsDir = null;
|
|
3804
|
+
function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
|
|
3805
|
+
if (cached !== null && cachedAccountsDir === accountsDir) return cached;
|
|
3806
|
+
const valid = enumerateValidAccountIds(accountsDir);
|
|
3807
|
+
if (valid.length === 0) {
|
|
3808
|
+
throw new Error(
|
|
3809
|
+
`[whatsapp-persist] resolvePlatformAccountId: no platform account found under ${accountsDir} \u2014 corrupt install? Cannot stamp n.accountId without a valid UUID.`
|
|
3810
|
+
);
|
|
3811
|
+
}
|
|
3812
|
+
if (valid.length > 1) {
|
|
3813
|
+
throw new Error(
|
|
3814
|
+
`[whatsapp-persist] resolvePlatformAccountId: multiple platform accounts found under ${accountsDir} (${valid.join(", ")}) \u2014 Phase 0 invariant requires exactly one. Loud-fail rather than picking one silently.`
|
|
3815
|
+
);
|
|
3816
|
+
}
|
|
3817
|
+
cached = valid[0];
|
|
3818
|
+
cachedAccountsDir = accountsDir;
|
|
3819
|
+
return cached;
|
|
3820
|
+
}
|
|
3821
|
+
function enumerateValidAccountIds(accountsDir) {
|
|
3822
|
+
let names;
|
|
3823
|
+
try {
|
|
3824
|
+
names = readdirSync2(accountsDir);
|
|
3825
|
+
} catch (err) {
|
|
3826
|
+
if (err.code === "ENOENT") return [];
|
|
3827
|
+
throw err;
|
|
3828
|
+
}
|
|
3829
|
+
const valid = [];
|
|
3830
|
+
for (const name of names) {
|
|
3831
|
+
if (!UUID_RE.test(name)) continue;
|
|
3832
|
+
const configPath2 = resolve6(accountsDir, name, "account.json");
|
|
3833
|
+
try {
|
|
3834
|
+
JSON.parse(readFileSync5(configPath2, "utf-8"));
|
|
3835
|
+
valid.push(name);
|
|
3836
|
+
} catch (err) {
|
|
3837
|
+
const code = err.code;
|
|
3838
|
+
if (code === "ENOENT") continue;
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
return valid;
|
|
3842
|
+
}
|
|
3843
|
+
|
|
3774
3844
|
// app/lib/whatsapp/inbound/media.ts
|
|
3775
3845
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3776
3846
|
import { writeFile, mkdir } from "fs/promises";
|
|
@@ -4249,9 +4319,23 @@ async function startConnection(accountId) {
|
|
|
4249
4319
|
console.error(`${TAG13} no credentials for account=${accountId}`);
|
|
4250
4320
|
return;
|
|
4251
4321
|
}
|
|
4322
|
+
let platformAccountId;
|
|
4323
|
+
try {
|
|
4324
|
+
platformAccountId = resolvePlatformAccountId();
|
|
4325
|
+
console.error(
|
|
4326
|
+
`[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId}`
|
|
4327
|
+
);
|
|
4328
|
+
} catch (err) {
|
|
4329
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
4330
|
+
console.error(
|
|
4331
|
+
`[whatsapp-persist] resolved-account-id FAIL waname=${accountId} reason=${reason}`
|
|
4332
|
+
);
|
|
4333
|
+
throw err;
|
|
4334
|
+
}
|
|
4252
4335
|
await stopConnection(accountId);
|
|
4253
4336
|
const conn = {
|
|
4254
4337
|
accountId,
|
|
4338
|
+
platformAccountId,
|
|
4255
4339
|
accountName: whatsAppConfig.accounts?.[accountId]?.name,
|
|
4256
4340
|
authDir,
|
|
4257
4341
|
sock: null,
|
|
@@ -4306,11 +4390,25 @@ function getSocket(accountId) {
|
|
|
4306
4390
|
}
|
|
4307
4391
|
async function registerLoginSocket(accountId, sock, authDir) {
|
|
4308
4392
|
if (!configDir) throw new Error("WhatsApp manager not initialized");
|
|
4393
|
+
let platformAccountId;
|
|
4394
|
+
try {
|
|
4395
|
+
platformAccountId = resolvePlatformAccountId();
|
|
4396
|
+
console.error(
|
|
4397
|
+
`[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId}`
|
|
4398
|
+
);
|
|
4399
|
+
} catch (err) {
|
|
4400
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
4401
|
+
console.error(
|
|
4402
|
+
`[whatsapp-persist] resolved-account-id FAIL waname=${accountId} reason=${reason}`
|
|
4403
|
+
);
|
|
4404
|
+
throw err;
|
|
4405
|
+
}
|
|
4309
4406
|
await stopConnection(accountId);
|
|
4310
4407
|
const selfId = readSelfId(authDir);
|
|
4311
4408
|
const lidMapping = sock.signalRepository?.lidMapping ?? null;
|
|
4312
4409
|
const conn = {
|
|
4313
4410
|
accountId,
|
|
4411
|
+
platformAccountId,
|
|
4314
4412
|
accountName: whatsAppConfig.accounts?.[accountId]?.name,
|
|
4315
4413
|
authDir,
|
|
4316
4414
|
sock,
|
|
@@ -4476,11 +4574,11 @@ async function connectWithReconnect(conn) {
|
|
|
4476
4574
|
console.error(
|
|
4477
4575
|
`${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
4478
4576
|
);
|
|
4479
|
-
await new Promise((
|
|
4480
|
-
const timer2 = setTimeout(
|
|
4577
|
+
await new Promise((resolve26) => {
|
|
4578
|
+
const timer2 = setTimeout(resolve26, delay);
|
|
4481
4579
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
4482
4580
|
clearTimeout(timer2);
|
|
4483
|
-
|
|
4581
|
+
resolve26();
|
|
4484
4582
|
}, { once: true });
|
|
4485
4583
|
});
|
|
4486
4584
|
}
|
|
@@ -4488,16 +4586,16 @@ async function connectWithReconnect(conn) {
|
|
|
4488
4586
|
}
|
|
4489
4587
|
}
|
|
4490
4588
|
function waitForDisconnectEvent(conn) {
|
|
4491
|
-
return new Promise((
|
|
4589
|
+
return new Promise((resolve26) => {
|
|
4492
4590
|
if (!conn.sock) {
|
|
4493
|
-
|
|
4591
|
+
resolve26();
|
|
4494
4592
|
return;
|
|
4495
4593
|
}
|
|
4496
4594
|
const sock = conn.sock;
|
|
4497
4595
|
const handler = (update) => {
|
|
4498
4596
|
if (update.connection === "close") {
|
|
4499
4597
|
sock.ev.off("connection.update", handler);
|
|
4500
|
-
|
|
4598
|
+
resolve26();
|
|
4501
4599
|
}
|
|
4502
4600
|
};
|
|
4503
4601
|
sock.ev.on("connection.update", handler);
|
|
@@ -4518,8 +4616,8 @@ function watchForDisconnect(conn) {
|
|
|
4518
4616
|
});
|
|
4519
4617
|
}
|
|
4520
4618
|
async function getGroupMeta(conn, jid) {
|
|
4521
|
-
const
|
|
4522
|
-
if (
|
|
4619
|
+
const cached2 = conn.groupMetaCache.get(jid);
|
|
4620
|
+
if (cached2 && cached2.expires > Date.now()) return cached2;
|
|
4523
4621
|
if (!conn.sock) return null;
|
|
4524
4622
|
console.error(`${TAG13} group metadata cache miss for ${jid}, fetching from Baileys account=${conn.accountId}`);
|
|
4525
4623
|
try {
|
|
@@ -4631,6 +4729,7 @@ function monitorInbound(conn) {
|
|
|
4631
4729
|
if (msg.key.id) {
|
|
4632
4730
|
const merged = await ensureWhatsAppConversation({
|
|
4633
4731
|
accountId: conn.accountId,
|
|
4732
|
+
platformAccountId: conn.platformAccountId,
|
|
4634
4733
|
sessionKey,
|
|
4635
4734
|
agentType: sessionKeyAgentType,
|
|
4636
4735
|
groupJid: isGroup ? remoteJid : void 0
|
|
@@ -4638,6 +4737,7 @@ function monitorInbound(conn) {
|
|
|
4638
4737
|
if (merged) {
|
|
4639
4738
|
await persistWhatsAppMessage({
|
|
4640
4739
|
accountId: conn.accountId,
|
|
4740
|
+
platformAccountId: conn.platformAccountId,
|
|
4641
4741
|
remoteJid,
|
|
4642
4742
|
sessionKey,
|
|
4643
4743
|
msgKeyId: msg.key.id,
|
|
@@ -4756,8 +4856,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4756
4856
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
4757
4857
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
4758
4858
|
let resolvePending;
|
|
4759
|
-
const sttPending = new Promise((
|
|
4760
|
-
resolvePending =
|
|
4859
|
+
const sttPending = new Promise((resolve26) => {
|
|
4860
|
+
resolvePending = resolve26;
|
|
4761
4861
|
});
|
|
4762
4862
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
4763
4863
|
try {
|
|
@@ -4870,20 +4970,20 @@ async function probeApiKey() {
|
|
|
4870
4970
|
return result.status;
|
|
4871
4971
|
}
|
|
4872
4972
|
function checkPort(port2, timeoutMs = 500) {
|
|
4873
|
-
return new Promise((
|
|
4973
|
+
return new Promise((resolve26) => {
|
|
4874
4974
|
const socket = createConnection(port2, "127.0.0.1");
|
|
4875
4975
|
socket.setTimeout(timeoutMs);
|
|
4876
4976
|
socket.once("connect", () => {
|
|
4877
4977
|
socket.destroy();
|
|
4878
|
-
|
|
4978
|
+
resolve26(true);
|
|
4879
4979
|
});
|
|
4880
4980
|
socket.once("error", () => {
|
|
4881
4981
|
socket.destroy();
|
|
4882
|
-
|
|
4982
|
+
resolve26(false);
|
|
4883
4983
|
});
|
|
4884
4984
|
socket.once("timeout", () => {
|
|
4885
4985
|
socket.destroy();
|
|
4886
|
-
|
|
4986
|
+
resolve26(false);
|
|
4887
4987
|
});
|
|
4888
4988
|
});
|
|
4889
4989
|
}
|
|
@@ -4893,7 +4993,7 @@ app.get("/", async (c) => {
|
|
|
4893
4993
|
let pinConfigured = false;
|
|
4894
4994
|
try {
|
|
4895
4995
|
if (existsSync6(USERS_FILE)) {
|
|
4896
|
-
const raw =
|
|
4996
|
+
const raw = readFileSync6(USERS_FILE, "utf-8").trim();
|
|
4897
4997
|
if (raw) {
|
|
4898
4998
|
const users = JSON.parse(raw);
|
|
4899
4999
|
pinConfigured = Array.isArray(users) && users.length > 0;
|
|
@@ -4978,14 +5078,14 @@ app.get("/", async (c) => {
|
|
|
4978
5078
|
var health_default = app;
|
|
4979
5079
|
|
|
4980
5080
|
// server/routes/session.ts
|
|
4981
|
-
import { resolve as
|
|
5081
|
+
import { resolve as resolve7 } from "path";
|
|
4982
5082
|
import { existsSync as existsSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "fs";
|
|
4983
|
-
var
|
|
5083
|
+
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4984
5084
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
4985
5085
|
try {
|
|
4986
|
-
const cacheDir =
|
|
5086
|
+
const cacheDir = resolve7(MAXY_DIR, "branding-cache", accountId);
|
|
4987
5087
|
mkdirSync4(cacheDir, { recursive: true });
|
|
4988
|
-
writeFileSync5(
|
|
5088
|
+
writeFileSync5(resolve7(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
4989
5089
|
} catch (err) {
|
|
4990
5090
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4991
5091
|
}
|
|
@@ -4995,7 +5095,7 @@ function parseVisitorCookie(cookieHeader) {
|
|
|
4995
5095
|
const match = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
|
|
4996
5096
|
if (!match) return null;
|
|
4997
5097
|
const value = decodeURIComponent(match[1]).trim();
|
|
4998
|
-
return
|
|
5098
|
+
return UUID_RE2.test(value) ? value : null;
|
|
4999
5099
|
}
|
|
5000
5100
|
function withVisitorCookie(response, visitorId) {
|
|
5001
5101
|
if (!visitorId) return response;
|
|
@@ -5055,8 +5155,8 @@ app2.post("/", async (c) => {
|
|
|
5055
5155
|
}
|
|
5056
5156
|
let agentConfig = null;
|
|
5057
5157
|
if (account) {
|
|
5058
|
-
const agentDir =
|
|
5059
|
-
const agentConfigPath =
|
|
5158
|
+
const agentDir = resolve7(account.accountDir, "agents", agentSlug);
|
|
5159
|
+
const agentConfigPath = resolve7(agentDir, "config.json");
|
|
5060
5160
|
if (!existsSync7(agentDir) || !existsSync7(agentConfigPath)) {
|
|
5061
5161
|
return c.json({ error: "Agent not found" }, 404);
|
|
5062
5162
|
}
|
|
@@ -5310,9 +5410,9 @@ ${raw}`;
|
|
|
5310
5410
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5311
5411
|
import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
5312
5412
|
import { realpathSync } from "fs";
|
|
5313
|
-
import { resolve as
|
|
5314
|
-
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
5315
|
-
var ATTACHMENTS_ROOT =
|
|
5413
|
+
import { resolve as resolve8, extname, basename as basename3 } from "path";
|
|
5414
|
+
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve8(process.cwd(), "../platform");
|
|
5415
|
+
var ATTACHMENTS_ROOT = resolve8(PLATFORM_ROOT2, "..", "data/uploads");
|
|
5316
5416
|
var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
5317
5417
|
"image/jpeg",
|
|
5318
5418
|
"image/png",
|
|
@@ -5339,11 +5439,11 @@ function assertSupportedMime(mimeType) {
|
|
|
5339
5439
|
}
|
|
5340
5440
|
async function writeAttachment(scope, filename, mimeType, sizeBytes, buffer) {
|
|
5341
5441
|
const attachmentId = randomUUID4();
|
|
5342
|
-
const dir =
|
|
5442
|
+
const dir = resolve8(ATTACHMENTS_ROOT, scope, attachmentId);
|
|
5343
5443
|
await mkdir2(dir, { recursive: true });
|
|
5344
5444
|
const ext = extname(filename) || "";
|
|
5345
|
-
const storagePath =
|
|
5346
|
-
const metaPath =
|
|
5445
|
+
const storagePath = resolve8(dir, `${attachmentId}${ext}`);
|
|
5446
|
+
const metaPath = resolve8(dir, `${attachmentId}.meta.json`);
|
|
5347
5447
|
const meta = {
|
|
5348
5448
|
attachmentId,
|
|
5349
5449
|
scope,
|
|
@@ -6021,16 +6121,16 @@ var group_default = app4;
|
|
|
6021
6121
|
|
|
6022
6122
|
// app/lib/access-gate.ts
|
|
6023
6123
|
import neo4j from "neo4j-driver";
|
|
6024
|
-
import { readFileSync as
|
|
6025
|
-
import { resolve as
|
|
6124
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
6125
|
+
import { resolve as resolve9 } from "path";
|
|
6026
6126
|
import { randomUUID as randomUUID5, randomInt } from "crypto";
|
|
6027
|
-
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ??
|
|
6127
|
+
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve9(process.cwd(), "..");
|
|
6028
6128
|
var driver = null;
|
|
6029
6129
|
function readPassword() {
|
|
6030
6130
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
6031
|
-
const passwordFile =
|
|
6131
|
+
const passwordFile = resolve9(PLATFORM_ROOT3, "config/.neo4j-password");
|
|
6032
6132
|
try {
|
|
6033
|
-
return
|
|
6133
|
+
return readFileSync7(passwordFile, "utf-8").trim();
|
|
6034
6134
|
} catch {
|
|
6035
6135
|
throw new Error(
|
|
6036
6136
|
`Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
|
|
@@ -6341,19 +6441,19 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
|
|
|
6341
6441
|
}
|
|
6342
6442
|
|
|
6343
6443
|
// app/lib/brevo-sms.ts
|
|
6344
|
-
import { readFileSync as
|
|
6444
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync8, chmodSync } from "fs";
|
|
6345
6445
|
import { dirname as dirname4 } from "path";
|
|
6346
|
-
import { resolve as
|
|
6347
|
-
var BREVO_API_KEY_FILE =
|
|
6446
|
+
import { resolve as resolve10 } from "path";
|
|
6447
|
+
var BREVO_API_KEY_FILE = resolve10(MAXY_DIR, ".brevo-api-key");
|
|
6348
6448
|
var BREVO_API_URL = "https://api.brevo.com/v3/transactionalSMS/sms";
|
|
6349
6449
|
var BREVO_TIMEOUT_MS = 1e4;
|
|
6350
6450
|
var BREVO_SENDER = "Maxy";
|
|
6351
6451
|
var platformRoot = process.env.MAXY_PLATFORM_ROOT;
|
|
6352
6452
|
if (platformRoot) {
|
|
6353
6453
|
try {
|
|
6354
|
-
const brandPath =
|
|
6454
|
+
const brandPath = resolve10(platformRoot, "config", "brand.json");
|
|
6355
6455
|
if (existsSync8(brandPath)) {
|
|
6356
|
-
const brand = JSON.parse(
|
|
6456
|
+
const brand = JSON.parse(readFileSync8(brandPath, "utf-8"));
|
|
6357
6457
|
if (brand.productName) BREVO_SENDER = brand.productName;
|
|
6358
6458
|
}
|
|
6359
6459
|
} catch {
|
|
@@ -6361,7 +6461,7 @@ if (platformRoot) {
|
|
|
6361
6461
|
}
|
|
6362
6462
|
function readBrevoApiKey() {
|
|
6363
6463
|
try {
|
|
6364
|
-
const key =
|
|
6464
|
+
const key = readFileSync8(BREVO_API_KEY_FILE, "utf-8").trim();
|
|
6365
6465
|
if (!key) {
|
|
6366
6466
|
throw new Error(`Brevo API key file is empty: ${BREVO_API_KEY_FILE}`);
|
|
6367
6467
|
}
|
|
@@ -6792,7 +6892,7 @@ app5.post("/send-otp", async (c) => {
|
|
|
6792
6892
|
var access_default = app5;
|
|
6793
6893
|
|
|
6794
6894
|
// server/routes/telegram.ts
|
|
6795
|
-
import { existsSync as existsSync9, readFileSync as
|
|
6895
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
|
|
6796
6896
|
import { timingSafeEqual } from "crypto";
|
|
6797
6897
|
|
|
6798
6898
|
// app/lib/telegram/access-control.ts
|
|
@@ -6830,7 +6930,7 @@ function getWebhookSecret(botType) {
|
|
|
6830
6930
|
const filePath = botType === "admin" ? TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE : TELEGRAM_WEBHOOK_SECRET_FILE;
|
|
6831
6931
|
try {
|
|
6832
6932
|
if (!existsSync9(filePath)) return null;
|
|
6833
|
-
const secret =
|
|
6933
|
+
const secret = readFileSync9(filePath, "utf-8").trim();
|
|
6834
6934
|
return secret || null;
|
|
6835
6935
|
} catch {
|
|
6836
6936
|
return null;
|
|
@@ -6988,9 +7088,9 @@ app6.post("/webhook", async (c) => {
|
|
|
6988
7088
|
var telegram_default = app6;
|
|
6989
7089
|
|
|
6990
7090
|
// server/routes/whatsapp.ts
|
|
6991
|
-
import { join as join6, resolve as
|
|
7091
|
+
import { join as join6, resolve as resolve11, basename as basename4 } from "path";
|
|
6992
7092
|
import { readFile as readFile2, stat as stat3 } from "fs/promises";
|
|
6993
|
-
import { realpathSync as realpathSync2, readdirSync as
|
|
7093
|
+
import { realpathSync as realpathSync2, readdirSync as readdirSync3, readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
|
|
6994
7094
|
|
|
6995
7095
|
// app/lib/whatsapp/login.ts
|
|
6996
7096
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
@@ -7096,8 +7196,8 @@ async function startLogin(opts) {
|
|
|
7096
7196
|
resetActiveLogin(accountId);
|
|
7097
7197
|
let resolveQr = null;
|
|
7098
7198
|
let rejectQr = null;
|
|
7099
|
-
const qrPromise = new Promise((
|
|
7100
|
-
resolveQr =
|
|
7199
|
+
const qrPromise = new Promise((resolve26, reject) => {
|
|
7200
|
+
resolveQr = resolve26;
|
|
7101
7201
|
rejectQr = reject;
|
|
7102
7202
|
});
|
|
7103
7203
|
const qrTimer = setTimeout(
|
|
@@ -7491,17 +7591,17 @@ app7.post("/config", async (c) => {
|
|
|
7491
7591
|
return c.json(result, result.ok ? 200 : 400);
|
|
7492
7592
|
}
|
|
7493
7593
|
case "list-public-agents": {
|
|
7494
|
-
const agentsDir =
|
|
7594
|
+
const agentsDir = resolve11(account.accountDir, "agents");
|
|
7495
7595
|
const agents = [];
|
|
7496
7596
|
if (existsSync10(agentsDir)) {
|
|
7497
7597
|
try {
|
|
7498
|
-
const entries =
|
|
7598
|
+
const entries = readdirSync3(agentsDir, { withFileTypes: true });
|
|
7499
7599
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
7500
7600
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
7501
|
-
const configPath2 =
|
|
7601
|
+
const configPath2 = resolve11(agentsDir, entry.name, "config.json");
|
|
7502
7602
|
if (!existsSync10(configPath2)) continue;
|
|
7503
7603
|
try {
|
|
7504
|
-
const config = JSON.parse(
|
|
7604
|
+
const config = JSON.parse(readFileSync10(configPath2, "utf-8"));
|
|
7505
7605
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
7506
7606
|
} catch {
|
|
7507
7607
|
console.error(`${TAG18} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
@@ -7576,7 +7676,7 @@ app7.post("/send-document", async (c) => {
|
|
|
7576
7676
|
if (!maxyAccountId || !PLATFORM_ROOT4) {
|
|
7577
7677
|
return c.json({ error: "Cannot validate file path: missing account or platform context" }, 400);
|
|
7578
7678
|
}
|
|
7579
|
-
const accountDir =
|
|
7679
|
+
const accountDir = resolve11(PLATFORM_ROOT4, "..", "data/accounts", maxyAccountId);
|
|
7580
7680
|
let resolvedPath;
|
|
7581
7681
|
try {
|
|
7582
7682
|
resolvedPath = realpathSync2(filePath);
|
|
@@ -7713,8 +7813,8 @@ var whatsapp_default = app7;
|
|
|
7713
7813
|
|
|
7714
7814
|
// server/routes/onboarding.ts
|
|
7715
7815
|
import { spawn, execFileSync } from "child_process";
|
|
7716
|
-
import { openSync as openSync2, closeSync as closeSync2, writeFileSync as writeFileSync7, writeSync, existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as
|
|
7717
|
-
import { resolve as
|
|
7816
|
+
import { openSync as openSync2, closeSync as closeSync2, writeFileSync as writeFileSync7, writeSync, existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync11, unlinkSync } from "fs";
|
|
7817
|
+
import { resolve as resolve12, dirname as dirname5 } from "path";
|
|
7718
7818
|
import { createHash, randomUUID as randomUUID7 } from "crypto";
|
|
7719
7819
|
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
7720
7820
|
function hashPin(pin) {
|
|
@@ -7722,7 +7822,7 @@ function hashPin(pin) {
|
|
|
7722
7822
|
}
|
|
7723
7823
|
function readUsersFile() {
|
|
7724
7824
|
if (!existsSync11(USERS_FILE)) return null;
|
|
7725
|
-
const raw =
|
|
7825
|
+
const raw = readFileSync11(USERS_FILE, "utf-8").trim();
|
|
7726
7826
|
if (!raw) return [];
|
|
7727
7827
|
return JSON.parse(raw);
|
|
7728
7828
|
}
|
|
@@ -7843,7 +7943,7 @@ app8.post("/set-pin", async (c) => {
|
|
|
7843
7943
|
console.log(`[set-pin] wrote users.json: userId=${userId.slice(0, 8)}\u2026 hash=${hash.slice(0, 8)}\u2026`);
|
|
7844
7944
|
if (account) {
|
|
7845
7945
|
try {
|
|
7846
|
-
const config = JSON.parse(
|
|
7946
|
+
const config = JSON.parse(readFileSync11(`${account.accountDir}/account.json`, "utf-8"));
|
|
7847
7947
|
if (!config.admins) config.admins = [];
|
|
7848
7948
|
if (!config.admins.some((a) => a.userId === userId)) {
|
|
7849
7949
|
config.admins.push({ userId, role: "owner" });
|
|
@@ -7919,16 +8019,16 @@ app8.post("/skip", async (c) => {
|
|
|
7919
8019
|
}
|
|
7920
8020
|
const { accountId, accountDir } = account;
|
|
7921
8021
|
let agentName = "Maxy";
|
|
7922
|
-
const brandPath = PLATFORM_ROOT5 ?
|
|
8022
|
+
const brandPath = PLATFORM_ROOT5 ? resolve12(PLATFORM_ROOT5, "config", "brand.json") : "";
|
|
7923
8023
|
if (brandPath && existsSync11(brandPath)) {
|
|
7924
8024
|
try {
|
|
7925
|
-
const brand = JSON.parse(
|
|
8025
|
+
const brand = JSON.parse(readFileSync11(brandPath, "utf-8"));
|
|
7926
8026
|
if (brand.productName) agentName = brand.productName;
|
|
7927
8027
|
} catch (err) {
|
|
7928
8028
|
console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7929
8029
|
}
|
|
7930
8030
|
}
|
|
7931
|
-
const soulPath =
|
|
8031
|
+
const soulPath = resolve12(accountDir, "agents", "admin", "SOUL.md");
|
|
7932
8032
|
try {
|
|
7933
8033
|
mkdirSync6(dirname5(soulPath), { recursive: true });
|
|
7934
8034
|
writeFileSync7(soulPath, `You are ${agentName}, an AI operations manager.
|
|
@@ -8070,7 +8170,8 @@ app9.post("/", async (c) => {
|
|
|
8070
8170
|
"unknown",
|
|
8071
8171
|
"graph-labels-in-graph",
|
|
8072
8172
|
"graph-default-view",
|
|
8073
|
-
"graph-nav"
|
|
8173
|
+
"graph-nav",
|
|
8174
|
+
"event"
|
|
8074
8175
|
]);
|
|
8075
8176
|
const kind = allowedKinds.has(kindRaw) ? kindRaw : "unknown";
|
|
8076
8177
|
const msg = truncate(body.msg, MAX_MSG_LEN);
|
|
@@ -8084,36 +8185,56 @@ app9.post("/", async (c) => {
|
|
|
8084
8185
|
const stackTrunc = truncate(body.stack, MAX_STACK_LEN);
|
|
8085
8186
|
const head = stackHead(body.stack);
|
|
8086
8187
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8188
|
+
if (kind === "event") {
|
|
8189
|
+
const source = typeof body.source === "string" ? truncate(body.source, 64) : "unknown";
|
|
8190
|
+
const extra = Object.entries(body).filter(([k]) => !["kind", "source", "msg", "url", "ua", "version", "stack", "filename", "lineno", "colno", "tag", "status"].includes(k)).map(([k, v]) => {
|
|
8191
|
+
const safe = typeof v === "string" ? truncate(v, 200) : typeof v === "number" || typeof v === "boolean" ? String(v) : JSON.stringify(v).slice(0, 200);
|
|
8192
|
+
return `${k}=${safe}`;
|
|
8193
|
+
}).join(" ");
|
|
8194
|
+
const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set(["admin-chat-relay-poll"]);
|
|
8195
|
+
if (TAGGED_PREFIX_SOURCES.has(source)) {
|
|
8196
|
+
console.log(
|
|
8197
|
+
`[${source}] ts=${ts} ip=${ip} version=${version || "unknown"}${extra ? " " + extra : ""}`
|
|
8198
|
+
);
|
|
8199
|
+
} else {
|
|
8200
|
+
console.log(
|
|
8201
|
+
`[client-event] ts=${ts} ip=${ip} source=${source} version=${version || "unknown"}${extra ? " " + extra : ""}`
|
|
8202
|
+
);
|
|
8203
|
+
}
|
|
8204
|
+
} else {
|
|
8205
|
+
console.error(
|
|
8206
|
+
`[client-error] ts=${ts} ip=${ip} kind=${kind} url=${JSON.stringify(url)} version=${version || "unknown"} ua=${JSON.stringify(ua)} msg=${JSON.stringify(msg)} stack-head=${JSON.stringify(head)} file-line-col=${JSON.stringify(fileLineCol)}`
|
|
8207
|
+
);
|
|
8208
|
+
}
|
|
8209
|
+
if (kind !== "event") {
|
|
8210
|
+
rotateIfNeeded();
|
|
8211
|
+
try {
|
|
8212
|
+
const payload = {
|
|
8213
|
+
ts,
|
|
8214
|
+
ip,
|
|
8215
|
+
kind,
|
|
8216
|
+
url,
|
|
8217
|
+
version,
|
|
8218
|
+
ua,
|
|
8219
|
+
msg,
|
|
8220
|
+
stack: stackTrunc,
|
|
8221
|
+
filename: file,
|
|
8222
|
+
lineno: line,
|
|
8223
|
+
colno: col,
|
|
8224
|
+
tag: typeof body.tag === "string" ? truncate(body.tag, 32) : void 0,
|
|
8225
|
+
status: typeof body.status === "number" ? body.status : void 0
|
|
8226
|
+
};
|
|
8227
|
+
appendFileSync2(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
|
|
8228
|
+
} catch (err) {
|
|
8229
|
+
console.error(`[client-error] append failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8230
|
+
}
|
|
8110
8231
|
}
|
|
8111
8232
|
return c.json({ ok: true });
|
|
8112
8233
|
});
|
|
8113
8234
|
var client_error_default = app9;
|
|
8114
8235
|
|
|
8115
8236
|
// server/routes/admin/session.ts
|
|
8116
|
-
import { readFileSync as
|
|
8237
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync13 } from "fs";
|
|
8117
8238
|
import { createHash as createHash2 } from "crypto";
|
|
8118
8239
|
var deprecationLogged = /* @__PURE__ */ new Set();
|
|
8119
8240
|
function hashPin2(pin) {
|
|
@@ -8121,7 +8242,7 @@ function hashPin2(pin) {
|
|
|
8121
8242
|
}
|
|
8122
8243
|
function readUsersFile2() {
|
|
8123
8244
|
if (!existsSync13(USERS_FILE)) return null;
|
|
8124
|
-
const raw =
|
|
8245
|
+
const raw = readFileSync12(USERS_FILE, "utf-8").trim();
|
|
8125
8246
|
if (!raw) return [];
|
|
8126
8247
|
return JSON.parse(raw);
|
|
8127
8248
|
}
|
|
@@ -8295,7 +8416,7 @@ app10.post("/", async (c) => {
|
|
|
8295
8416
|
var session_default2 = app10;
|
|
8296
8417
|
|
|
8297
8418
|
// server/routes/admin/chat.ts
|
|
8298
|
-
import { resolve as
|
|
8419
|
+
import { resolve as resolve13 } from "path";
|
|
8299
8420
|
import { appendFileSync as appendFileSync4 } from "fs";
|
|
8300
8421
|
|
|
8301
8422
|
// app/lib/script-stream-tailer.ts
|
|
@@ -8586,7 +8707,7 @@ var app11 = new Hono();
|
|
|
8586
8707
|
app11.post("/cancel", requireAdminSession, async (c) => {
|
|
8587
8708
|
const session_key = c.var.sessionKey;
|
|
8588
8709
|
try {
|
|
8589
|
-
const { interruptClient: interruptClient2 } = await import("./client-pool-
|
|
8710
|
+
const { interruptClient: interruptClient2 } = await import("./client-pool-N2Y57223.js");
|
|
8590
8711
|
await interruptClient2(session_key);
|
|
8591
8712
|
return c.json({ ok: true });
|
|
8592
8713
|
} catch (err) {
|
|
@@ -8750,7 +8871,7 @@ app11.post("/", requireAdminSession, async (c) => {
|
|
|
8750
8871
|
function resolveTeeStreamLogPath() {
|
|
8751
8872
|
const liveConvId = getConversationIdForSession(session_key);
|
|
8752
8873
|
const key = liveConvId ?? preflushStreamLogKey(session_key);
|
|
8753
|
-
return
|
|
8874
|
+
return resolve13(account.accountDir, "logs", `claude-agent-stream-${key}.log`);
|
|
8754
8875
|
}
|
|
8755
8876
|
try {
|
|
8756
8877
|
appendFileSync4(resolveTeeStreamLogPath(), `[${(/* @__PURE__ */ new Date()).toISOString()}] [chat-route-version=task606-tee-path-resolve] sessionKey=${session_key.slice(0, 12)}\u2026
|
|
@@ -8852,7 +8973,7 @@ app11.post("/", requireAdminSession, async (c) => {
|
|
|
8852
8973
|
try {
|
|
8853
8974
|
registerAdminSSE(sseEntry);
|
|
8854
8975
|
if (sseConvId) {
|
|
8855
|
-
const streamLogPath =
|
|
8976
|
+
const streamLogPath = resolve13(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
|
|
8856
8977
|
tailer = startScriptStreamTailer({
|
|
8857
8978
|
path: streamLogPath,
|
|
8858
8979
|
onEvent: (event) => {
|
|
@@ -8981,8 +9102,8 @@ app12.post("/", requireAdminSession, async (c) => {
|
|
|
8981
9102
|
var compact_default = app12;
|
|
8982
9103
|
|
|
8983
9104
|
// server/routes/admin/logs.ts
|
|
8984
|
-
import { existsSync as existsSync15, readdirSync as
|
|
8985
|
-
import { resolve as
|
|
9105
|
+
import { existsSync as existsSync15, readdirSync as readdirSync4, readFileSync as readFileSync13, statSync as statSync7 } from "fs";
|
|
9106
|
+
import { resolve as resolve14, basename as basename5 } from "path";
|
|
8986
9107
|
|
|
8987
9108
|
// app/lib/logs-read-resolve.ts
|
|
8988
9109
|
import { existsSync as existsSync14 } from "fs";
|
|
@@ -9026,7 +9147,7 @@ app13.get("/", async (c) => {
|
|
|
9026
9147
|
const sessionKeyParam = c.req.query("sessionKey");
|
|
9027
9148
|
const download = c.req.query("download") === "1";
|
|
9028
9149
|
const account = resolveAccount();
|
|
9029
|
-
const accountLogDir2 = account ?
|
|
9150
|
+
const accountLogDir2 = account ? resolve14(account.accountDir, "logs") : null;
|
|
9030
9151
|
const logDirs = [];
|
|
9031
9152
|
if (accountLogDir2) logDirs.push(accountLogDir2);
|
|
9032
9153
|
logDirs.push(LOG_DIR);
|
|
@@ -9034,10 +9155,10 @@ app13.get("/", async (c) => {
|
|
|
9034
9155
|
const safe = basename5(fileParam);
|
|
9035
9156
|
const searched = [];
|
|
9036
9157
|
for (const dir of logDirs) {
|
|
9037
|
-
const filePath =
|
|
9158
|
+
const filePath = resolve14(dir, safe);
|
|
9038
9159
|
searched.push(filePath);
|
|
9039
9160
|
try {
|
|
9040
|
-
const buffer =
|
|
9161
|
+
const buffer = readFileSync13(filePath);
|
|
9041
9162
|
const onDiskBytes = statSync7(filePath).size;
|
|
9042
9163
|
const headers = {
|
|
9043
9164
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -9112,7 +9233,7 @@ app13.get("/", async (c) => {
|
|
|
9112
9233
|
try {
|
|
9113
9234
|
const filename = basename5(hit.path);
|
|
9114
9235
|
if (stalePreflushCount > 0 && !download) {
|
|
9115
|
-
const content =
|
|
9236
|
+
const content = readFileSync13(hit.path, "utf-8");
|
|
9116
9237
|
return c.json({
|
|
9117
9238
|
log: content,
|
|
9118
9239
|
filename,
|
|
@@ -9120,7 +9241,7 @@ app13.get("/", async (c) => {
|
|
|
9120
9241
|
warnings: stalePreflushPaths.map((path2) => ({ kind: "stale-preflush", path: path2 }))
|
|
9121
9242
|
});
|
|
9122
9243
|
}
|
|
9123
|
-
const buffer =
|
|
9244
|
+
const buffer = readFileSync13(hit.path);
|
|
9124
9245
|
const onDiskBytes = statSync7(hit.path).size;
|
|
9125
9246
|
const headers = {
|
|
9126
9247
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -9157,16 +9278,16 @@ app13.get("/", async (c) => {
|
|
|
9157
9278
|
if (!existsSync15(dir)) continue;
|
|
9158
9279
|
let files;
|
|
9159
9280
|
try {
|
|
9160
|
-
files =
|
|
9281
|
+
files = readdirSync4(dir).filter((f) => f.endsWith(".log"));
|
|
9161
9282
|
} catch (err) {
|
|
9162
9283
|
const reason = err instanceof Error ? err.message : String(err);
|
|
9163
9284
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
9164
9285
|
continue;
|
|
9165
9286
|
}
|
|
9166
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync7(
|
|
9287
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync7(resolve14(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
9167
9288
|
seen.add(name);
|
|
9168
9289
|
try {
|
|
9169
|
-
const content =
|
|
9290
|
+
const content = readFileSync13(resolve14(dir, name));
|
|
9170
9291
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
9171
9292
|
logs[name] = tail.trim() || "(empty)";
|
|
9172
9293
|
} catch (err) {
|
|
@@ -9206,7 +9327,7 @@ var claude_info_default = app14;
|
|
|
9206
9327
|
// server/routes/admin/attachment.ts
|
|
9207
9328
|
import { readFile as readFile3, readdir } from "fs/promises";
|
|
9208
9329
|
import { existsSync as existsSync16 } from "fs";
|
|
9209
|
-
import { resolve as
|
|
9330
|
+
import { resolve as resolve15 } from "path";
|
|
9210
9331
|
var app15 = new Hono();
|
|
9211
9332
|
app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
9212
9333
|
const attachmentId = c.req.param("attachmentId");
|
|
@@ -9218,11 +9339,11 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
9218
9339
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
|
|
9219
9340
|
return new Response("Not found", { status: 404 });
|
|
9220
9341
|
}
|
|
9221
|
-
const dir =
|
|
9342
|
+
const dir = resolve15(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
9222
9343
|
if (!existsSync16(dir)) {
|
|
9223
9344
|
return new Response("Not found", { status: 404 });
|
|
9224
9345
|
}
|
|
9225
|
-
const metaPath =
|
|
9346
|
+
const metaPath = resolve15(dir, `${attachmentId}.meta.json`);
|
|
9226
9347
|
if (!existsSync16(metaPath)) {
|
|
9227
9348
|
return new Response("Not found", { status: 404 });
|
|
9228
9349
|
}
|
|
@@ -9237,7 +9358,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
9237
9358
|
if (!dataFile) {
|
|
9238
9359
|
return new Response("Not found", { status: 404 });
|
|
9239
9360
|
}
|
|
9240
|
-
const filePath =
|
|
9361
|
+
const filePath = resolve15(dir, dataFile);
|
|
9241
9362
|
const buffer = await readFile3(filePath);
|
|
9242
9363
|
return new Response(new Uint8Array(buffer), {
|
|
9243
9364
|
headers: {
|
|
@@ -9250,24 +9371,24 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
9250
9371
|
var attachment_default = app15;
|
|
9251
9372
|
|
|
9252
9373
|
// server/routes/admin/agents.ts
|
|
9253
|
-
import { resolve as
|
|
9254
|
-
import { readdirSync as
|
|
9374
|
+
import { resolve as resolve16 } from "path";
|
|
9375
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync14, existsSync as existsSync17, rmSync } from "fs";
|
|
9255
9376
|
var app16 = new Hono();
|
|
9256
9377
|
app16.get("/", (c) => {
|
|
9257
9378
|
const account = resolveAccount();
|
|
9258
9379
|
if (!account) return c.json({ agents: [] });
|
|
9259
|
-
const agentsDir =
|
|
9380
|
+
const agentsDir = resolve16(account.accountDir, "agents");
|
|
9260
9381
|
if (!existsSync17(agentsDir)) return c.json({ agents: [] });
|
|
9261
9382
|
const agents = [];
|
|
9262
9383
|
try {
|
|
9263
|
-
const entries =
|
|
9384
|
+
const entries = readdirSync5(agentsDir, { withFileTypes: true });
|
|
9264
9385
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
9265
9386
|
if (!entry.isDirectory()) continue;
|
|
9266
9387
|
if (entry.name === "admin") continue;
|
|
9267
|
-
const configPath2 =
|
|
9388
|
+
const configPath2 = resolve16(agentsDir, entry.name, "config.json");
|
|
9268
9389
|
if (!existsSync17(configPath2)) continue;
|
|
9269
9390
|
try {
|
|
9270
|
-
const config = JSON.parse(
|
|
9391
|
+
const config = JSON.parse(readFileSync14(configPath2, "utf-8"));
|
|
9271
9392
|
agents.push({
|
|
9272
9393
|
slug: entry.name,
|
|
9273
9394
|
displayName: config.displayName ?? entry.name,
|
|
@@ -9293,7 +9414,7 @@ app16.delete("/:slug", async (c) => {
|
|
|
9293
9414
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
9294
9415
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
9295
9416
|
}
|
|
9296
|
-
const agentDir =
|
|
9417
|
+
const agentDir = resolve16(account.accountDir, "agents", slug);
|
|
9297
9418
|
if (!existsSync17(agentDir)) {
|
|
9298
9419
|
return c.json({ error: "Agent not found" }, 404);
|
|
9299
9420
|
}
|
|
@@ -9323,7 +9444,7 @@ app16.post("/:slug/project", async (c) => {
|
|
|
9323
9444
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
9324
9445
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
9325
9446
|
}
|
|
9326
|
-
const agentDir =
|
|
9447
|
+
const agentDir = resolve16(account.accountDir, "agents", slug);
|
|
9327
9448
|
if (!existsSync17(agentDir)) {
|
|
9328
9449
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
9329
9450
|
}
|
|
@@ -9749,13 +9870,13 @@ async function cdpNavigateNewTab(url, opts = {}) {
|
|
|
9749
9870
|
// server/routes/admin/device-browser.ts
|
|
9750
9871
|
var app19 = new Hono();
|
|
9751
9872
|
app19.post("/navigate", async (c) => {
|
|
9752
|
-
const
|
|
9873
|
+
const TAG20 = "[device-url:click]";
|
|
9753
9874
|
let body;
|
|
9754
9875
|
try {
|
|
9755
9876
|
body = await c.req.json();
|
|
9756
9877
|
} catch (err) {
|
|
9757
9878
|
const detail = err instanceof Error ? err.message : String(err);
|
|
9758
|
-
console.error(`${
|
|
9879
|
+
console.error(`${TAG20} reject reason=body-not-json detail=${detail} browser=fallback navigateResult=error`);
|
|
9759
9880
|
return c.json(
|
|
9760
9881
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "Request body was not valid JSON" },
|
|
9761
9882
|
400
|
|
@@ -9765,7 +9886,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9765
9886
|
const intent = typeof body.intent === "string" ? body.intent : "";
|
|
9766
9887
|
const hostname2 = typeof body.hostname === "string" ? body.hostname : "";
|
|
9767
9888
|
if (!url) {
|
|
9768
|
-
console.error(`${
|
|
9889
|
+
console.error(`${TAG20} reject reason=missing-url intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`);
|
|
9769
9890
|
return c.json(
|
|
9770
9891
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "url field is required" },
|
|
9771
9892
|
400
|
|
@@ -9775,7 +9896,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9775
9896
|
try {
|
|
9776
9897
|
parsed = new URL(url);
|
|
9777
9898
|
} catch {
|
|
9778
|
-
console.error(`${
|
|
9899
|
+
console.error(`${TAG20} reject reason=url-malformed intent=${JSON.stringify(intent)} url=${url} browser=fallback navigateResult=error`);
|
|
9779
9900
|
return c.json(
|
|
9780
9901
|
{ ok: false, navigateResult: "error", browser: "fallback", detail: "url is not a valid URL" },
|
|
9781
9902
|
400
|
|
@@ -9783,7 +9904,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9783
9904
|
}
|
|
9784
9905
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
9785
9906
|
console.error(
|
|
9786
|
-
`${
|
|
9907
|
+
`${TAG20} reject reason=scheme-not-allowed scheme=${parsed.protocol} intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`
|
|
9787
9908
|
);
|
|
9788
9909
|
return c.json(
|
|
9789
9910
|
{
|
|
@@ -9799,7 +9920,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9799
9920
|
const cdpOk = await ensureCdp(transport);
|
|
9800
9921
|
if (!cdpOk) {
|
|
9801
9922
|
console.error(
|
|
9802
|
-
`${
|
|
9923
|
+
`${TAG20} intent=${JSON.stringify(intent)} browser=fallback navigateResult=cdp-unreachable hostname=${JSON.stringify(hostname2)}`
|
|
9803
9924
|
);
|
|
9804
9925
|
return c.json(
|
|
9805
9926
|
{
|
|
@@ -9815,7 +9936,7 @@ app19.post("/navigate", async (c) => {
|
|
|
9815
9936
|
const browser = outcome.result === "ok" ? "vnc" : "fallback";
|
|
9816
9937
|
const detailStr = outcome.detail ? ` detail=${JSON.stringify(outcome.detail.length > 230 ? outcome.detail.slice(0, 227) + "..." : outcome.detail)}` : "";
|
|
9817
9938
|
console.error(
|
|
9818
|
-
`${
|
|
9939
|
+
`${TAG20} intent=${JSON.stringify(intent)} browser=${browser} navigateResult=${outcome.result} hostname=${JSON.stringify(hostname2)} targetId=${outcome.targetId ?? "none"}${detailStr}`
|
|
9819
9940
|
);
|
|
9820
9941
|
if (outcome.result !== "ok") {
|
|
9821
9942
|
return c.json(
|
|
@@ -9846,18 +9967,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
9846
9967
|
]);
|
|
9847
9968
|
var app20 = new Hono();
|
|
9848
9969
|
app20.post("/", async (c) => {
|
|
9849
|
-
const
|
|
9970
|
+
const TAG20 = "[admin:events]";
|
|
9850
9971
|
let body;
|
|
9851
9972
|
try {
|
|
9852
9973
|
body = await c.req.json();
|
|
9853
9974
|
} catch (err) {
|
|
9854
9975
|
const detail = err instanceof Error ? err.message : String(err);
|
|
9855
|
-
console.error(`${
|
|
9976
|
+
console.error(`${TAG20} reject reason=body-not-json detail=${detail}`);
|
|
9856
9977
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
9857
9978
|
}
|
|
9858
9979
|
const event = typeof body.event === "string" ? body.event : "";
|
|
9859
9980
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
9860
|
-
console.error(`${
|
|
9981
|
+
console.error(`${TAG20} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
9861
9982
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
9862
9983
|
}
|
|
9863
9984
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -9880,8 +10001,8 @@ var events_default = app20;
|
|
|
9880
10001
|
|
|
9881
10002
|
// server/routes/admin/cloudflare.ts
|
|
9882
10003
|
import { homedir } from "os";
|
|
9883
|
-
import { resolve as
|
|
9884
|
-
import { readFileSync as
|
|
10004
|
+
import { resolve as resolve18 } from "path";
|
|
10005
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
9885
10006
|
|
|
9886
10007
|
// app/lib/dns-label.ts
|
|
9887
10008
|
var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
@@ -9897,14 +10018,14 @@ function isValidDomain(value) {
|
|
|
9897
10018
|
}
|
|
9898
10019
|
|
|
9899
10020
|
// app/lib/alias-domains.ts
|
|
9900
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as
|
|
10021
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
|
|
9901
10022
|
import { dirname as dirname7 } from "path";
|
|
9902
|
-
import { resolve as
|
|
9903
|
-
var ALIAS_DOMAINS_PATH =
|
|
10023
|
+
import { resolve as resolve17 } from "path";
|
|
10024
|
+
var ALIAS_DOMAINS_PATH = resolve17(MAXY_DIR, "alias-domains.json");
|
|
9904
10025
|
function readExisting() {
|
|
9905
10026
|
if (!existsSync19(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
|
|
9906
10027
|
try {
|
|
9907
|
-
const parsed = JSON.parse(
|
|
10028
|
+
const parsed = JSON.parse(readFileSync15(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
9908
10029
|
if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
|
|
9909
10030
|
return new Set(parsed.filter((h) => typeof h === "string"));
|
|
9910
10031
|
} catch {
|
|
@@ -9919,14 +10040,131 @@ function addAliasDomain(hostname2) {
|
|
|
9919
10040
|
writeFileSync9(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
|
|
9920
10041
|
}
|
|
9921
10042
|
|
|
10043
|
+
// app/lib/action-relay-queue.ts
|
|
10044
|
+
import {
|
|
10045
|
+
existsSync as existsSync20,
|
|
10046
|
+
mkdirSync as mkdirSync9,
|
|
10047
|
+
readdirSync as readdirSync6,
|
|
10048
|
+
readFileSync as readFileSync16,
|
|
10049
|
+
unlinkSync as unlinkSync2,
|
|
10050
|
+
writeFileSync as writeFileSync10
|
|
10051
|
+
} from "fs";
|
|
10052
|
+
import { join as join9 } from "path";
|
|
10053
|
+
var TAG19 = "[action-relay-queue]";
|
|
10054
|
+
var FILE_PREFIX = "action-completion-relay-";
|
|
10055
|
+
var FILE_SUFFIX = ".json";
|
|
10056
|
+
function queueDir(accountDir) {
|
|
10057
|
+
return join9(accountDir, "queue");
|
|
10058
|
+
}
|
|
10059
|
+
function relayFilePath(accountDir, actionId) {
|
|
10060
|
+
return join9(queueDir(accountDir), `${FILE_PREFIX}${actionId}${FILE_SUFFIX}`);
|
|
10061
|
+
}
|
|
10062
|
+
function enqueueActionCompletionRelay(opts) {
|
|
10063
|
+
const dir = queueDir(opts.accountDir);
|
|
10064
|
+
mkdirSync9(dir, { recursive: true });
|
|
10065
|
+
const filePath = relayFilePath(opts.accountDir, opts.actionId);
|
|
10066
|
+
const record = {
|
|
10067
|
+
actionId: opts.actionId,
|
|
10068
|
+
conversationId: opts.conversationId,
|
|
10069
|
+
message: opts.message,
|
|
10070
|
+
queuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10071
|
+
};
|
|
10072
|
+
const body = JSON.stringify(record);
|
|
10073
|
+
try {
|
|
10074
|
+
writeFileSync10(filePath, body, { flag: "wx", encoding: "utf-8" });
|
|
10075
|
+
console.log(
|
|
10076
|
+
`${TAG19} phase=enqueued actionId=${opts.actionId} conversationId=${opts.conversationId} path=${filePath} bytes=${Buffer.byteLength(body, "utf-8")}`
|
|
10077
|
+
);
|
|
10078
|
+
return { enqueued: true, path: filePath };
|
|
10079
|
+
} catch (e) {
|
|
10080
|
+
if (e.code === "EEXIST") {
|
|
10081
|
+
console.log(
|
|
10082
|
+
`${TAG19} phase=enqueue-skipped actionId=${opts.actionId} reason=already-queued`
|
|
10083
|
+
);
|
|
10084
|
+
return { enqueued: false, reason: "already-queued", path: filePath };
|
|
10085
|
+
}
|
|
10086
|
+
throw e;
|
|
10087
|
+
}
|
|
10088
|
+
}
|
|
10089
|
+
function consumeActionCompletionRelays(accountDir) {
|
|
10090
|
+
const dir = queueDir(accountDir);
|
|
10091
|
+
if (!existsSync20(dir)) return [];
|
|
10092
|
+
let entries;
|
|
10093
|
+
try {
|
|
10094
|
+
entries = readdirSync6(dir);
|
|
10095
|
+
} catch (e) {
|
|
10096
|
+
console.error(
|
|
10097
|
+
`${TAG19} phase=readdir-failed dir=${dir} error=${e instanceof Error ? e.message : String(e)}`
|
|
10098
|
+
);
|
|
10099
|
+
return [];
|
|
10100
|
+
}
|
|
10101
|
+
const records = [];
|
|
10102
|
+
for (const name of entries) {
|
|
10103
|
+
if (!name.startsWith(FILE_PREFIX) || !name.endsWith(FILE_SUFFIX)) continue;
|
|
10104
|
+
const filePath = join9(dir, name);
|
|
10105
|
+
let raw;
|
|
10106
|
+
try {
|
|
10107
|
+
raw = readFileSync16(filePath, "utf-8");
|
|
10108
|
+
} catch (e) {
|
|
10109
|
+
console.error(
|
|
10110
|
+
`${TAG19} phase=read-failed file=${name} error=${e instanceof Error ? e.message : String(e)}`
|
|
10111
|
+
);
|
|
10112
|
+
continue;
|
|
10113
|
+
}
|
|
10114
|
+
let parsed;
|
|
10115
|
+
try {
|
|
10116
|
+
parsed = JSON.parse(raw);
|
|
10117
|
+
} catch (e) {
|
|
10118
|
+
console.error(
|
|
10119
|
+
`${TAG19} phase=parse-failed file=${name} error=${e instanceof Error ? e.message : String(e)}`
|
|
10120
|
+
);
|
|
10121
|
+
continue;
|
|
10122
|
+
}
|
|
10123
|
+
if (!isActionCompletionRelay(parsed)) {
|
|
10124
|
+
console.error(
|
|
10125
|
+
`${TAG19} phase=shape-invalid file=${name} keys=${parsed && typeof parsed === "object" ? Object.keys(parsed).join(",") : "non-object"}`
|
|
10126
|
+
);
|
|
10127
|
+
continue;
|
|
10128
|
+
}
|
|
10129
|
+
records.push({ rec: parsed, filePath });
|
|
10130
|
+
}
|
|
10131
|
+
records.sort((a, b) => a.rec.queuedAt.localeCompare(b.rec.queuedAt));
|
|
10132
|
+
const out = [];
|
|
10133
|
+
const now = Date.now();
|
|
10134
|
+
for (const { rec, filePath } of records) {
|
|
10135
|
+
const queuedAtMs = Date.parse(rec.queuedAt);
|
|
10136
|
+
const ageMs = Number.isFinite(queuedAtMs) ? now - queuedAtMs : 0;
|
|
10137
|
+
out.push({ ...rec, ageMs, filePath });
|
|
10138
|
+
}
|
|
10139
|
+
return out;
|
|
10140
|
+
}
|
|
10141
|
+
function deleteConsumedRelay(filePath) {
|
|
10142
|
+
try {
|
|
10143
|
+
unlinkSync2(filePath);
|
|
10144
|
+
} catch (e) {
|
|
10145
|
+
const code = e.code;
|
|
10146
|
+
if (code !== "ENOENT") {
|
|
10147
|
+
console.error(
|
|
10148
|
+
`${TAG19} phase=unlink-failed file=${filePath} error=${e instanceof Error ? e.message : String(e)}`
|
|
10149
|
+
);
|
|
10150
|
+
}
|
|
10151
|
+
}
|
|
10152
|
+
}
|
|
10153
|
+
function isActionCompletionRelay(value) {
|
|
10154
|
+
if (!value || typeof value !== "object") return false;
|
|
10155
|
+
const v = value;
|
|
10156
|
+
return typeof v.actionId === "string" && typeof v.conversationId === "string" && typeof v.message === "string" && typeof v.queuedAt === "string";
|
|
10157
|
+
}
|
|
10158
|
+
|
|
9922
10159
|
// server/routes/admin/cloudflare.ts
|
|
10160
|
+
import { existsSync as existsSyncFs, readFileSync as readFileSyncFs } from "fs";
|
|
9923
10161
|
var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
9924
10162
|
var DOMAINS_TIMEOUT_MS = 40 * 1e3;
|
|
9925
10163
|
function loadBrandInfo() {
|
|
9926
|
-
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
9927
|
-
const brandPath =
|
|
10164
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
|
|
10165
|
+
const brandPath = resolve18(platformRoot2, "config", "brand.json");
|
|
9928
10166
|
try {
|
|
9929
|
-
const parsed = JSON.parse(
|
|
10167
|
+
const parsed = JSON.parse(readFileSync17(brandPath, "utf-8"));
|
|
9930
10168
|
const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
|
|
9931
10169
|
const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
|
|
9932
10170
|
return { hostname: hostname2, configDir: configDir2 };
|
|
@@ -10029,7 +10267,7 @@ app21.get("/domains", requireAdminSession, async (c) => {
|
|
|
10029
10267
|
streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
|
|
10030
10268
|
log(`phase=stream-log-resolved path=${streamLogPath}`);
|
|
10031
10269
|
const brand = loadBrandInfo();
|
|
10032
|
-
const scriptPath =
|
|
10270
|
+
const scriptPath = resolve18(homedir(), "list-cf-domains.sh");
|
|
10033
10271
|
const result = await runFormSpawn({
|
|
10034
10272
|
scriptPath,
|
|
10035
10273
|
args: [brand.hostname],
|
|
@@ -10222,23 +10460,89 @@ actionId: ${actionId}`,
|
|
|
10222
10460
|
};
|
|
10223
10461
|
return ok(success);
|
|
10224
10462
|
});
|
|
10463
|
+
var RELAY_MAX_BODY = 8 * 1024;
|
|
10464
|
+
var RELAY_MAX_MESSAGE = 2048;
|
|
10465
|
+
var ACTION_ID_RE = /^[a-z0-9-]+$/;
|
|
10466
|
+
app21.post("/relay-completion", requireAdminSession, async (c) => {
|
|
10467
|
+
const sessionKey = c.var.sessionKey;
|
|
10468
|
+
let raw;
|
|
10469
|
+
try {
|
|
10470
|
+
raw = await c.req.text();
|
|
10471
|
+
} catch {
|
|
10472
|
+
return c.json({ ok: false, reason: "invalid-body" }, 400);
|
|
10473
|
+
}
|
|
10474
|
+
if (Buffer.byteLength(raw, "utf-8") > RELAY_MAX_BODY) {
|
|
10475
|
+
return c.json({ ok: false, reason: "body-too-large" }, 413);
|
|
10476
|
+
}
|
|
10477
|
+
let body;
|
|
10478
|
+
try {
|
|
10479
|
+
body = JSON.parse(raw);
|
|
10480
|
+
} catch {
|
|
10481
|
+
return c.json({ ok: false, reason: "invalid-json" }, 400);
|
|
10482
|
+
}
|
|
10483
|
+
if (!body || typeof body !== "object") {
|
|
10484
|
+
return c.json({ ok: false, reason: "invalid-body" }, 400);
|
|
10485
|
+
}
|
|
10486
|
+
const { actionId, message } = body;
|
|
10487
|
+
if (typeof actionId !== "string" || !ACTION_ID_RE.test(actionId) || actionId.length > 200) {
|
|
10488
|
+
console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=invalid-actionid sessionKey=${sessionKey.slice(-8)}`);
|
|
10489
|
+
return c.json({ ok: false, reason: "invalid-actionid" }, 400);
|
|
10490
|
+
}
|
|
10491
|
+
if (typeof message !== "string" || message.length === 0 || message.length > RELAY_MAX_MESSAGE) {
|
|
10492
|
+
return c.json({ ok: false, reason: "invalid-message" }, 400);
|
|
10493
|
+
}
|
|
10494
|
+
const conversationId = getConversationIdForSession(sessionKey);
|
|
10495
|
+
if (!conversationId) {
|
|
10496
|
+
console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=missing-conversation-id sessionKey=${sessionKey.slice(-8)}`);
|
|
10497
|
+
return c.json({ ok: false, reason: "missing-conversation-id" }, 400);
|
|
10498
|
+
}
|
|
10499
|
+
const account = resolveAccount();
|
|
10500
|
+
if (!account) {
|
|
10501
|
+
console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=missing-account-dir actionId=${actionId}`);
|
|
10502
|
+
return c.json({ ok: false, reason: "missing-account-dir" }, 500);
|
|
10503
|
+
}
|
|
10504
|
+
const logPath2 = actionLogPath(actionId);
|
|
10505
|
+
let auditOutcome = "log-absent";
|
|
10506
|
+
if (existsSyncFs(logPath2)) {
|
|
10507
|
+
try {
|
|
10508
|
+
const lines = readFileSyncFs(logPath2, "utf-8").split("\n");
|
|
10509
|
+
const reconciled = reconcileCloudflareSetupFromLog(lines);
|
|
10510
|
+
auditOutcome = reconciled ? reconciled.kind : "log-incomplete";
|
|
10511
|
+
} catch (e) {
|
|
10512
|
+
auditOutcome = `read-failed:${e instanceof Error ? e.message : String(e)}`;
|
|
10513
|
+
}
|
|
10514
|
+
}
|
|
10515
|
+
console.log(`[cloudflare-relay-completion] phase=enqueue-attempt actionId=${actionId} auditOutcome=${auditOutcome} conversationId=${conversationId}`);
|
|
10516
|
+
try {
|
|
10517
|
+
enqueueActionCompletionRelay({
|
|
10518
|
+
accountDir: account.accountDir,
|
|
10519
|
+
actionId,
|
|
10520
|
+
conversationId,
|
|
10521
|
+
message
|
|
10522
|
+
});
|
|
10523
|
+
} catch (e) {
|
|
10524
|
+
console.error(`[cloudflare-relay-completion] phase=enqueue-failed actionId=${actionId} error=${e instanceof Error ? e.message : String(e)}`);
|
|
10525
|
+
return c.json({ ok: false, reason: "enqueue-failed" }, 500);
|
|
10526
|
+
}
|
|
10527
|
+
return c.body(null, 204);
|
|
10528
|
+
});
|
|
10225
10529
|
var cloudflare_default = app21;
|
|
10226
10530
|
|
|
10227
10531
|
// server/routes/admin/files.ts
|
|
10228
10532
|
import { createReadStream as createReadStream3 } from "fs";
|
|
10229
10533
|
import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
10230
10534
|
import { realpathSync as realpathSync4 } from "fs";
|
|
10231
|
-
import { basename as basename6, dirname as dirname8, join as
|
|
10535
|
+
import { basename as basename6, dirname as dirname8, join as join10, resolve as resolve20, sep as sep2 } from "path";
|
|
10232
10536
|
import { Readable as Readable2 } from "stream";
|
|
10233
10537
|
|
|
10234
10538
|
// app/lib/data-path.ts
|
|
10235
10539
|
import { realpathSync as realpathSync3 } from "fs";
|
|
10236
|
-
import { resolve as
|
|
10237
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ??
|
|
10238
|
-
var DATA_ROOT =
|
|
10540
|
+
import { resolve as resolve19, normalize, sep, relative } from "path";
|
|
10541
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "../platform");
|
|
10542
|
+
var DATA_ROOT = resolve19(PLATFORM_ROOT6, "..", "data");
|
|
10239
10543
|
function resolveDataPath(raw) {
|
|
10240
10544
|
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
10241
|
-
const absolute =
|
|
10545
|
+
const absolute = resolve19(DATA_ROOT, cleaned);
|
|
10242
10546
|
let dataRootReal;
|
|
10243
10547
|
try {
|
|
10244
10548
|
dataRootReal = realpathSync3(DATA_ROOT);
|
|
@@ -10503,7 +10807,7 @@ async function restoreNode(params) {
|
|
|
10503
10807
|
}
|
|
10504
10808
|
|
|
10505
10809
|
// app/lib/file-delete-cascade.ts
|
|
10506
|
-
var
|
|
10810
|
+
var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10507
10811
|
function parseAttachmentPath(relPath2) {
|
|
10508
10812
|
const segments = relPath2.split("/").filter(Boolean);
|
|
10509
10813
|
if (segments.length !== 4) return null;
|
|
@@ -10511,7 +10815,7 @@ function parseAttachmentPath(relPath2) {
|
|
|
10511
10815
|
const accountId = segments[1];
|
|
10512
10816
|
const attachmentId = segments[2];
|
|
10513
10817
|
const filename = segments[3];
|
|
10514
|
-
if (!
|
|
10818
|
+
if (!UUID_RE3.test(accountId) || !UUID_RE3.test(attachmentId)) return null;
|
|
10515
10819
|
const dot = filename.lastIndexOf(".");
|
|
10516
10820
|
if (dot === -1) return null;
|
|
10517
10821
|
const stem = filename.slice(0, dot);
|
|
@@ -10583,10 +10887,10 @@ async function cascadeDeleteDocument(params) {
|
|
|
10583
10887
|
}
|
|
10584
10888
|
|
|
10585
10889
|
// server/routes/admin/files.ts
|
|
10586
|
-
var
|
|
10890
|
+
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10587
10891
|
async function readMeta(absDir, baseName) {
|
|
10588
10892
|
try {
|
|
10589
|
-
const raw = await readFile4(
|
|
10893
|
+
const raw = await readFile4(join10(absDir, `${baseName}.meta.json`), "utf8");
|
|
10590
10894
|
const parsed = JSON.parse(raw);
|
|
10591
10895
|
if (typeof parsed?.filename === "string") {
|
|
10592
10896
|
return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
|
|
@@ -10597,7 +10901,7 @@ async function readMeta(absDir, baseName) {
|
|
|
10597
10901
|
}
|
|
10598
10902
|
async function readAccountNames() {
|
|
10599
10903
|
const map = /* @__PURE__ */ new Map();
|
|
10600
|
-
const accountsDir =
|
|
10904
|
+
const accountsDir = resolve20(DATA_ROOT, "accounts");
|
|
10601
10905
|
let names;
|
|
10602
10906
|
try {
|
|
10603
10907
|
names = await readdir2(accountsDir);
|
|
@@ -10605,8 +10909,8 @@ async function readAccountNames() {
|
|
|
10605
10909
|
return map;
|
|
10606
10910
|
}
|
|
10607
10911
|
for (const name of names) {
|
|
10608
|
-
if (!
|
|
10609
|
-
const configPath2 =
|
|
10912
|
+
if (!UUID_RE4.test(name)) continue;
|
|
10913
|
+
const configPath2 = resolve20(accountsDir, name, "account.json");
|
|
10610
10914
|
try {
|
|
10611
10915
|
const raw = await readFile4(configPath2, "utf8");
|
|
10612
10916
|
const parsed = JSON.parse(raw);
|
|
@@ -10623,8 +10927,8 @@ async function readAccountNames() {
|
|
|
10623
10927
|
return map;
|
|
10624
10928
|
}
|
|
10625
10929
|
async function enrich(absolute, entry, accountNames) {
|
|
10626
|
-
if (entry.kind === "directory" &&
|
|
10627
|
-
const meta = await readMeta(
|
|
10930
|
+
if (entry.kind === "directory" && UUID_RE4.test(entry.name)) {
|
|
10931
|
+
const meta = await readMeta(join10(absolute, entry.name), entry.name);
|
|
10628
10932
|
if (meta?.filename) {
|
|
10629
10933
|
entry.displayName = meta.filename;
|
|
10630
10934
|
entry.mimeType = meta.mimeType;
|
|
@@ -10639,7 +10943,7 @@ async function enrich(absolute, entry, accountNames) {
|
|
|
10639
10943
|
if (entry.kind === "file") {
|
|
10640
10944
|
const dot = entry.name.lastIndexOf(".");
|
|
10641
10945
|
const base = dot === -1 ? entry.name : entry.name.slice(0, dot);
|
|
10642
|
-
if (
|
|
10946
|
+
if (UUID_RE4.test(base)) {
|
|
10643
10947
|
const meta = await readMeta(absolute, base);
|
|
10644
10948
|
if (meta?.filename) {
|
|
10645
10949
|
entry.displayName = meta.filename;
|
|
@@ -10651,7 +10955,7 @@ async function enrich(absolute, entry, accountNames) {
|
|
|
10651
10955
|
function buildDisplayPath(relPath2, accountNames) {
|
|
10652
10956
|
if (relPath2 === "." || relPath2 === "") return [];
|
|
10653
10957
|
return relPath2.split("/").filter(Boolean).map((seg) => {
|
|
10654
|
-
const dn =
|
|
10958
|
+
const dn = UUID_RE4.test(seg) ? accountNames.get(seg) : void 0;
|
|
10655
10959
|
return dn ? { name: seg, displayName: dn } : { name: seg };
|
|
10656
10960
|
});
|
|
10657
10961
|
}
|
|
@@ -10679,11 +10983,11 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
10679
10983
|
const names = await readdir2(absolute);
|
|
10680
10984
|
const entries = [];
|
|
10681
10985
|
for (const name of names) {
|
|
10682
|
-
if (
|
|
10986
|
+
if (UUID_RE4.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
|
|
10683
10987
|
continue;
|
|
10684
10988
|
}
|
|
10685
10989
|
try {
|
|
10686
|
-
const entryPath =
|
|
10990
|
+
const entryPath = join10(absolute, name);
|
|
10687
10991
|
const s = await stat4(entryPath);
|
|
10688
10992
|
entries.push({
|
|
10689
10993
|
name,
|
|
@@ -10797,8 +11101,8 @@ app22.post("/upload", requireAdminSession, async (c) => {
|
|
|
10797
11101
|
}
|
|
10798
11102
|
const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
|
|
10799
11103
|
const finalName = `${Date.now()}-${safeName}`;
|
|
10800
|
-
const destDir =
|
|
10801
|
-
const destPath =
|
|
11104
|
+
const destDir = resolve20(DATA_ROOT, "uploads", accountId);
|
|
11105
|
+
const destPath = resolve20(destDir, finalName);
|
|
10802
11106
|
try {
|
|
10803
11107
|
await mkdir3(destDir, { recursive: true });
|
|
10804
11108
|
const dataRootReal = realpathSync4(DATA_ROOT);
|
|
@@ -10856,7 +11160,7 @@ app22.delete("/", requireAdminSession, async (c) => {
|
|
|
10856
11160
|
}
|
|
10857
11161
|
const dot = base.lastIndexOf(".");
|
|
10858
11162
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
10859
|
-
const sidecarPath =
|
|
11163
|
+
const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ? join10(dirname8(absolute), `${stem}.meta.json`) : null;
|
|
10860
11164
|
await unlink2(absolute);
|
|
10861
11165
|
if (sidecarPath) {
|
|
10862
11166
|
try {
|
|
@@ -12313,8 +12617,8 @@ var adherence_default = app30;
|
|
|
12313
12617
|
// server/routes/admin/sidebar-artefacts.ts
|
|
12314
12618
|
import neo4j3 from "neo4j-driver";
|
|
12315
12619
|
import { readFile as readFile5, readdir as readdir3, stat as stat5 } from "fs/promises";
|
|
12316
|
-
import { resolve as
|
|
12317
|
-
import { existsSync as
|
|
12620
|
+
import { resolve as resolve21, relative as relative2, isAbsolute } from "path";
|
|
12621
|
+
import { existsSync as existsSync21 } from "fs";
|
|
12318
12622
|
var LIMIT = 50;
|
|
12319
12623
|
var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
|
|
12320
12624
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
@@ -12330,7 +12634,7 @@ app31.get("/", requireAdminSession, async (c) => {
|
|
|
12330
12634
|
if (docs === null) {
|
|
12331
12635
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
12332
12636
|
}
|
|
12333
|
-
const accountDir =
|
|
12637
|
+
const accountDir = resolve21(ACCOUNTS_DIR, accountId);
|
|
12334
12638
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
12335
12639
|
const artefacts = [...docs, ...agents].sort(
|
|
12336
12640
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
@@ -12393,8 +12697,8 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
|
|
|
12393
12697
|
logSkip(displayName, "non-text-mime", mimeType);
|
|
12394
12698
|
return { content: "", skipReason: "non-text-mime" };
|
|
12395
12699
|
}
|
|
12396
|
-
const accountDir =
|
|
12397
|
-
const dir =
|
|
12700
|
+
const accountDir = resolve21(ATTACHMENTS_ROOT, accountId);
|
|
12701
|
+
const dir = resolve21(accountDir, attachmentId);
|
|
12398
12702
|
try {
|
|
12399
12703
|
validateFilePathInAccount(dir, accountDir);
|
|
12400
12704
|
} catch {
|
|
@@ -12408,7 +12712,7 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
|
|
|
12408
12712
|
logSkip(displayName, "missing-on-disk", mimeType);
|
|
12409
12713
|
return { content: "", skipReason: "missing-on-disk" };
|
|
12410
12714
|
}
|
|
12411
|
-
return { content: await readFile5(
|
|
12715
|
+
return { content: await readFile5(resolve21(dir, dataFile), "utf-8"), skipReason: null };
|
|
12412
12716
|
} catch (err) {
|
|
12413
12717
|
const message = err instanceof Error ? err.message : String(err);
|
|
12414
12718
|
console.error(`[admin/sidebar-artefacts] read-failed attachmentId=${attachmentId.slice(0, 8)} error="${message}"`);
|
|
@@ -12424,8 +12728,8 @@ function logSkip(name, reason, mimeType) {
|
|
|
12424
12728
|
async function fetchAgentTemplateRows(accountDir) {
|
|
12425
12729
|
const rows = [];
|
|
12426
12730
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
12427
|
-
const overridePath =
|
|
12428
|
-
const bundledPath =
|
|
12731
|
+
const overridePath = resolve21(accountDir, "agents", "admin", filename);
|
|
12732
|
+
const bundledPath = resolve21(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
12429
12733
|
const labelStem = filename.replace(/\.md$/, "");
|
|
12430
12734
|
const row = await readAgentTemplateRow({
|
|
12431
12735
|
id: `agent-template:admin:${filename}`,
|
|
@@ -12439,12 +12743,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
12439
12743
|
});
|
|
12440
12744
|
if (row) rows.push(row);
|
|
12441
12745
|
}
|
|
12442
|
-
const overrideDir =
|
|
12443
|
-
const bundledDir =
|
|
12746
|
+
const overrideDir = resolve21(accountDir, "specialists", "agents");
|
|
12747
|
+
const bundledDir = resolve21(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
12444
12748
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
12445
12749
|
for (const filename of specialistNames) {
|
|
12446
|
-
const overridePath =
|
|
12447
|
-
const bundledPath =
|
|
12750
|
+
const overridePath = resolve21(overrideDir, filename);
|
|
12751
|
+
const bundledPath = resolve21(bundledDir, filename);
|
|
12448
12752
|
const row = await readAgentTemplateRow({
|
|
12449
12753
|
id: `agent-template:specialist:${filename}`,
|
|
12450
12754
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -12462,7 +12766,7 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
12462
12766
|
async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
12463
12767
|
const names = /* @__PURE__ */ new Set();
|
|
12464
12768
|
for (const dir of [overrideDir, bundledDir]) {
|
|
12465
|
-
if (!
|
|
12769
|
+
if (!existsSync21(dir)) continue;
|
|
12466
12770
|
try {
|
|
12467
12771
|
const entries = await readdir3(dir);
|
|
12468
12772
|
for (const entry of entries) {
|
|
@@ -12477,7 +12781,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
12477
12781
|
}
|
|
12478
12782
|
async function readAgentTemplateRow(inp) {
|
|
12479
12783
|
let chosenPath = null;
|
|
12480
|
-
if (
|
|
12784
|
+
if (existsSync21(inp.overridePath)) {
|
|
12481
12785
|
try {
|
|
12482
12786
|
validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
|
|
12483
12787
|
chosenPath = inp.overridePath;
|
|
@@ -12488,7 +12792,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
12488
12792
|
);
|
|
12489
12793
|
return null;
|
|
12490
12794
|
}
|
|
12491
|
-
} else if (
|
|
12795
|
+
} else if (existsSync21(inp.bundledPath)) {
|
|
12492
12796
|
if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
|
|
12493
12797
|
console.error(
|
|
12494
12798
|
`[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
|
|
@@ -12529,10 +12833,10 @@ var sidebar_artefacts_default = app31;
|
|
|
12529
12833
|
|
|
12530
12834
|
// server/routes/admin/sidebar-artefact-save.ts
|
|
12531
12835
|
import { mkdir as mkdir4, readdir as readdir4, stat as stat6, writeFile as writeFile5 } from "fs/promises";
|
|
12532
|
-
import { resolve as
|
|
12533
|
-
import { existsSync as
|
|
12836
|
+
import { resolve as resolve22 } from "path";
|
|
12837
|
+
import { existsSync as existsSync22 } from "fs";
|
|
12534
12838
|
var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
|
|
12535
|
-
var
|
|
12839
|
+
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
12536
12840
|
var app32 = new Hono();
|
|
12537
12841
|
app32.post("/", requireAdminSession, async (c) => {
|
|
12538
12842
|
const sessionKey = c.var.sessionKey;
|
|
@@ -12542,7 +12846,7 @@ app32.post("/", requireAdminSession, async (c) => {
|
|
|
12542
12846
|
if (!body || typeof body.id !== "string" || typeof body.content !== "string") {
|
|
12543
12847
|
return c.json({ error: "id and content required" }, 400);
|
|
12544
12848
|
}
|
|
12545
|
-
const accountDir =
|
|
12849
|
+
const accountDir = resolve22(ACCOUNTS_DIR, accountId);
|
|
12546
12850
|
const resolved = await resolveSavePath(body.id, accountId, accountDir);
|
|
12547
12851
|
if (resolved.kind === "reject") {
|
|
12548
12852
|
console.error(
|
|
@@ -12583,22 +12887,22 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
12583
12887
|
if (role !== "admin" || !ADMIN_AGENT_FILES2.has(filename)) {
|
|
12584
12888
|
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
12585
12889
|
}
|
|
12586
|
-
const parent =
|
|
12890
|
+
const parent = resolve22(accountDir, "agents", "admin");
|
|
12587
12891
|
await mkdir4(parent, { recursive: true });
|
|
12588
12892
|
try {
|
|
12589
12893
|
validateFilePathInAccount(parent, accountDir);
|
|
12590
12894
|
} catch {
|
|
12591
12895
|
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
12592
12896
|
}
|
|
12593
|
-
return { kind: "admin-template", path:
|
|
12897
|
+
return { kind: "admin-template", path: resolve22(parent, filename) };
|
|
12594
12898
|
}
|
|
12595
|
-
if (
|
|
12596
|
-
const dir =
|
|
12597
|
-
if (!
|
|
12899
|
+
if (UUID_RE5.test(id)) {
|
|
12900
|
+
const dir = resolve22(ATTACHMENTS_ROOT, accountId, id);
|
|
12901
|
+
if (!existsSync22(dir)) {
|
|
12598
12902
|
return { kind: "reject", status: 400, reason: "not-found" };
|
|
12599
12903
|
}
|
|
12600
12904
|
try {
|
|
12601
|
-
validateFilePathInAccount(dir,
|
|
12905
|
+
validateFilePathInAccount(dir, resolve22(ATTACHMENTS_ROOT, accountId));
|
|
12602
12906
|
} catch {
|
|
12603
12907
|
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
12604
12908
|
}
|
|
@@ -12607,7 +12911,7 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
12607
12911
|
if (!dataFile) {
|
|
12608
12912
|
return { kind: "reject", status: 400, reason: "not-found" };
|
|
12609
12913
|
}
|
|
12610
|
-
return { kind: "knowledge-doc", path:
|
|
12914
|
+
return { kind: "knowledge-doc", path: resolve22(dir, dataFile) };
|
|
12611
12915
|
}
|
|
12612
12916
|
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
12613
12917
|
}
|
|
@@ -12618,27 +12922,27 @@ var sidebar_artefact_save_default = app32;
|
|
|
12618
12922
|
|
|
12619
12923
|
// server/routes/admin/sidebar-artefact-content.ts
|
|
12620
12924
|
import { readFile as readFile6, readdir as readdir5 } from "fs/promises";
|
|
12621
|
-
import { existsSync as
|
|
12622
|
-
import { resolve as
|
|
12623
|
-
var
|
|
12925
|
+
import { existsSync as existsSync23 } from "fs";
|
|
12926
|
+
import { resolve as resolve23 } from "path";
|
|
12927
|
+
var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
12624
12928
|
var app33 = new Hono();
|
|
12625
12929
|
app33.get("/", requireAdminSession, async (c) => {
|
|
12626
12930
|
const sessionKey = c.var.sessionKey;
|
|
12627
12931
|
const accountId = getAccountIdForSession(sessionKey);
|
|
12628
12932
|
if (!accountId) return new Response("Unauthorized", { status: 401 });
|
|
12629
12933
|
const id = c.req.query("id") ?? "";
|
|
12630
|
-
if (!
|
|
12934
|
+
if (!UUID_RE6.test(id)) {
|
|
12631
12935
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
12632
12936
|
return new Response("Not found", { status: 404 });
|
|
12633
12937
|
}
|
|
12634
|
-
const dir =
|
|
12635
|
-
if (!
|
|
12938
|
+
const dir = resolve23(ATTACHMENTS_ROOT, accountId, id);
|
|
12939
|
+
if (!existsSync23(dir)) {
|
|
12636
12940
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
12637
12941
|
return new Response("Not found", { status: 404 });
|
|
12638
12942
|
}
|
|
12639
12943
|
let meta;
|
|
12640
12944
|
try {
|
|
12641
|
-
meta = JSON.parse(await readFile6(
|
|
12945
|
+
meta = JSON.parse(await readFile6(resolve23(dir, `${id}.meta.json`), "utf-8"));
|
|
12642
12946
|
} catch {
|
|
12643
12947
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
12644
12948
|
return new Response("Not found", { status: 404 });
|
|
@@ -12650,7 +12954,7 @@ app33.get("/", requireAdminSession, async (c) => {
|
|
|
12650
12954
|
return new Response("Not found", { status: 404 });
|
|
12651
12955
|
}
|
|
12652
12956
|
const start = Date.now();
|
|
12653
|
-
const buffer = await readFile6(
|
|
12957
|
+
const buffer = await readFile6(resolve23(dir, dataFile));
|
|
12654
12958
|
const ms = Date.now() - start;
|
|
12655
12959
|
console.log(
|
|
12656
12960
|
`[admin/sidebar-artefact-content] account=${accountId} id=${id.slice(0, 8)} mime=${meta.mimeType} bytes=${buffer.length} ms=${ms}`
|
|
@@ -12694,8 +12998,8 @@ app34.route("/sidebar-artefact-content", sidebar_artefact_content_default);
|
|
|
12694
12998
|
var admin_default = app34;
|
|
12695
12999
|
|
|
12696
13000
|
// server/routes/sites.ts
|
|
12697
|
-
import { existsSync as
|
|
12698
|
-
import { resolve as
|
|
13001
|
+
import { existsSync as existsSync24, readFileSync as readFileSync18, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
|
|
13002
|
+
import { resolve as resolve24 } from "path";
|
|
12699
13003
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
12700
13004
|
var MIME = {
|
|
12701
13005
|
".html": "text/html; charset=utf-8",
|
|
@@ -12753,28 +13057,28 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
12753
13057
|
}
|
|
12754
13058
|
segments.push(seg);
|
|
12755
13059
|
}
|
|
12756
|
-
const rootDir =
|
|
12757
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
13060
|
+
const rootDir = resolve24(account.accountDir, "sites");
|
|
13061
|
+
let filePath = segments.length === 0 ? rootDir : resolve24(rootDir, ...segments);
|
|
12758
13062
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
12759
13063
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
12760
13064
|
return c.text("Forbidden", 403);
|
|
12761
13065
|
}
|
|
12762
13066
|
let stat7;
|
|
12763
13067
|
try {
|
|
12764
|
-
stat7 =
|
|
13068
|
+
stat7 = existsSync24(filePath) ? statSync8(filePath) : null;
|
|
12765
13069
|
} catch {
|
|
12766
13070
|
stat7 = null;
|
|
12767
13071
|
}
|
|
12768
13072
|
if (stat7?.isDirectory()) {
|
|
12769
|
-
filePath =
|
|
13073
|
+
filePath = resolve24(filePath, "index.html");
|
|
12770
13074
|
} else if (stat7 === null && isDirRequest) {
|
|
12771
|
-
filePath =
|
|
13075
|
+
filePath = resolve24(filePath, "index.html");
|
|
12772
13076
|
}
|
|
12773
13077
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
12774
13078
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
12775
13079
|
return c.text("Forbidden", 403);
|
|
12776
13080
|
}
|
|
12777
|
-
if (!
|
|
13081
|
+
if (!existsSync24(filePath)) {
|
|
12778
13082
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
12779
13083
|
return c.text("Not found", 404);
|
|
12780
13084
|
}
|
|
@@ -12793,7 +13097,7 @@ app35.get("/:rel{.*}", (c) => {
|
|
|
12793
13097
|
}
|
|
12794
13098
|
let body;
|
|
12795
13099
|
try {
|
|
12796
|
-
body =
|
|
13100
|
+
body = readFileSync18(realPath);
|
|
12797
13101
|
} catch (err) {
|
|
12798
13102
|
const code = err?.code;
|
|
12799
13103
|
if (code === "EISDIR") {
|
|
@@ -12925,14 +13229,14 @@ function clientFrom(c) {
|
|
|
12925
13229
|
);
|
|
12926
13230
|
}
|
|
12927
13231
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
12928
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT7 ?
|
|
13232
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join11(PLATFORM_ROOT7, "config", "brand.json") : "";
|
|
12929
13233
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
|
|
12930
|
-
if (BRAND_JSON_PATH && !
|
|
13234
|
+
if (BRAND_JSON_PATH && !existsSync25(BRAND_JSON_PATH)) {
|
|
12931
13235
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
12932
13236
|
}
|
|
12933
|
-
if (BRAND_JSON_PATH &&
|
|
13237
|
+
if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
|
|
12934
13238
|
try {
|
|
12935
|
-
const parsed = JSON.parse(
|
|
13239
|
+
const parsed = JSON.parse(readFileSync19(BRAND_JSON_PATH, "utf-8"));
|
|
12936
13240
|
BRAND = { ...BRAND, ...parsed };
|
|
12937
13241
|
} catch (err) {
|
|
12938
13242
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -12951,11 +13255,11 @@ var brandLoginOpts = {
|
|
|
12951
13255
|
bodyFont: BRAND.defaultFonts?.body,
|
|
12952
13256
|
logoContainsName: !!BRAND.logoContainsName
|
|
12953
13257
|
};
|
|
12954
|
-
var ALIAS_DOMAINS_PATH2 =
|
|
13258
|
+
var ALIAS_DOMAINS_PATH2 = join11(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
12955
13259
|
function loadAliasDomains() {
|
|
12956
13260
|
try {
|
|
12957
|
-
if (!
|
|
12958
|
-
const parsed = JSON.parse(
|
|
13261
|
+
if (!existsSync25(ALIAS_DOMAINS_PATH2)) return null;
|
|
13262
|
+
const parsed = JSON.parse(readFileSync19(ALIAS_DOMAINS_PATH2, "utf-8"));
|
|
12959
13263
|
if (!Array.isArray(parsed)) {
|
|
12960
13264
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
12961
13265
|
return null;
|
|
@@ -13295,20 +13599,20 @@ app36.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
13295
13599
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
13296
13600
|
return c.text("Not found", 404);
|
|
13297
13601
|
}
|
|
13298
|
-
const filePath =
|
|
13299
|
-
const expectedDir =
|
|
13602
|
+
const filePath = resolve25(account.accountDir, "agents", slug, "assets", filename);
|
|
13603
|
+
const expectedDir = resolve25(account.accountDir, "agents", slug, "assets");
|
|
13300
13604
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
13301
13605
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
13302
13606
|
return c.text("Forbidden", 403);
|
|
13303
13607
|
}
|
|
13304
|
-
if (!
|
|
13608
|
+
if (!existsSync25(filePath)) {
|
|
13305
13609
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
13306
13610
|
return c.text("Not found", 404);
|
|
13307
13611
|
}
|
|
13308
13612
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
13309
13613
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
13310
13614
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
13311
|
-
const body =
|
|
13615
|
+
const body = readFileSync19(filePath);
|
|
13312
13616
|
return c.body(body, 200, {
|
|
13313
13617
|
"Content-Type": contentType,
|
|
13314
13618
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -13325,20 +13629,20 @@ app36.get("/generated/:filename", (c) => {
|
|
|
13325
13629
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
13326
13630
|
return c.text("Not found", 404);
|
|
13327
13631
|
}
|
|
13328
|
-
const filePath =
|
|
13329
|
-
const expectedDir =
|
|
13632
|
+
const filePath = resolve25(account.accountDir, "generated", filename);
|
|
13633
|
+
const expectedDir = resolve25(account.accountDir, "generated");
|
|
13330
13634
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
13331
13635
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
13332
13636
|
return c.text("Forbidden", 403);
|
|
13333
13637
|
}
|
|
13334
|
-
if (!
|
|
13638
|
+
if (!existsSync25(filePath)) {
|
|
13335
13639
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
13336
13640
|
return c.text("Not found", 404);
|
|
13337
13641
|
}
|
|
13338
13642
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
13339
13643
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
13340
13644
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
13341
|
-
const body =
|
|
13645
|
+
const body = readFileSync19(filePath);
|
|
13342
13646
|
return c.body(body, 200, {
|
|
13343
13647
|
"Content-Type": contentType,
|
|
13344
13648
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -13348,9 +13652,9 @@ app36.route("/sites", sites_default);
|
|
|
13348
13652
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
13349
13653
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
13350
13654
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
13351
|
-
if (BRAND_JSON_PATH &&
|
|
13655
|
+
if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
|
|
13352
13656
|
try {
|
|
13353
|
-
const fullBrand = JSON.parse(
|
|
13657
|
+
const fullBrand = JSON.parse(readFileSync19(BRAND_JSON_PATH, "utf-8"));
|
|
13354
13658
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
13355
13659
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
13356
13660
|
} catch {
|
|
@@ -13367,9 +13671,9 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
13367
13671
|
function readInstalledVersion() {
|
|
13368
13672
|
try {
|
|
13369
13673
|
if (!PLATFORM_ROOT7) return "unknown";
|
|
13370
|
-
const versionFile =
|
|
13371
|
-
if (!
|
|
13372
|
-
const content =
|
|
13674
|
+
const versionFile = join11(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
|
|
13675
|
+
if (!existsSync25(versionFile)) return "unknown";
|
|
13676
|
+
const content = readFileSync19(versionFile, "utf-8").trim();
|
|
13373
13677
|
return content || "unknown";
|
|
13374
13678
|
} catch {
|
|
13375
13679
|
return "unknown";
|
|
@@ -13410,7 +13714,7 @@ var clientErrorReporterScript = `<script>
|
|
|
13410
13714
|
function cachedHtml(file) {
|
|
13411
13715
|
let html = htmlCache.get(file);
|
|
13412
13716
|
if (!html) {
|
|
13413
|
-
html =
|
|
13717
|
+
html = readFileSync19(resolve25(process.cwd(), "public", file), "utf-8");
|
|
13414
13718
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
13415
13719
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
13416
13720
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -13426,26 +13730,26 @@ ${clientErrorReporterScript}
|
|
|
13426
13730
|
}
|
|
13427
13731
|
var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
13428
13732
|
function loadBrandingCache(agentSlug) {
|
|
13429
|
-
const configDir2 =
|
|
13733
|
+
const configDir2 = join11(homedir2(), BRAND.configDir);
|
|
13430
13734
|
try {
|
|
13431
|
-
const accountJsonPath =
|
|
13432
|
-
if (!
|
|
13433
|
-
const account = JSON.parse(
|
|
13735
|
+
const accountJsonPath = join11(configDir2, "account.json");
|
|
13736
|
+
if (!existsSync25(accountJsonPath)) return null;
|
|
13737
|
+
const account = JSON.parse(readFileSync19(accountJsonPath, "utf-8"));
|
|
13434
13738
|
const accountId = account.accountId;
|
|
13435
13739
|
if (!accountId) return null;
|
|
13436
|
-
const cachePath =
|
|
13437
|
-
if (!
|
|
13438
|
-
return JSON.parse(
|
|
13740
|
+
const cachePath = join11(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
13741
|
+
if (!existsSync25(cachePath)) return null;
|
|
13742
|
+
return JSON.parse(readFileSync19(cachePath, "utf-8"));
|
|
13439
13743
|
} catch {
|
|
13440
13744
|
return null;
|
|
13441
13745
|
}
|
|
13442
13746
|
}
|
|
13443
13747
|
function resolveDefaultSlug() {
|
|
13444
13748
|
try {
|
|
13445
|
-
const configDir2 =
|
|
13446
|
-
const accountJsonPath =
|
|
13447
|
-
if (!
|
|
13448
|
-
const account = JSON.parse(
|
|
13749
|
+
const configDir2 = join11(homedir2(), BRAND.configDir);
|
|
13750
|
+
const accountJsonPath = join11(configDir2, "account.json");
|
|
13751
|
+
if (!existsSync25(accountJsonPath)) return null;
|
|
13752
|
+
const account = JSON.parse(readFileSync19(accountJsonPath, "utf-8"));
|
|
13449
13753
|
return account.defaultAgent || null;
|
|
13450
13754
|
} catch {
|
|
13451
13755
|
return null;
|
|
@@ -13454,11 +13758,11 @@ function resolveDefaultSlug() {
|
|
|
13454
13758
|
function brandedPublicHtml(agentSlug) {
|
|
13455
13759
|
const baseHtml = cachedHtml("public.html");
|
|
13456
13760
|
if (!agentSlug) return baseHtml;
|
|
13457
|
-
const
|
|
13761
|
+
const cached2 = brandedHtmlCache.get(agentSlug);
|
|
13458
13762
|
const branding = loadBrandingCache(agentSlug);
|
|
13459
13763
|
if (!branding) return baseHtml;
|
|
13460
13764
|
const brandHash = JSON.stringify(branding).length;
|
|
13461
|
-
if (
|
|
13765
|
+
if (cached2 && cached2.mtime === brandHash) return cached2.html;
|
|
13462
13766
|
const title = escapeHtml(branding.name);
|
|
13463
13767
|
const description = branding.tagline ? escapeHtml(branding.tagline) : "";
|
|
13464
13768
|
const themeColor = branding.primaryColor || "";
|
|
@@ -13518,7 +13822,7 @@ app36.use("/vnc-popout.html", logViewerFetch);
|
|
|
13518
13822
|
app36.get("/vnc-popout.html", (c) => {
|
|
13519
13823
|
let html = htmlCache.get("vnc-popout.html");
|
|
13520
13824
|
if (!html) {
|
|
13521
|
-
html =
|
|
13825
|
+
html = readFileSync19(resolve25(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
13522
13826
|
const name = escapeHtml(BRAND.productName);
|
|
13523
13827
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
13524
13828
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -13608,8 +13912,8 @@ try {
|
|
|
13608
13912
|
(async () => {
|
|
13609
13913
|
try {
|
|
13610
13914
|
let userId = "";
|
|
13611
|
-
if (
|
|
13612
|
-
const users = JSON.parse(
|
|
13915
|
+
if (existsSync25(USERS_FILE)) {
|
|
13916
|
+
const users = JSON.parse(readFileSync19(USERS_FILE, "utf-8").trim() || "[]");
|
|
13613
13917
|
userId = users[0]?.userId ?? "";
|
|
13614
13918
|
}
|
|
13615
13919
|
await backfillNullUserIdConversations(userId);
|
|
@@ -13669,6 +13973,48 @@ for (const dir of bootEnabled) {
|
|
|
13669
13973
|
bootDelivered.push(dir);
|
|
13670
13974
|
}
|
|
13671
13975
|
console.error(`[plugins] readiness enabled=${bootEnabled.length} delivered=${bootDelivered.length} dist-missing=[${bootDistMissing.join(",")}] missing=[${bootMissing.join(",")}]`);
|
|
13976
|
+
(async () => {
|
|
13977
|
+
if (!bootAccount) {
|
|
13978
|
+
console.log("[action-completion-relay] phase=consumed-skip reason=no-account");
|
|
13979
|
+
return;
|
|
13980
|
+
}
|
|
13981
|
+
let records;
|
|
13982
|
+
try {
|
|
13983
|
+
records = consumeActionCompletionRelays(bootAccount.accountDir);
|
|
13984
|
+
} catch (err) {
|
|
13985
|
+
console.error(`[action-completion-relay] phase=consume-failed error=${err instanceof Error ? err.message : String(err)}`);
|
|
13986
|
+
return;
|
|
13987
|
+
}
|
|
13988
|
+
if (records.length === 0) {
|
|
13989
|
+
console.log(`[action-completion-relay] phase=consumed-empty accountId=${bootAccount.accountId.slice(0, 8)}\u2026`);
|
|
13990
|
+
return;
|
|
13991
|
+
}
|
|
13992
|
+
console.log(`[action-completion-relay] phase=consume-batch count=${records.length} accountId=${bootAccount.accountId.slice(0, 8)}\u2026`);
|
|
13993
|
+
for (const rec of records) {
|
|
13994
|
+
const sessionKey = `cloudflare-relay-boot:${rec.actionId}`;
|
|
13995
|
+
let outcome = "injected";
|
|
13996
|
+
let dispatchError;
|
|
13997
|
+
try {
|
|
13998
|
+
registerSession(sessionKey, "admin", bootAccount.accountId);
|
|
13999
|
+
setConversationIdForSession(sessionKey, rec.conversationId);
|
|
14000
|
+
for await (const _ev of invokeAgent({ type: "admin" }, rec.message, sessionKey)) {
|
|
14001
|
+
}
|
|
14002
|
+
} catch (err) {
|
|
14003
|
+
outcome = "dispatch-failed";
|
|
14004
|
+
dispatchError = err instanceof Error ? err.message : String(err);
|
|
14005
|
+
} finally {
|
|
14006
|
+
unregisterSession(sessionKey);
|
|
14007
|
+
}
|
|
14008
|
+
if (outcome === "injected") {
|
|
14009
|
+
deleteConsumedRelay(rec.filePath);
|
|
14010
|
+
console.log(`[action-completion-relay] phase=consumed actionId=${rec.actionId} conversationId=${rec.conversationId} ageMs=${rec.ageMs} outcome=injected`);
|
|
14011
|
+
} else {
|
|
14012
|
+
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)}`);
|
|
14013
|
+
}
|
|
14014
|
+
}
|
|
14015
|
+
})().catch((err) => {
|
|
14016
|
+
console.error(`[action-completion-relay] boot-drain rejected: ${err instanceof Error ? err.message : String(err)}`);
|
|
14017
|
+
});
|
|
13672
14018
|
if (bootAccountConfig?.whatsapp) {
|
|
13673
14019
|
console.error(`[whatsapp:boot] loading whatsapp config from account.json publicAgent=${bootPublicAgent ?? "none"}`);
|
|
13674
14020
|
} else {
|
|
@@ -13676,7 +14022,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
13676
14022
|
}
|
|
13677
14023
|
init({
|
|
13678
14024
|
configDir: configDirForWhatsApp,
|
|
13679
|
-
platformRoot:
|
|
14025
|
+
platformRoot: resolve25(process.env.MAXY_PLATFORM_ROOT ?? join11(__dirname, "..")),
|
|
13680
14026
|
accountConfig: bootAccountConfig,
|
|
13681
14027
|
onMessage: async (msg) => {
|
|
13682
14028
|
try {
|