@rubytech/create-maxy-code 0.1.276 → 0.1.277
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/platform-architecture/SKILL.md +3 -3
- package/payload/platform/plugins/docs/references/admin-identity-gate.md +44 -7
- package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
- package/payload/platform/scripts/check-no-esm-require.mjs +3 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +52 -10
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +8 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts +8 -0
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js +13 -0
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/wa-channel-store.d.ts +29 -0
- package/payload/platform/services/claude-session-manager/dist/wa-channel-store.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/wa-channel-store.js +124 -0
- package/payload/platform/services/claude-session-manager/dist/wa-channel-store.js.map +1 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +23 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +26 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.js +41 -14
- package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
- package/payload/server/{chunk-W4EM7RK4.js → chunk-QHD5TKLQ.js} +2 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/server.js +411 -172
package/payload/server/server.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
Hono,
|
|
12
12
|
HtmlEscapedCallbackPhase,
|
|
13
13
|
INSTALL_OWNER_FILE,
|
|
14
|
+
LID_MAP_FILE,
|
|
14
15
|
LOG_DIR,
|
|
15
16
|
MAXY_DIR,
|
|
16
17
|
PLATFORM_ROOT,
|
|
@@ -95,7 +96,7 @@ import {
|
|
|
95
96
|
vncLog,
|
|
96
97
|
walkPremiumBundles,
|
|
97
98
|
writeAdminUserAndPerson
|
|
98
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-QHD5TKLQ.js";
|
|
99
100
|
import {
|
|
100
101
|
__commonJS,
|
|
101
102
|
__toESM
|
|
@@ -1200,7 +1201,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1200
1201
|
};
|
|
1201
1202
|
|
|
1202
1203
|
// server/index.ts
|
|
1203
|
-
import { readFileSync as
|
|
1204
|
+
import { readFileSync as readFileSync23, existsSync as existsSync24, watchFile } from "fs";
|
|
1204
1205
|
import { resolve as resolve26, join as join18, basename as basename6 } from "path";
|
|
1205
1206
|
import { homedir as homedir2 } from "os";
|
|
1206
1207
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
@@ -1488,7 +1489,7 @@ async function ensureAuth() {
|
|
|
1488
1489
|
}
|
|
1489
1490
|
|
|
1490
1491
|
// server/routes/health.ts
|
|
1491
|
-
import { existsSync as
|
|
1492
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
|
|
1492
1493
|
import { createConnection as createConnection2 } from "net";
|
|
1493
1494
|
|
|
1494
1495
|
// app/lib/network.ts
|
|
@@ -2189,6 +2190,49 @@ async function resolveJidToE164(jid, lidMapping) {
|
|
|
2189
2190
|
return null;
|
|
2190
2191
|
}
|
|
2191
2192
|
|
|
2193
|
+
// app/lib/whatsapp/inbound/lid-map.ts
|
|
2194
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2195
|
+
function resolveViaLidMap(map, senderPhone) {
|
|
2196
|
+
const key = normalizeE164(senderPhone);
|
|
2197
|
+
if (!key) return null;
|
|
2198
|
+
const mapped = map[key];
|
|
2199
|
+
return mapped && mapped.length > 0 ? mapped : null;
|
|
2200
|
+
}
|
|
2201
|
+
function lidCaptureFor(senderJid, senderPhone) {
|
|
2202
|
+
if (!isLidJid(senderJid)) return null;
|
|
2203
|
+
const numericLid = jidToE164(senderJid);
|
|
2204
|
+
if (!numericLid) return null;
|
|
2205
|
+
const e164 = normalizeE164(senderPhone);
|
|
2206
|
+
if (!e164 || e164 === numericLid) return null;
|
|
2207
|
+
return { numericLid, e164 };
|
|
2208
|
+
}
|
|
2209
|
+
function readLidMap() {
|
|
2210
|
+
try {
|
|
2211
|
+
if (!existsSync2(LID_MAP_FILE)) return null;
|
|
2212
|
+
const raw = readFileSync2(LID_MAP_FILE, "utf-8").trim();
|
|
2213
|
+
if (!raw) return null;
|
|
2214
|
+
const parsed = JSON.parse(raw);
|
|
2215
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
2216
|
+
return parsed;
|
|
2217
|
+
} catch {
|
|
2218
|
+
return null;
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
function recordLidMapping(numericLid, e164) {
|
|
2222
|
+
try {
|
|
2223
|
+
const key = normalizeE164(numericLid);
|
|
2224
|
+
const val = normalizeE164(e164);
|
|
2225
|
+
if (!key || !val) return;
|
|
2226
|
+
const map = readLidMap() ?? {};
|
|
2227
|
+
if (map[key] === val) return;
|
|
2228
|
+
map[key] = val;
|
|
2229
|
+
writeFileSync2(LID_MAP_FILE, JSON.stringify(map, null, 2));
|
|
2230
|
+
console.error(`[lid-map] recorded lid=${key} userId-phone=present`);
|
|
2231
|
+
} catch (err) {
|
|
2232
|
+
console.error(`[lid-map] record-failed lid=${numericLid}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2192
2236
|
// app/lib/whatsapp/inbound/extract.ts
|
|
2193
2237
|
import {
|
|
2194
2238
|
extractMessageContent,
|
|
@@ -2743,7 +2787,7 @@ async function ensureWhatsAppConversation(input) {
|
|
|
2743
2787
|
}
|
|
2744
2788
|
|
|
2745
2789
|
// ../lib/account-enumeration/src/index.ts
|
|
2746
|
-
import { readdirSync, readFileSync as
|
|
2790
|
+
import { readdirSync, readFileSync as readFileSync3 } from "fs";
|
|
2747
2791
|
import { resolve } from "path";
|
|
2748
2792
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2749
2793
|
var cache = /* @__PURE__ */ new Map();
|
|
@@ -2765,7 +2809,7 @@ function enumerateValidAccountIds(accountsDir) {
|
|
|
2765
2809
|
if (!UUID_RE.test(name)) continue;
|
|
2766
2810
|
const configPath2 = resolve(accountsDir, name, "account.json");
|
|
2767
2811
|
try {
|
|
2768
|
-
JSON.parse(
|
|
2812
|
+
JSON.parse(readFileSync3(configPath2, "utf-8"));
|
|
2769
2813
|
valid.push(name);
|
|
2770
2814
|
} catch (err) {
|
|
2771
2815
|
const code = err.code;
|
|
@@ -3877,6 +3921,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
3877
3921
|
const isGroup = isGroupJid(remoteJid);
|
|
3878
3922
|
const senderJid = isGroup ? msg.key.participant ?? remoteJid : remoteJid;
|
|
3879
3923
|
const senderPhone = await resolveJidToE164(senderJid, conn.lidMapping) ?? senderJid;
|
|
3924
|
+
const lidCapture = lidCaptureFor(senderJid, senderPhone);
|
|
3925
|
+
if (lidCapture) recordLidMapping(lidCapture.numericLid, lidCapture.e164);
|
|
3880
3926
|
const selfPhone = conn.selfPhone ?? "";
|
|
3881
3927
|
if (extracted.mediaType === "audio" && mediaResult?.path) {
|
|
3882
3928
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
@@ -3997,7 +4043,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
3997
4043
|
// app/lib/vnc.ts
|
|
3998
4044
|
import { spawnSync, execFileSync } from "child_process";
|
|
3999
4045
|
import { createConnection } from "net";
|
|
4000
|
-
import { mkdirSync, readFileSync as
|
|
4046
|
+
import { mkdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
4001
4047
|
import { resolve as resolve2 } from "path";
|
|
4002
4048
|
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "..");
|
|
4003
4049
|
var VNC_SCRIPT = resolve2(PLATFORM_ROOT2, "scripts/vnc.sh");
|
|
@@ -4055,7 +4101,7 @@ function discoverNativeDisplay() {
|
|
|
4055
4101
|
const leaderPid = leaderResult.stdout?.trim();
|
|
4056
4102
|
if (leaderPid) {
|
|
4057
4103
|
try {
|
|
4058
|
-
const environ =
|
|
4104
|
+
const environ = readFileSync4(`/proc/${leaderPid}/environ`, "utf8");
|
|
4059
4105
|
const match = environ.split("\0").find((e) => e.startsWith("WAYLAND_DISPLAY="));
|
|
4060
4106
|
if (match) waylandDisplay = match.split("=")[1];
|
|
4061
4107
|
} catch {
|
|
@@ -4193,7 +4239,7 @@ function killChromium() {
|
|
|
4193
4239
|
function writeChromiumWrapper() {
|
|
4194
4240
|
mkdirSync(BIN_DIR, { recursive: true });
|
|
4195
4241
|
const wrapperPath = resolve2(BIN_DIR, "chromium");
|
|
4196
|
-
|
|
4242
|
+
writeFileSync3(wrapperPath, `#!/bin/bash
|
|
4197
4243
|
LOG="${LOG_DIR}/chromium.log"
|
|
4198
4244
|
echo "==== [$(date)] chromium wrapper ====" >> "$LOG"
|
|
4199
4245
|
echo " DISPLAY=$DISPLAY WAYLAND=$WAYLAND_DISPLAY XDG_SESSION_TYPE=$XDG_SESSION_TYPE" >> "$LOG"
|
|
@@ -4314,8 +4360,8 @@ app.get("/", async (c) => {
|
|
|
4314
4360
|
const browserTransport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
|
|
4315
4361
|
let pinConfigured = false;
|
|
4316
4362
|
try {
|
|
4317
|
-
if (
|
|
4318
|
-
const raw =
|
|
4363
|
+
if (existsSync3(USERS_FILE)) {
|
|
4364
|
+
const raw = readFileSync5(USERS_FILE, "utf-8").trim();
|
|
4319
4365
|
if (raw) {
|
|
4320
4366
|
const users = JSON.parse(raw);
|
|
4321
4367
|
pinConfigured = Array.isArray(users) && users.length > 0;
|
|
@@ -4634,6 +4680,7 @@ async function managerRcSpawn(opts) {
|
|
|
4634
4680
|
closeAfterTurn: opts.closeAfterTurn,
|
|
4635
4681
|
sliceToken: opts.sliceToken,
|
|
4636
4682
|
personId: opts.personId,
|
|
4683
|
+
userId: opts.userId,
|
|
4637
4684
|
waChannel: opts.waChannel
|
|
4638
4685
|
})
|
|
4639
4686
|
}).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
|
|
@@ -5753,13 +5800,13 @@ var chat_default = app2;
|
|
|
5753
5800
|
|
|
5754
5801
|
// server/routes/whatsapp.ts
|
|
5755
5802
|
import { join as join6, resolve as resolve7 } from "path";
|
|
5756
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
5803
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync7, existsSync as existsSync5 } from "fs";
|
|
5757
5804
|
|
|
5758
5805
|
// app/lib/whatsapp/login.ts
|
|
5759
5806
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
5760
5807
|
|
|
5761
5808
|
// app/lib/whatsapp/config-persist.ts
|
|
5762
|
-
import { readFileSync as
|
|
5809
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "fs";
|
|
5763
5810
|
import { resolve as resolve6, join as join4 } from "path";
|
|
5764
5811
|
var TAG17 = "[whatsapp:config]";
|
|
5765
5812
|
function configPath(accountDir) {
|
|
@@ -5767,12 +5814,12 @@ function configPath(accountDir) {
|
|
|
5767
5814
|
}
|
|
5768
5815
|
function readConfig(accountDir) {
|
|
5769
5816
|
const path2 = configPath(accountDir);
|
|
5770
|
-
if (!
|
|
5771
|
-
return JSON.parse(
|
|
5817
|
+
if (!existsSync4(path2)) throw new Error(`account.json not found at ${path2}`);
|
|
5818
|
+
return JSON.parse(readFileSync6(path2, "utf-8"));
|
|
5772
5819
|
}
|
|
5773
5820
|
function writeConfig(accountDir, config) {
|
|
5774
5821
|
const path2 = configPath(accountDir);
|
|
5775
|
-
|
|
5822
|
+
writeFileSync4(path2, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
5776
5823
|
}
|
|
5777
5824
|
function reloadManagerConfig(accountDir) {
|
|
5778
5825
|
try {
|
|
@@ -5913,7 +5960,7 @@ function setPublicAgent(accountDir, slug) {
|
|
|
5913
5960
|
return { ok: false, error: "Agent slug cannot be empty." };
|
|
5914
5961
|
}
|
|
5915
5962
|
const agentConfigPath = join4(accountDir, "agents", trimmed, "config.json");
|
|
5916
|
-
if (!
|
|
5963
|
+
if (!existsSync4(agentConfigPath)) {
|
|
5917
5964
|
return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
|
|
5918
5965
|
}
|
|
5919
5966
|
try {
|
|
@@ -5977,7 +6024,7 @@ function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
|
|
|
5977
6024
|
if (!trimmedGroup) return { ok: false, error: "Group JID cannot be empty." };
|
|
5978
6025
|
if (!trimmedAccount) return { ok: false, error: "Account ID cannot be empty." };
|
|
5979
6026
|
const agentConfigPath = join4(accountDir, "agents", trimmedSlug, "config.json");
|
|
5980
|
-
if (!
|
|
6027
|
+
if (!existsSync4(agentConfigPath)) {
|
|
5981
6028
|
return { ok: false, error: `Agent "${trimmedSlug}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
|
|
5982
6029
|
}
|
|
5983
6030
|
try {
|
|
@@ -6663,15 +6710,15 @@ app3.post("/config", async (c) => {
|
|
|
6663
6710
|
case "list-public-agents": {
|
|
6664
6711
|
const agentsDir = resolve7(account.accountDir, "agents");
|
|
6665
6712
|
const agents = [];
|
|
6666
|
-
if (
|
|
6713
|
+
if (existsSync5(agentsDir)) {
|
|
6667
6714
|
try {
|
|
6668
6715
|
const entries = readdirSync3(agentsDir, { withFileTypes: true });
|
|
6669
6716
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
6670
6717
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
6671
6718
|
const configPath2 = resolve7(agentsDir, entry.name, "config.json");
|
|
6672
|
-
if (!
|
|
6719
|
+
if (!existsSync5(configPath2)) continue;
|
|
6673
6720
|
try {
|
|
6674
|
-
const config = JSON.parse(
|
|
6721
|
+
const config = JSON.parse(readFileSync7(configPath2, "utf-8"));
|
|
6675
6722
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
6676
6723
|
} catch {
|
|
6677
6724
|
console.error(`${TAG20} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
@@ -6941,11 +6988,11 @@ var whatsapp_default = app3;
|
|
|
6941
6988
|
|
|
6942
6989
|
// server/routes/onboarding.ts
|
|
6943
6990
|
import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
|
|
6944
|
-
import { openSync, closeSync, writeFileSync as
|
|
6991
|
+
import { openSync, closeSync, writeFileSync as writeFileSync6, writeSync, existsSync as existsSync7, readFileSync as readFileSync9, unlinkSync } from "fs";
|
|
6945
6992
|
import { createHash as createHash2, randomUUID as randomUUID7 } from "crypto";
|
|
6946
6993
|
|
|
6947
6994
|
// ../lib/admins-write/src/index.ts
|
|
6948
|
-
import { existsSync as
|
|
6995
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
6949
6996
|
import { dirname, join as join7 } from "path";
|
|
6950
6997
|
function logLine(input, result) {
|
|
6951
6998
|
const userIdShort = input.userId.slice(0, 8);
|
|
@@ -6956,15 +7003,15 @@ function logLine(input, result) {
|
|
|
6956
7003
|
function writeFileAtomic(filePath, contents, mode) {
|
|
6957
7004
|
mkdirSync2(dirname(filePath), { recursive: true });
|
|
6958
7005
|
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
6959
|
-
|
|
7006
|
+
writeFileSync5(tempPath, contents, mode !== void 0 ? { mode } : void 0);
|
|
6960
7007
|
renameSync(tempPath, filePath);
|
|
6961
7008
|
}
|
|
6962
7009
|
function writeAdminEntry(input) {
|
|
6963
7010
|
const result = { usersJsonResult: "noop", accountJsonResult: "noop" };
|
|
6964
7011
|
try {
|
|
6965
7012
|
let users = [];
|
|
6966
|
-
if (
|
|
6967
|
-
const raw =
|
|
7013
|
+
if (existsSync6(input.usersFile)) {
|
|
7014
|
+
const raw = readFileSync8(input.usersFile, "utf-8").trim();
|
|
6968
7015
|
if (raw) {
|
|
6969
7016
|
const parsed = JSON.parse(raw);
|
|
6970
7017
|
if (!Array.isArray(parsed)) {
|
|
@@ -6989,10 +7036,10 @@ function writeAdminEntry(input) {
|
|
|
6989
7036
|
}
|
|
6990
7037
|
try {
|
|
6991
7038
|
const accountJsonPath = join7(input.accountDir, "account.json");
|
|
6992
|
-
if (!
|
|
7039
|
+
if (!existsSync6(accountJsonPath)) {
|
|
6993
7040
|
throw new Error(`account.json not found at ${accountJsonPath}`);
|
|
6994
7041
|
}
|
|
6995
|
-
const config = JSON.parse(
|
|
7042
|
+
const config = JSON.parse(readFileSync8(accountJsonPath, "utf-8"));
|
|
6996
7043
|
const admins = config.admins ?? [];
|
|
6997
7044
|
if (admins.some((a) => a.userId === input.userId)) {
|
|
6998
7045
|
result.accountJsonResult = "noop";
|
|
@@ -7016,9 +7063,9 @@ function checkAdminAuthInvariant(input) {
|
|
|
7016
7063
|
usersWithoutAccount: []
|
|
7017
7064
|
};
|
|
7018
7065
|
const usersUserIds = /* @__PURE__ */ new Set();
|
|
7019
|
-
if (
|
|
7066
|
+
if (existsSync6(input.usersFile)) {
|
|
7020
7067
|
try {
|
|
7021
|
-
const raw =
|
|
7068
|
+
const raw = readFileSync8(input.usersFile, "utf-8").trim();
|
|
7022
7069
|
if (raw) {
|
|
7023
7070
|
const users = JSON.parse(raw);
|
|
7024
7071
|
for (const u of users) {
|
|
@@ -7031,7 +7078,7 @@ function checkAdminAuthInvariant(input) {
|
|
|
7031
7078
|
}
|
|
7032
7079
|
}
|
|
7033
7080
|
const accountUserIds = /* @__PURE__ */ new Set();
|
|
7034
|
-
if (
|
|
7081
|
+
if (existsSync6(input.accountsDir)) {
|
|
7035
7082
|
let entries;
|
|
7036
7083
|
try {
|
|
7037
7084
|
entries = readdirSync4(input.accountsDir);
|
|
@@ -7050,10 +7097,10 @@ function checkAdminAuthInvariant(input) {
|
|
|
7050
7097
|
continue;
|
|
7051
7098
|
}
|
|
7052
7099
|
const accountJsonPath = join7(accountDir, "account.json");
|
|
7053
|
-
if (!
|
|
7100
|
+
if (!existsSync6(accountJsonPath)) continue;
|
|
7054
7101
|
let admins = [];
|
|
7055
7102
|
try {
|
|
7056
|
-
const config = JSON.parse(
|
|
7103
|
+
const config = JSON.parse(readFileSync8(accountJsonPath, "utf-8"));
|
|
7057
7104
|
admins = config.admins ?? [];
|
|
7058
7105
|
} catch (err) {
|
|
7059
7106
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -7093,8 +7140,8 @@ function hashPin(pin) {
|
|
|
7093
7140
|
return createHash2("sha256").update(pin).digest("hex");
|
|
7094
7141
|
}
|
|
7095
7142
|
function readUsersFile() {
|
|
7096
|
-
if (!
|
|
7097
|
-
const raw =
|
|
7143
|
+
if (!existsSync7(USERS_FILE)) return null;
|
|
7144
|
+
const raw = readFileSync9(USERS_FILE, "utf-8").trim();
|
|
7098
7145
|
if (!raw) return [];
|
|
7099
7146
|
return JSON.parse(raw);
|
|
7100
7147
|
}
|
|
@@ -7159,7 +7206,7 @@ app4.post("/claude-auth", async (c) => {
|
|
|
7159
7206
|
return c.json({ launched: cdpReady });
|
|
7160
7207
|
}
|
|
7161
7208
|
ensureLogDir();
|
|
7162
|
-
|
|
7209
|
+
writeFileSync6(logPath("claude-auth"), "");
|
|
7163
7210
|
const claudeAuthLogFd = openSync(logPath("claude-auth"), "a");
|
|
7164
7211
|
const onClaudeOutput = (chunk) => writeSync(claudeAuthLogFd, chunk);
|
|
7165
7212
|
if (process.platform === "darwin") {
|
|
@@ -7248,9 +7295,9 @@ app4.post("/set-pin", async (c) => {
|
|
|
7248
7295
|
console.log(`[set-pin] wrote users.json + account.json admins: userId=${userId.slice(0, 8)}\u2026 role=owner`);
|
|
7249
7296
|
if (process.platform === "linux") {
|
|
7250
7297
|
let installOwner = null;
|
|
7251
|
-
if (
|
|
7298
|
+
if (existsSync7(INSTALL_OWNER_FILE)) {
|
|
7252
7299
|
try {
|
|
7253
|
-
const raw =
|
|
7300
|
+
const raw = readFileSync9(INSTALL_OWNER_FILE, "utf-8").trim();
|
|
7254
7301
|
if (raw.length > 0) installOwner = raw;
|
|
7255
7302
|
} catch (err) {
|
|
7256
7303
|
console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -7323,7 +7370,7 @@ app4.delete("/set-pin", async (c) => {
|
|
|
7323
7370
|
unlinkSync(USERS_FILE);
|
|
7324
7371
|
console.log(`[set-pin] cleared users.json (last entry removed): userId=${matchedUser.userId.slice(0, 8)}\u2026`);
|
|
7325
7372
|
} else {
|
|
7326
|
-
|
|
7373
|
+
writeFileSync6(USERS_FILE, JSON.stringify(remaining), { mode: 384 });
|
|
7327
7374
|
console.log(`[set-pin] removed entry from users.json: userId=${matchedUser.userId.slice(0, 8)}\u2026 remaining=${remaining.length}`);
|
|
7328
7375
|
}
|
|
7329
7376
|
return c.json({ ok: true });
|
|
@@ -7331,7 +7378,7 @@ app4.delete("/set-pin", async (c) => {
|
|
|
7331
7378
|
var onboarding_default = app4;
|
|
7332
7379
|
|
|
7333
7380
|
// server/routes/client-error.ts
|
|
7334
|
-
import { appendFileSync, existsSync as
|
|
7381
|
+
import { appendFileSync, existsSync as existsSync8, renameSync as renameSync2, statSync as statSync3 } from "fs";
|
|
7335
7382
|
import { join as join8 } from "path";
|
|
7336
7383
|
var CLIENT_ERRORS_LOG = join8(LOG_DIR, "client-errors.log");
|
|
7337
7384
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
@@ -7376,7 +7423,7 @@ function stackHead(stack) {
|
|
|
7376
7423
|
}
|
|
7377
7424
|
function rotateIfNeeded() {
|
|
7378
7425
|
try {
|
|
7379
|
-
if (!
|
|
7426
|
+
if (!existsSync8(CLIENT_ERRORS_LOG)) return;
|
|
7380
7427
|
const stats = statSync3(CLIENT_ERRORS_LOG);
|
|
7381
7428
|
if (stats.size < MAX_LOG_SIZE) return;
|
|
7382
7429
|
renameSync2(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
|
|
@@ -7494,14 +7541,14 @@ import { randomUUID as randomUUID8 } from "crypto";
|
|
|
7494
7541
|
|
|
7495
7542
|
// app/lib/admin-identity/pin-validator.ts
|
|
7496
7543
|
import { createHash as createHash3 } from "crypto";
|
|
7497
|
-
import { existsSync as
|
|
7544
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
7498
7545
|
import { join as join9 } from "path";
|
|
7499
7546
|
function hashPin2(pin) {
|
|
7500
7547
|
return createHash3("sha256").update(pin).digest("hex");
|
|
7501
7548
|
}
|
|
7502
7549
|
function readUsersFile2(usersFilePath) {
|
|
7503
|
-
if (!
|
|
7504
|
-
const raw =
|
|
7550
|
+
if (!existsSync9(usersFilePath)) return null;
|
|
7551
|
+
const raw = readFileSync10(usersFilePath, "utf-8").trim();
|
|
7505
7552
|
if (!raw) return [];
|
|
7506
7553
|
return JSON.parse(raw);
|
|
7507
7554
|
}
|
|
@@ -7520,7 +7567,7 @@ function validatePin(pin, usersFilePath) {
|
|
|
7520
7567
|
function scanForOrphanedAccountAdmins(users, accountsDir) {
|
|
7521
7568
|
const known = new Set(users.map((u) => u.userId));
|
|
7522
7569
|
const orphans = [];
|
|
7523
|
-
if (!
|
|
7570
|
+
if (!existsSync9(accountsDir)) return orphans;
|
|
7524
7571
|
let entries;
|
|
7525
7572
|
try {
|
|
7526
7573
|
entries = readdirSync5(accountsDir);
|
|
@@ -7536,9 +7583,9 @@ function scanForOrphanedAccountAdmins(users, accountsDir) {
|
|
|
7536
7583
|
continue;
|
|
7537
7584
|
}
|
|
7538
7585
|
const accountJsonPath = join9(accountDir, "account.json");
|
|
7539
|
-
if (!
|
|
7586
|
+
if (!existsSync9(accountJsonPath)) continue;
|
|
7540
7587
|
try {
|
|
7541
|
-
const config = JSON.parse(
|
|
7588
|
+
const config = JSON.parse(readFileSync10(accountJsonPath, "utf-8"));
|
|
7542
7589
|
const admins = config.admins ?? [];
|
|
7543
7590
|
for (const a of admins) {
|
|
7544
7591
|
if (typeof a.userId === "string" && !known.has(a.userId)) orphans.push(a.userId);
|
|
@@ -7718,18 +7765,18 @@ app6.post("/", async (c) => {
|
|
|
7718
7765
|
var session_default = app6;
|
|
7719
7766
|
|
|
7720
7767
|
// server/routes/admin/logs.ts
|
|
7721
|
-
import { existsSync as
|
|
7768
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync11, statSync as statSync5 } from "fs";
|
|
7722
7769
|
import { resolve as resolve8, basename as basename2 } from "path";
|
|
7723
7770
|
|
|
7724
7771
|
// app/lib/logs-read-resolve.ts
|
|
7725
|
-
import { existsSync as
|
|
7772
|
+
import { existsSync as existsSync10 } from "fs";
|
|
7726
7773
|
import { join as join10 } from "path";
|
|
7727
7774
|
function resolveSessionLogPaths(filename, logDirs) {
|
|
7728
7775
|
const tried = [filename];
|
|
7729
7776
|
const hits = [];
|
|
7730
7777
|
for (const dir of logDirs) {
|
|
7731
7778
|
const fullPath = join10(dir, filename);
|
|
7732
|
-
if (
|
|
7779
|
+
if (existsSync10(fullPath)) {
|
|
7733
7780
|
hits.push({ path: fullPath, dir });
|
|
7734
7781
|
}
|
|
7735
7782
|
}
|
|
@@ -7757,7 +7804,7 @@ app7.get("/", async (c) => {
|
|
|
7757
7804
|
const filePath = resolve8(dir, safe);
|
|
7758
7805
|
searched.push(filePath);
|
|
7759
7806
|
try {
|
|
7760
|
-
const buffer =
|
|
7807
|
+
const buffer = readFileSync11(filePath);
|
|
7761
7808
|
const onDiskBytes = statSync5(filePath).size;
|
|
7762
7809
|
const headers = {
|
|
7763
7810
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -7822,7 +7869,7 @@ app7.get("/", async (c) => {
|
|
|
7822
7869
|
console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
|
|
7823
7870
|
try {
|
|
7824
7871
|
const filename = basename2(hit.path);
|
|
7825
|
-
const buffer =
|
|
7872
|
+
const buffer = readFileSync11(hit.path);
|
|
7826
7873
|
const onDiskBytes = statSync5(hit.path).size;
|
|
7827
7874
|
const headers = {
|
|
7828
7875
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -7857,7 +7904,7 @@ app7.get("/", async (c) => {
|
|
|
7857
7904
|
const seen = /* @__PURE__ */ new Set();
|
|
7858
7905
|
const logs = {};
|
|
7859
7906
|
for (const dir of logDirs) {
|
|
7860
|
-
if (!
|
|
7907
|
+
if (!existsSync11(dir)) continue;
|
|
7861
7908
|
let files;
|
|
7862
7909
|
try {
|
|
7863
7910
|
files = readdirSync6(dir).filter((f) => f.endsWith(".log"));
|
|
@@ -7869,7 +7916,7 @@ app7.get("/", async (c) => {
|
|
|
7869
7916
|
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync5(resolve8(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
7870
7917
|
seen.add(name);
|
|
7871
7918
|
try {
|
|
7872
|
-
const content =
|
|
7919
|
+
const content = readFileSync11(resolve8(dir, name));
|
|
7873
7920
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
7874
7921
|
logs[name] = tail.trim() || "(empty)";
|
|
7875
7922
|
} catch (err) {
|
|
@@ -7908,7 +7955,7 @@ var claude_info_default = app8;
|
|
|
7908
7955
|
|
|
7909
7956
|
// server/routes/admin/attachment.ts
|
|
7910
7957
|
import { readFile as readFile2, readdir } from "fs/promises";
|
|
7911
|
-
import { existsSync as
|
|
7958
|
+
import { existsSync as existsSync12 } from "fs";
|
|
7912
7959
|
import { resolve as resolve9 } from "path";
|
|
7913
7960
|
var app9 = new Hono();
|
|
7914
7961
|
app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
@@ -7922,11 +7969,11 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
7922
7969
|
return new Response("Not found", { status: 404 });
|
|
7923
7970
|
}
|
|
7924
7971
|
const dir = resolve9(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
7925
|
-
if (!
|
|
7972
|
+
if (!existsSync12(dir)) {
|
|
7926
7973
|
return new Response("Not found", { status: 404 });
|
|
7927
7974
|
}
|
|
7928
7975
|
const metaPath = resolve9(dir, `${attachmentId}.meta.json`);
|
|
7929
|
-
if (!
|
|
7976
|
+
if (!existsSync12(metaPath)) {
|
|
7930
7977
|
return new Response("Not found", { status: 404 });
|
|
7931
7978
|
}
|
|
7932
7979
|
let meta;
|
|
@@ -7954,13 +8001,13 @@ var attachment_default = app9;
|
|
|
7954
8001
|
|
|
7955
8002
|
// server/routes/admin/agents.ts
|
|
7956
8003
|
import { resolve as resolve10 } from "path";
|
|
7957
|
-
import { readdirSync as readdirSync7, readFileSync as
|
|
8004
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync12, existsSync as existsSync13, rmSync } from "fs";
|
|
7958
8005
|
var app10 = new Hono();
|
|
7959
8006
|
app10.get("/", (c) => {
|
|
7960
8007
|
const account = resolveAccount();
|
|
7961
8008
|
if (!account) return c.json({ agents: [] });
|
|
7962
8009
|
const agentsDir = resolve10(account.accountDir, "agents");
|
|
7963
|
-
if (!
|
|
8010
|
+
if (!existsSync13(agentsDir)) return c.json({ agents: [] });
|
|
7964
8011
|
const agents = [];
|
|
7965
8012
|
try {
|
|
7966
8013
|
const entries = readdirSync7(agentsDir, { withFileTypes: true });
|
|
@@ -7968,9 +8015,9 @@ app10.get("/", (c) => {
|
|
|
7968
8015
|
if (!entry.isDirectory()) continue;
|
|
7969
8016
|
if (entry.name === "admin") continue;
|
|
7970
8017
|
const configPath2 = resolve10(agentsDir, entry.name, "config.json");
|
|
7971
|
-
if (!
|
|
8018
|
+
if (!existsSync13(configPath2)) continue;
|
|
7972
8019
|
try {
|
|
7973
|
-
const config = JSON.parse(
|
|
8020
|
+
const config = JSON.parse(readFileSync12(configPath2, "utf-8"));
|
|
7974
8021
|
agents.push({
|
|
7975
8022
|
slug: entry.name,
|
|
7976
8023
|
displayName: config.displayName ?? entry.name,
|
|
@@ -7997,7 +8044,7 @@ app10.delete("/:slug", async (c) => {
|
|
|
7997
8044
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
7998
8045
|
}
|
|
7999
8046
|
const agentDir = resolve10(account.accountDir, "agents", slug);
|
|
8000
|
-
if (!
|
|
8047
|
+
if (!existsSync13(agentDir)) {
|
|
8001
8048
|
return c.json({ error: "Agent not found" }, 404);
|
|
8002
8049
|
}
|
|
8003
8050
|
try {
|
|
@@ -8027,7 +8074,7 @@ app10.post("/:slug/project", async (c) => {
|
|
|
8027
8074
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
8028
8075
|
}
|
|
8029
8076
|
const agentDir = resolve10(account.accountDir, "agents", slug);
|
|
8030
|
-
if (!
|
|
8077
|
+
if (!existsSync13(agentDir)) {
|
|
8031
8078
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
8032
8079
|
}
|
|
8033
8080
|
try {
|
|
@@ -8043,7 +8090,7 @@ var agents_default = app10;
|
|
|
8043
8090
|
// server/routes/admin/sessions.ts
|
|
8044
8091
|
import crypto2 from "crypto";
|
|
8045
8092
|
import { resolve as resolvePath } from "path";
|
|
8046
|
-
import { existsSync as
|
|
8093
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync3, createWriteStream } from "fs";
|
|
8047
8094
|
|
|
8048
8095
|
// ../lib/admin-conversation-purge/src/index.ts
|
|
8049
8096
|
async function purgeAdminConversationJsonls(args) {
|
|
@@ -8203,7 +8250,7 @@ function validateAndShapeAttachments(raws, conversationAccountId, sessionId, mes
|
|
|
8203
8250
|
let reason = null;
|
|
8204
8251
|
if (!a.attachmentId || !a.filename || !a.mimeType || !a.storagePath) reason = "schema-fail";
|
|
8205
8252
|
else if (a.accountId !== conversationAccountId) reason = "account-mismatch";
|
|
8206
|
-
else if (!
|
|
8253
|
+
else if (!existsSync14(a.storagePath)) reason = "missing-file";
|
|
8207
8254
|
if (reason) {
|
|
8208
8255
|
invalid++;
|
|
8209
8256
|
try {
|
|
@@ -8730,7 +8777,7 @@ app11.put("/:id/label", requireAdminSession, async (c) => {
|
|
|
8730
8777
|
var sessions_default = app11;
|
|
8731
8778
|
|
|
8732
8779
|
// app/lib/claude-agent/spawn-context.ts
|
|
8733
|
-
import { existsSync as
|
|
8780
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13, readdirSync as readdirSync8, statSync as statSync6 } from "fs";
|
|
8734
8781
|
import { dirname as dirname2, resolve as resolve11, join as join11 } from "path";
|
|
8735
8782
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
8736
8783
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
@@ -8765,9 +8812,9 @@ function extractPluginToolDescriptions(pluginDir) {
|
|
|
8765
8812
|
];
|
|
8766
8813
|
let raw = null;
|
|
8767
8814
|
for (const path2 of candidates) {
|
|
8768
|
-
if (!
|
|
8815
|
+
if (!existsSync15(path2)) continue;
|
|
8769
8816
|
try {
|
|
8770
|
-
raw =
|
|
8817
|
+
raw = readFileSync13(path2, "utf-8");
|
|
8771
8818
|
break;
|
|
8772
8819
|
} catch {
|
|
8773
8820
|
}
|
|
@@ -8817,7 +8864,7 @@ function parseSkillPathsFromFrontmatter(fm) {
|
|
|
8817
8864
|
function listPluginDirs() {
|
|
8818
8865
|
const out = /* @__PURE__ */ new Map();
|
|
8819
8866
|
const platformPlugins = resolve11(PLATFORM_ROOT, "plugins");
|
|
8820
|
-
if (
|
|
8867
|
+
if (existsSync15(platformPlugins)) {
|
|
8821
8868
|
for (const name of readdirSync8(platformPlugins)) {
|
|
8822
8869
|
const dir = join11(platformPlugins, name);
|
|
8823
8870
|
try {
|
|
@@ -8827,10 +8874,10 @@ function listPluginDirs() {
|
|
|
8827
8874
|
}
|
|
8828
8875
|
}
|
|
8829
8876
|
const premiumRoot = resolve11(dirname2(PLATFORM_ROOT), "premium-plugins");
|
|
8830
|
-
if (
|
|
8877
|
+
if (existsSync15(premiumRoot)) {
|
|
8831
8878
|
for (const bundle of readdirSync8(premiumRoot)) {
|
|
8832
8879
|
const bundlePlugins = join11(premiumRoot, bundle, "plugins");
|
|
8833
|
-
if (!
|
|
8880
|
+
if (!existsSync15(bundlePlugins)) continue;
|
|
8834
8881
|
try {
|
|
8835
8882
|
for (const name of readdirSync8(bundlePlugins)) {
|
|
8836
8883
|
const dir = join11(bundlePlugins, name);
|
|
@@ -8847,9 +8894,9 @@ function listPluginDirs() {
|
|
|
8847
8894
|
}
|
|
8848
8895
|
function readEnabledPlugins(accountId) {
|
|
8849
8896
|
const accountFile = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
8850
|
-
if (!
|
|
8897
|
+
if (!existsSync15(accountFile)) return /* @__PURE__ */ new Set();
|
|
8851
8898
|
try {
|
|
8852
|
-
const parsed = JSON.parse(
|
|
8899
|
+
const parsed = JSON.parse(readFileSync13(accountFile, "utf-8"));
|
|
8853
8900
|
return new Set(
|
|
8854
8901
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
8855
8902
|
);
|
|
@@ -8858,10 +8905,10 @@ function readEnabledPlugins(accountId) {
|
|
|
8858
8905
|
}
|
|
8859
8906
|
}
|
|
8860
8907
|
function readFrontmatter(path2) {
|
|
8861
|
-
if (!
|
|
8908
|
+
if (!existsSync15(path2)) return null;
|
|
8862
8909
|
let raw;
|
|
8863
8910
|
try {
|
|
8864
|
-
raw =
|
|
8911
|
+
raw = readFileSync13(path2, "utf-8");
|
|
8865
8912
|
} catch {
|
|
8866
8913
|
return null;
|
|
8867
8914
|
}
|
|
@@ -8893,7 +8940,7 @@ ${fm}
|
|
|
8893
8940
|
`plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
|
|
8894
8941
|
);
|
|
8895
8942
|
if (toolNames.length > 0 && descMap.size === 0) {
|
|
8896
|
-
const hasSource =
|
|
8943
|
+
const hasSource = existsSync15(join11(dir, "mcp/dist/index.js")) || existsSync15(join11(dir, "mcp/src/index.ts"));
|
|
8897
8944
|
if (hasSource) {
|
|
8898
8945
|
console.warn(
|
|
8899
8946
|
`[spawn-context] WARN plugin=${name} mcp source present but tool-description regex matched zero entries \u2014 SDK upgrade may have changed the registration shape`
|
|
@@ -8920,7 +8967,7 @@ ${skillFm}
|
|
|
8920
8967
|
}
|
|
8921
8968
|
function computeSpecialistDomains(accountId) {
|
|
8922
8969
|
const specialistsDir = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
|
|
8923
|
-
if (!
|
|
8970
|
+
if (!existsSync15(specialistsDir)) return [];
|
|
8924
8971
|
let entries;
|
|
8925
8972
|
try {
|
|
8926
8973
|
entries = readdirSync8(specialistsDir);
|
|
@@ -8966,10 +9013,10 @@ ${fm}
|
|
|
8966
9013
|
}
|
|
8967
9014
|
function computeDormantPlugins(accountId) {
|
|
8968
9015
|
const accountFile = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
8969
|
-
if (!
|
|
9016
|
+
if (!existsSync15(accountFile)) return [];
|
|
8970
9017
|
let enabled;
|
|
8971
9018
|
try {
|
|
8972
|
-
const raw =
|
|
9019
|
+
const raw = readFileSync13(accountFile, "utf-8");
|
|
8973
9020
|
const parsed = JSON.parse(raw);
|
|
8974
9021
|
enabled = new Set(
|
|
8975
9022
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
@@ -8981,7 +9028,7 @@ function computeDormantPlugins(accountId) {
|
|
|
8981
9028
|
return [];
|
|
8982
9029
|
}
|
|
8983
9030
|
const pluginsDir = resolve11(PLATFORM_ROOT, "plugins");
|
|
8984
|
-
if (!
|
|
9031
|
+
if (!existsSync15(pluginsDir)) return [];
|
|
8985
9032
|
let entries;
|
|
8986
9033
|
try {
|
|
8987
9034
|
entries = readdirSync8(pluginsDir);
|
|
@@ -8992,10 +9039,10 @@ function computeDormantPlugins(accountId) {
|
|
|
8992
9039
|
for (const name of entries) {
|
|
8993
9040
|
if (enabled.has(name)) continue;
|
|
8994
9041
|
const manifestPath = resolve11(pluginsDir, name, "PLUGIN.md");
|
|
8995
|
-
if (!
|
|
9042
|
+
if (!existsSync15(manifestPath)) continue;
|
|
8996
9043
|
let manifest;
|
|
8997
9044
|
try {
|
|
8998
|
-
manifest =
|
|
9045
|
+
manifest = readFileSync13(manifestPath, "utf-8");
|
|
8999
9046
|
} catch {
|
|
9000
9047
|
continue;
|
|
9001
9048
|
}
|
|
@@ -9009,13 +9056,13 @@ function computeDormantPlugins(accountId) {
|
|
|
9009
9056
|
}
|
|
9010
9057
|
|
|
9011
9058
|
// server/routes/admin/claude-sessions.ts
|
|
9012
|
-
import { existsSync as
|
|
9059
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
|
|
9013
9060
|
import { resolve as resolve12 } from "path";
|
|
9014
9061
|
function readTunnelState(brandConfigDir) {
|
|
9015
9062
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
9016
|
-
if (!
|
|
9063
|
+
if (!existsSync16(statePath)) return null;
|
|
9017
9064
|
try {
|
|
9018
|
-
const parsed = JSON.parse(
|
|
9065
|
+
const parsed = JSON.parse(readFileSync14(statePath, "utf-8"));
|
|
9019
9066
|
const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
|
|
9020
9067
|
const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
|
|
9021
9068
|
const domain = typeof parsed.domain === "string" ? parsed.domain : null;
|
|
@@ -9029,7 +9076,7 @@ function resolveTunnelUrl() {
|
|
|
9029
9076
|
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve12(process.cwd(), "..");
|
|
9030
9077
|
let brand;
|
|
9031
9078
|
try {
|
|
9032
|
-
brand = JSON.parse(
|
|
9079
|
+
brand = JSON.parse(readFileSync14(resolve12(platformRoot2, "config", "brand.json"), "utf-8"));
|
|
9033
9080
|
} catch {
|
|
9034
9081
|
return null;
|
|
9035
9082
|
}
|
|
@@ -12454,7 +12501,7 @@ var graph_default_view_default = app21;
|
|
|
12454
12501
|
// server/routes/admin/sidebar-artefacts.ts
|
|
12455
12502
|
import { readdir as readdir4, stat as stat5 } from "fs/promises";
|
|
12456
12503
|
import { resolve as resolve16, relative as relative3, isAbsolute, sep as sep5, basename as basename5 } from "path";
|
|
12457
|
-
import { existsSync as
|
|
12504
|
+
import { existsSync as existsSync17 } from "fs";
|
|
12458
12505
|
var LIMIT = 50;
|
|
12459
12506
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
12460
12507
|
function toDataRootRelPath(absPath) {
|
|
@@ -12469,27 +12516,27 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
12469
12516
|
return c.json({ error: "Account not found for session" }, 401);
|
|
12470
12517
|
}
|
|
12471
12518
|
const start = Date.now();
|
|
12472
|
-
const
|
|
12473
|
-
if (
|
|
12519
|
+
const accountFiles = await fetchAccountFileArtefacts(accountId);
|
|
12520
|
+
if (accountFiles === null) {
|
|
12474
12521
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
12475
12522
|
}
|
|
12476
12523
|
const accountDir = resolve16(ACCOUNTS_DIR, accountId);
|
|
12477
12524
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
12478
|
-
const artefacts = [...
|
|
12525
|
+
const artefacts = [...accountFiles, ...agents].sort(
|
|
12479
12526
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
12480
12527
|
).slice(0, LIMIT);
|
|
12481
12528
|
const ms = Date.now() - start;
|
|
12482
12529
|
console.log(
|
|
12483
|
-
`[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length}
|
|
12530
|
+
`[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length} accountFiles=${accountFiles.length} agents=${agents.length} ms=${ms}`
|
|
12484
12531
|
);
|
|
12485
12532
|
return c.json({ artefacts });
|
|
12486
12533
|
});
|
|
12487
|
-
async function
|
|
12534
|
+
async function fetchAccountFileArtefacts(accountId) {
|
|
12488
12535
|
const session = getSession();
|
|
12489
12536
|
try {
|
|
12490
12537
|
const result = await session.run(
|
|
12491
12538
|
`MATCH (f:FileArtifact { accountId: $accountId })
|
|
12492
|
-
WHERE f.relativePath
|
|
12539
|
+
WHERE f.relativePath STARTS WITH 'accounts/'
|
|
12493
12540
|
RETURN f.relativePath AS relativePath,
|
|
12494
12541
|
f.displayName AS displayName,
|
|
12495
12542
|
f.mimeType AS mimeType,
|
|
@@ -12504,9 +12551,9 @@ async function fetchOutputArtefacts(accountId) {
|
|
|
12504
12551
|
const mimeType = r.get("mimeType") ?? "";
|
|
12505
12552
|
const modifiedAt = r.get("modifiedAt") ?? "";
|
|
12506
12553
|
return {
|
|
12507
|
-
id: `
|
|
12554
|
+
id: `account-file:${relativePath}`,
|
|
12508
12555
|
name: displayName ?? basename5(relativePath),
|
|
12509
|
-
kind: "
|
|
12556
|
+
kind: "account-file",
|
|
12510
12557
|
updatedAt: modifiedAt,
|
|
12511
12558
|
mimeType,
|
|
12512
12559
|
downloadPath: relativePath,
|
|
@@ -12560,7 +12607,7 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
12560
12607
|
async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
12561
12608
|
const names = /* @__PURE__ */ new Set();
|
|
12562
12609
|
for (const dir of [overrideDir, bundledDir]) {
|
|
12563
|
-
if (!
|
|
12610
|
+
if (!existsSync17(dir)) continue;
|
|
12564
12611
|
try {
|
|
12565
12612
|
const entries = await readdir4(dir);
|
|
12566
12613
|
for (const entry of entries) {
|
|
@@ -12575,7 +12622,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
12575
12622
|
}
|
|
12576
12623
|
async function readAgentTemplateRow(inp) {
|
|
12577
12624
|
let chosenPath = null;
|
|
12578
|
-
if (
|
|
12625
|
+
if (existsSync17(inp.overridePath)) {
|
|
12579
12626
|
try {
|
|
12580
12627
|
validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
|
|
12581
12628
|
chosenPath = inp.overridePath;
|
|
@@ -12586,7 +12633,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
12586
12633
|
);
|
|
12587
12634
|
return null;
|
|
12588
12635
|
}
|
|
12589
|
-
} else if (
|
|
12636
|
+
} else if (existsSync17(inp.bundledPath)) {
|
|
12590
12637
|
if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
|
|
12591
12638
|
console.error(
|
|
12592
12639
|
`[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
|
|
@@ -12628,7 +12675,7 @@ function isWithin(target, root) {
|
|
|
12628
12675
|
var sidebar_artefacts_default = app22;
|
|
12629
12676
|
|
|
12630
12677
|
// server/routes/admin/sidebar-sessions.ts
|
|
12631
|
-
import { readdirSync as readdirSync9, readFileSync as
|
|
12678
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync15, statSync as statSync7 } from "fs";
|
|
12632
12679
|
import { join as join14, resolve as resolve17 } from "path";
|
|
12633
12680
|
var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
|
|
12634
12681
|
var PID_FILE_RE = /^([0-9]+)\.json$/;
|
|
@@ -12698,7 +12745,7 @@ function sessionIdToPidMap(configDir2) {
|
|
|
12698
12745
|
if (!Number.isFinite(pid) || pid <= 0) continue;
|
|
12699
12746
|
let body;
|
|
12700
12747
|
try {
|
|
12701
|
-
body =
|
|
12748
|
+
body = readFileSync15(join14(sessionsDir, entry.name), "utf8");
|
|
12702
12749
|
} catch {
|
|
12703
12750
|
continue;
|
|
12704
12751
|
}
|
|
@@ -12720,7 +12767,7 @@ function loadUserTitles(accountDir) {
|
|
|
12720
12767
|
const path2 = join14(accountDir, "session-titles.json");
|
|
12721
12768
|
let raw;
|
|
12722
12769
|
try {
|
|
12723
|
-
raw =
|
|
12770
|
+
raw = readFileSync15(path2, "utf8");
|
|
12724
12771
|
} catch {
|
|
12725
12772
|
return out;
|
|
12726
12773
|
}
|
|
@@ -12745,7 +12792,7 @@ function loadUserTitles(accountDir) {
|
|
|
12745
12792
|
function readSidecarMeta(metaPath) {
|
|
12746
12793
|
let raw;
|
|
12747
12794
|
try {
|
|
12748
|
-
raw =
|
|
12795
|
+
raw = readFileSync15(metaPath, "utf8");
|
|
12749
12796
|
} catch {
|
|
12750
12797
|
return { bridgeIds: [], role: null, channel: null };
|
|
12751
12798
|
}
|
|
@@ -12835,7 +12882,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
12835
12882
|
let body;
|
|
12836
12883
|
let mtimeMs;
|
|
12837
12884
|
try {
|
|
12838
|
-
body =
|
|
12885
|
+
body = readFileSync15(path2, "utf8");
|
|
12839
12886
|
mtimeMs = statSync7(path2).mtimeMs;
|
|
12840
12887
|
} catch {
|
|
12841
12888
|
continue;
|
|
@@ -12872,7 +12919,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
12872
12919
|
var sidebar_sessions_default = app23;
|
|
12873
12920
|
|
|
12874
12921
|
// server/routes/admin/session-delete.ts
|
|
12875
|
-
import { existsSync as
|
|
12922
|
+
import { existsSync as existsSync18, readdirSync as readdirSync10, readFileSync as readFileSync16, unlinkSync as unlinkSync2 } from "fs";
|
|
12876
12923
|
import { join as join15, resolve as resolve18 } from "path";
|
|
12877
12924
|
var app24 = new Hono();
|
|
12878
12925
|
var SESSION_ID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -12896,7 +12943,7 @@ function findPidForSession(configDir2, sessionId) {
|
|
|
12896
12943
|
if (!Number.isFinite(pid) || pid <= 0) continue;
|
|
12897
12944
|
let body;
|
|
12898
12945
|
try {
|
|
12899
|
-
body =
|
|
12946
|
+
body = readFileSync16(join15(sessionsDir, entry.name), "utf8");
|
|
12900
12947
|
} catch {
|
|
12901
12948
|
continue;
|
|
12902
12949
|
}
|
|
@@ -13019,7 +13066,7 @@ app24.post("/", requireAdminSession, async (c) => {
|
|
|
13019
13066
|
}
|
|
13020
13067
|
let deleted = false;
|
|
13021
13068
|
try {
|
|
13022
|
-
if (
|
|
13069
|
+
if (existsSync18(jsonlPath)) {
|
|
13023
13070
|
unlinkSync2(jsonlPath);
|
|
13024
13071
|
deleted = true;
|
|
13025
13072
|
}
|
|
@@ -13032,17 +13079,17 @@ app24.post("/", requireAdminSession, async (c) => {
|
|
|
13032
13079
|
return c.json({ error: `unlink failed: ${msg}` }, 500);
|
|
13033
13080
|
}
|
|
13034
13081
|
try {
|
|
13035
|
-
if (
|
|
13082
|
+
if (existsSync18(sidecarPath)) unlinkSync2(sidecarPath);
|
|
13036
13083
|
} catch {
|
|
13037
13084
|
}
|
|
13038
13085
|
if (pid !== null) {
|
|
13039
13086
|
try {
|
|
13040
13087
|
const pidFile = join15(configDir2, "sessions", `${pid}.json`);
|
|
13041
|
-
if (
|
|
13088
|
+
if (existsSync18(pidFile)) unlinkSync2(pidFile);
|
|
13042
13089
|
} catch {
|
|
13043
13090
|
}
|
|
13044
13091
|
}
|
|
13045
|
-
if (
|
|
13092
|
+
if (existsSync18(jsonlPath)) {
|
|
13046
13093
|
console.error(`[sessions-delete] resurrection jsonlPath=${jsonlPath} sessionId=${sessionId}`);
|
|
13047
13094
|
return c.json(
|
|
13048
13095
|
{ error: "jsonl-resurrected", jsonlPath, pidKilled, waitForExitMs, escalatedToSIGKILL },
|
|
@@ -13221,14 +13268,14 @@ app26.get("/", async (c) => {
|
|
|
13221
13268
|
var system_stats_default = app26;
|
|
13222
13269
|
|
|
13223
13270
|
// server/routes/admin/health.ts
|
|
13224
|
-
import { existsSync as
|
|
13271
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
|
|
13225
13272
|
import { resolve as resolve19, join as join16 } from "path";
|
|
13226
13273
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
|
|
13227
13274
|
var brandHostname = "maxy";
|
|
13228
13275
|
var brandJsonPath = join16(PLATFORM_ROOT6, "config", "brand.json");
|
|
13229
|
-
if (
|
|
13276
|
+
if (existsSync19(brandJsonPath)) {
|
|
13230
13277
|
try {
|
|
13231
|
-
const brand = JSON.parse(
|
|
13278
|
+
const brand = JSON.parse(readFileSync17(brandJsonPath, "utf-8"));
|
|
13232
13279
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
13233
13280
|
} catch {
|
|
13234
13281
|
}
|
|
@@ -13237,8 +13284,8 @@ var VERSION_FILE = resolve19(PLATFORM_ROOT6, `config/.${brandHostname}-version`)
|
|
|
13237
13284
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
13238
13285
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
13239
13286
|
function readVersion() {
|
|
13240
|
-
if (!
|
|
13241
|
-
return
|
|
13287
|
+
if (!existsSync19(VERSION_FILE)) return "unknown";
|
|
13288
|
+
return readFileSync17(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
13242
13289
|
}
|
|
13243
13290
|
async function probeConversationDb() {
|
|
13244
13291
|
let session;
|
|
@@ -13778,7 +13825,7 @@ var admin_default = app34;
|
|
|
13778
13825
|
|
|
13779
13826
|
// app/lib/access-gate.ts
|
|
13780
13827
|
import neo4j4 from "neo4j-driver";
|
|
13781
|
-
import { readFileSync as
|
|
13828
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
13782
13829
|
import { resolve as resolve20 } from "path";
|
|
13783
13830
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
13784
13831
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
@@ -13787,7 +13834,7 @@ function readPassword() {
|
|
|
13787
13834
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
13788
13835
|
const passwordFile = resolve20(PLATFORM_ROOT7, "config/.neo4j-password");
|
|
13789
13836
|
try {
|
|
13790
|
-
return
|
|
13837
|
+
return readFileSync18(passwordFile, "utf-8").trim();
|
|
13791
13838
|
} catch {
|
|
13792
13839
|
throw new Error(
|
|
13793
13840
|
`Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
|
|
@@ -14219,7 +14266,7 @@ app37.route("/request-magic-link", request_magic_link_default);
|
|
|
14219
14266
|
var access_default = app37;
|
|
14220
14267
|
|
|
14221
14268
|
// server/routes/sites.ts
|
|
14222
|
-
import { existsSync as
|
|
14269
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
|
|
14223
14270
|
import { resolve as resolve22 } from "path";
|
|
14224
14271
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
14225
14272
|
var MIME = {
|
|
@@ -14285,7 +14332,7 @@ app38.get("/:rel{.*}", (c) => {
|
|
|
14285
14332
|
}
|
|
14286
14333
|
let stat7;
|
|
14287
14334
|
try {
|
|
14288
|
-
stat7 =
|
|
14335
|
+
stat7 = existsSync20(filePath) ? statSync8(filePath) : null;
|
|
14289
14336
|
} catch {
|
|
14290
14337
|
stat7 = null;
|
|
14291
14338
|
}
|
|
@@ -14304,7 +14351,7 @@ app38.get("/:rel{.*}", (c) => {
|
|
|
14304
14351
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
14305
14352
|
return c.text("Forbidden", 403);
|
|
14306
14353
|
}
|
|
14307
|
-
if (!
|
|
14354
|
+
if (!existsSync20(filePath)) {
|
|
14308
14355
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
14309
14356
|
return c.text("Not found", 404);
|
|
14310
14357
|
}
|
|
@@ -14323,7 +14370,7 @@ app38.get("/:rel{.*}", (c) => {
|
|
|
14323
14370
|
}
|
|
14324
14371
|
let body;
|
|
14325
14372
|
try {
|
|
14326
|
-
body =
|
|
14373
|
+
body = readFileSync19(realPath);
|
|
14327
14374
|
} catch (err) {
|
|
14328
14375
|
const code = err?.code;
|
|
14329
14376
|
if (code === "EISDIR") {
|
|
@@ -14359,7 +14406,7 @@ var sites_default = app38;
|
|
|
14359
14406
|
|
|
14360
14407
|
// app/lib/visitor-token.ts
|
|
14361
14408
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
14362
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
14409
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync20, writeFileSync as writeFileSync7 } from "fs";
|
|
14363
14410
|
import { dirname as dirname4 } from "path";
|
|
14364
14411
|
var TOKEN_PREFIX = "v1.";
|
|
14365
14412
|
var SECRET_BYTES = 32;
|
|
@@ -14368,7 +14415,7 @@ var cachedSecret = null;
|
|
|
14368
14415
|
function getSecret() {
|
|
14369
14416
|
if (cachedSecret) return cachedSecret;
|
|
14370
14417
|
try {
|
|
14371
|
-
const hex2 =
|
|
14418
|
+
const hex2 = readFileSync20(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
14372
14419
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
14373
14420
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
14374
14421
|
return cachedSecret;
|
|
@@ -14378,11 +14425,11 @@ function getSecret() {
|
|
|
14378
14425
|
const fresh = randomBytes(SECRET_BYTES).toString("hex");
|
|
14379
14426
|
try {
|
|
14380
14427
|
mkdirSync4(dirname4(VISITOR_TOKEN_SECRET_FILE), { recursive: true, mode: 448 });
|
|
14381
|
-
|
|
14428
|
+
writeFileSync7(VISITOR_TOKEN_SECRET_FILE, fresh, { mode: 384, flag: "wx" });
|
|
14382
14429
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
14383
14430
|
} catch {
|
|
14384
14431
|
}
|
|
14385
|
-
const hex =
|
|
14432
|
+
const hex = readFileSync20(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
14386
14433
|
cachedSecret = Buffer.from(hex, "hex");
|
|
14387
14434
|
return cachedSecret;
|
|
14388
14435
|
}
|
|
@@ -14435,7 +14482,7 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
14435
14482
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
14436
14483
|
|
|
14437
14484
|
// app/lib/brand-config.ts
|
|
14438
|
-
import { existsSync as
|
|
14485
|
+
import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
|
|
14439
14486
|
import { join as join17 } from "path";
|
|
14440
14487
|
var cached2 = null;
|
|
14441
14488
|
var cachedAttempted = false;
|
|
@@ -14445,9 +14492,9 @@ function readBrandConfig() {
|
|
|
14445
14492
|
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
|
|
14446
14493
|
if (!platformRoot2) return null;
|
|
14447
14494
|
const brandPath = join17(platformRoot2, "config", "brand.json");
|
|
14448
|
-
if (!
|
|
14495
|
+
if (!existsSync21(brandPath)) return null;
|
|
14449
14496
|
try {
|
|
14450
|
-
cached2 = JSON.parse(
|
|
14497
|
+
cached2 = JSON.parse(readFileSync21(brandPath, "utf-8"));
|
|
14451
14498
|
return cached2;
|
|
14452
14499
|
} catch {
|
|
14453
14500
|
return null;
|
|
@@ -14936,13 +14983,13 @@ var visitor_event_default = app41;
|
|
|
14936
14983
|
|
|
14937
14984
|
// server/routes/session.ts
|
|
14938
14985
|
import { resolve as resolve23 } from "path";
|
|
14939
|
-
import { existsSync as
|
|
14986
|
+
import { existsSync as existsSync22, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
14940
14987
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14941
14988
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
14942
14989
|
try {
|
|
14943
14990
|
const cacheDir = resolve23(MAXY_DIR, "branding-cache", accountId);
|
|
14944
14991
|
mkdirSync5(cacheDir, { recursive: true });
|
|
14945
|
-
|
|
14992
|
+
writeFileSync8(resolve23(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
14946
14993
|
} catch (err) {
|
|
14947
14994
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
14948
14995
|
}
|
|
@@ -15031,7 +15078,7 @@ app42.post("/", async (c) => {
|
|
|
15031
15078
|
let agentConfig = null;
|
|
15032
15079
|
if (account) {
|
|
15033
15080
|
const agentDir = resolve23(account.accountDir, "agents", agentSlug);
|
|
15034
|
-
if (!
|
|
15081
|
+
if (!existsSync22(agentDir) || !existsSync22(resolve23(agentDir, "config.json"))) {
|
|
15035
15082
|
return c.json({ error: "Agent not found" }, 404);
|
|
15036
15083
|
}
|
|
15037
15084
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -15423,44 +15470,115 @@ var InboundHub = class {
|
|
|
15423
15470
|
state(senderId) {
|
|
15424
15471
|
let s = this.senders.get(senderId);
|
|
15425
15472
|
if (!s) {
|
|
15426
|
-
s = { subscriber: null,
|
|
15473
|
+
s = { subscriber: null, ready: false, inFlight: [] };
|
|
15427
15474
|
this.senders.set(senderId, s);
|
|
15428
15475
|
}
|
|
15429
15476
|
return s;
|
|
15430
15477
|
}
|
|
15431
|
-
/** Route an inbound message
|
|
15432
|
-
*
|
|
15433
|
-
|
|
15478
|
+
/** Route an inbound message. Delivers immediately only if the sender's channel
|
|
15479
|
+
* server is attached AND has signalled readiness; otherwise queues it (cold
|
|
15480
|
+
* start, or the attach->ready window) so it is never written before the
|
|
15481
|
+
* channel server can consume it. Every accepted message is tracked in-flight
|
|
15482
|
+
* until it reaches a terminal state. */
|
|
15483
|
+
deliver(payload, now) {
|
|
15434
15484
|
const s = this.state(payload.senderId);
|
|
15435
|
-
if (s.subscriber) {
|
|
15485
|
+
if (s.subscriber && s.ready) {
|
|
15436
15486
|
s.subscriber(payload);
|
|
15487
|
+
s.inFlight.push({ payload, state: "drained", enqueuedAt: now, drainedAt: now });
|
|
15437
15488
|
} else {
|
|
15438
|
-
s.
|
|
15489
|
+
s.inFlight.push({ payload, state: "queued", enqueuedAt: now, drainedAt: null });
|
|
15439
15490
|
}
|
|
15440
15491
|
}
|
|
15441
|
-
/** A sender's channel server connected.
|
|
15442
|
-
*
|
|
15492
|
+
/** A sender's channel server connected. Register the subscriber but do NOT
|
|
15493
|
+
* drain yet — the drain waits for markReady so nothing is written before the
|
|
15494
|
+
* channel server is consuming. Replaces any prior subscriber. */
|
|
15443
15495
|
attach(senderId, subscriber) {
|
|
15444
15496
|
const s = this.state(senderId);
|
|
15445
15497
|
s.subscriber = subscriber;
|
|
15446
|
-
|
|
15447
|
-
|
|
15448
|
-
|
|
15449
|
-
|
|
15498
|
+
s.ready = false;
|
|
15499
|
+
}
|
|
15500
|
+
/** The sender's channel server reported it is connected and consuming. Drain
|
|
15501
|
+
* every queued message to it in arrival order, marking each drained-awaiting-
|
|
15502
|
+
* ack, then route live. Returns the number drained. */
|
|
15503
|
+
markReady(senderId, now) {
|
|
15504
|
+
const s = this.state(senderId);
|
|
15505
|
+
s.ready = true;
|
|
15506
|
+
if (!s.subscriber) return 0;
|
|
15507
|
+
let count = 0;
|
|
15508
|
+
for (const e of s.inFlight) {
|
|
15509
|
+
if (e.state === "queued") {
|
|
15510
|
+
s.subscriber(e.payload);
|
|
15511
|
+
e.state = "drained";
|
|
15512
|
+
e.drainedAt = now;
|
|
15513
|
+
count++;
|
|
15514
|
+
}
|
|
15450
15515
|
}
|
|
15516
|
+
return count;
|
|
15517
|
+
}
|
|
15518
|
+
/** The channel server confirmed it received (emitted) a drained message. The
|
|
15519
|
+
* message has reached the agent; drop it from in-flight. Returns whether a
|
|
15520
|
+
* matching in-flight message was found. */
|
|
15521
|
+
ack(senderId, waMessageId) {
|
|
15522
|
+
const s = this.senders.get(senderId);
|
|
15523
|
+
if (!s) return false;
|
|
15524
|
+
const i = s.inFlight.findIndex((e) => e.payload.waMessageId === waMessageId);
|
|
15525
|
+
if (i < 0) return false;
|
|
15526
|
+
s.inFlight.splice(i, 1);
|
|
15527
|
+
return true;
|
|
15451
15528
|
}
|
|
15452
15529
|
/** A sender's channel server disconnected (session ended/restarting).
|
|
15453
|
-
* Subsequent messages queue again until re-attach, so nothing is lost
|
|
15454
|
-
* across a restart. */
|
|
15530
|
+
* Subsequent messages queue again until re-attach + ready, so nothing is lost
|
|
15531
|
+
* across a restart. In-flight drained messages stay tracked for ack/sweep. */
|
|
15455
15532
|
detach(senderId) {
|
|
15456
15533
|
const s = this.senders.get(senderId);
|
|
15457
|
-
if (s)
|
|
15534
|
+
if (s) {
|
|
15535
|
+
s.subscriber = null;
|
|
15536
|
+
s.ready = false;
|
|
15537
|
+
}
|
|
15538
|
+
}
|
|
15539
|
+
/** Reconciliation from queue state alone — does not wait for a future inbound
|
|
15540
|
+
* to expose a loss. Returns (and removes) a terminal event for every message
|
|
15541
|
+
* that never reached the agent:
|
|
15542
|
+
* - a still-queued message older than queuedThresholdMs WHILE a subscriber
|
|
15543
|
+
* is attached -> inbound-undelivered (the readiness handoff broke);
|
|
15544
|
+
* - a still-queued message older than queuedThresholdMs with NO subscriber
|
|
15545
|
+
* attached -> spawn-undelivered (the channel server never attached: spawn
|
|
15546
|
+
* failed, or the session ended and never resumed);
|
|
15547
|
+
* - a drained message unacked past ackThresholdMs -> drain-undelivered (the
|
|
15548
|
+
* channel server never confirmed receipt — the silent cold-start drop).
|
|
15549
|
+
* Each message is emitted at most once (removed when emitted). */
|
|
15550
|
+
sweep(now, t) {
|
|
15551
|
+
const events = [];
|
|
15552
|
+
for (const [senderId, s] of this.senders) {
|
|
15553
|
+
s.inFlight = s.inFlight.filter((e) => {
|
|
15554
|
+
if (e.state === "queued" && s.subscriber && now - e.enqueuedAt > t.queuedThresholdMs) {
|
|
15555
|
+
events.push({ kind: "inbound-undelivered", senderId, waMessageId: e.payload.waMessageId, ageMs: now - e.enqueuedAt });
|
|
15556
|
+
return false;
|
|
15557
|
+
}
|
|
15558
|
+
if (e.state === "queued" && !s.subscriber && now - e.enqueuedAt > t.queuedThresholdMs) {
|
|
15559
|
+
events.push({ kind: "spawn-undelivered", senderId, waMessageId: e.payload.waMessageId, ageMs: now - e.enqueuedAt });
|
|
15560
|
+
return false;
|
|
15561
|
+
}
|
|
15562
|
+
if (e.state === "drained" && e.drainedAt != null && now - e.drainedAt > t.ackThresholdMs) {
|
|
15563
|
+
events.push({ kind: "drain-undelivered", senderId, waMessageId: e.payload.waMessageId, ageMs: now - e.drainedAt });
|
|
15564
|
+
return false;
|
|
15565
|
+
}
|
|
15566
|
+
return true;
|
|
15567
|
+
});
|
|
15568
|
+
}
|
|
15569
|
+
return events;
|
|
15458
15570
|
}
|
|
15459
|
-
/** Whether a sender currently has a live channel server. The gateway
|
|
15460
|
-
* this to decide whether an inbound needs a cold-start spawn/resume. */
|
|
15571
|
+
/** Whether a sender currently has a live channel server attached. The gateway
|
|
15572
|
+
* uses this to decide whether an inbound needs a cold-start spawn/resume. */
|
|
15461
15573
|
hasSubscriber(senderId) {
|
|
15462
15574
|
return this.senders.get(senderId)?.subscriber != null;
|
|
15463
15575
|
}
|
|
15576
|
+
/** Whether a sender's channel server is attached AND has signalled readiness.
|
|
15577
|
+
* Used only to log delivery=immediate vs queued accurately. */
|
|
15578
|
+
isReady(senderId) {
|
|
15579
|
+
const s = this.senders.get(senderId);
|
|
15580
|
+
return s?.subscriber != null && s.ready;
|
|
15581
|
+
}
|
|
15464
15582
|
};
|
|
15465
15583
|
|
|
15466
15584
|
// node_modules/hono/dist/utils/stream.js
|
|
@@ -15661,10 +15779,23 @@ function createWaChannelRoutes(deps) {
|
|
|
15661
15779
|
deps.onReady?.(senderId);
|
|
15662
15780
|
return c.json({ ok: true });
|
|
15663
15781
|
});
|
|
15782
|
+
app44.post("/wa-channel/received", async (c) => {
|
|
15783
|
+
const body = await c.req.json().catch(() => null);
|
|
15784
|
+
const senderId = body?.senderId;
|
|
15785
|
+
const waMessageId = body?.waMessageId;
|
|
15786
|
+
if (typeof senderId !== "string" || typeof waMessageId !== "string") {
|
|
15787
|
+
return c.json({ error: "senderId and waMessageId required" }, 400);
|
|
15788
|
+
}
|
|
15789
|
+
deps.onReceived?.(senderId, waMessageId);
|
|
15790
|
+
return c.json({ ok: true });
|
|
15791
|
+
});
|
|
15664
15792
|
return app44;
|
|
15665
15793
|
}
|
|
15666
15794
|
|
|
15667
15795
|
// app/lib/whatsapp/gateway/wa-gateway.ts
|
|
15796
|
+
var QUEUED_THRESHOLD_MS = 3e4;
|
|
15797
|
+
var ACK_THRESHOLD_MS = 3e4;
|
|
15798
|
+
var SWEEP_INTERVAL_MS = 15e3;
|
|
15668
15799
|
var WaGateway = class {
|
|
15669
15800
|
constructor(deps) {
|
|
15670
15801
|
this.deps = deps;
|
|
@@ -15677,17 +15808,55 @@ var WaGateway = class {
|
|
|
15677
15808
|
routes() {
|
|
15678
15809
|
return createWaChannelRoutes({
|
|
15679
15810
|
hub: this.hub,
|
|
15680
|
-
sendOutbound: (senderId, text) => this.sendOutbound(senderId, text)
|
|
15811
|
+
sendOutbound: (senderId, text) => this.sendOutbound(senderId, text),
|
|
15812
|
+
onReady: (senderId) => {
|
|
15813
|
+
const count = this.hub.markReady(senderId, Date.now());
|
|
15814
|
+
if (count > 0) console.error(`[whatsapp-native] op=drained senderId=${senderId} count=${count}`);
|
|
15815
|
+
},
|
|
15816
|
+
onReceived: (senderId, waMessageId) => {
|
|
15817
|
+
const found = this.hub.ack(senderId, waMessageId);
|
|
15818
|
+
console.error(
|
|
15819
|
+
`[whatsapp-native] op=channel-received senderId=${senderId} waMessageId=${waMessageId}${found ? "" : " note=unknown"}`
|
|
15820
|
+
);
|
|
15821
|
+
}
|
|
15681
15822
|
});
|
|
15682
15823
|
}
|
|
15824
|
+
/** Run one reconciliation pass: log every terminal delivery event the sweep
|
|
15825
|
+
* surfaces — a queued message that never drained while a subscriber was
|
|
15826
|
+
* attached (inbound-undelivered), a queued message whose channel server never
|
|
15827
|
+
* attached (spawn-undelivered), or a drained message the channel server never
|
|
15828
|
+
* confirmed (drain-undelivered). Returns the events (for tests). Driven by
|
|
15829
|
+
* startSweeper() in production; called directly with an injected clock in
|
|
15830
|
+
* tests. */
|
|
15831
|
+
reconcile(now) {
|
|
15832
|
+
const events = this.hub.sweep(now, {
|
|
15833
|
+
queuedThresholdMs: QUEUED_THRESHOLD_MS,
|
|
15834
|
+
ackThresholdMs: ACK_THRESHOLD_MS
|
|
15835
|
+
});
|
|
15836
|
+
for (const e of events) {
|
|
15837
|
+
console.error(
|
|
15838
|
+
`[whatsapp-native] op=${e.kind} senderId=${e.senderId} waMessageId=${e.waMessageId} ageMs=${e.ageMs}`
|
|
15839
|
+
);
|
|
15840
|
+
}
|
|
15841
|
+
return events;
|
|
15842
|
+
}
|
|
15843
|
+
/** Start the periodic reconciliation sweep in the long-lived UI process.
|
|
15844
|
+
* Returns the interval handle. */
|
|
15845
|
+
startSweeper(intervalMs = SWEEP_INTERVAL_MS) {
|
|
15846
|
+
return setInterval(() => this.reconcile(Date.now()), intervalMs);
|
|
15847
|
+
}
|
|
15683
15848
|
/** Handle one inbound admin WhatsApp message. */
|
|
15684
15849
|
async handleInbound(input) {
|
|
15685
15850
|
const bytes = Buffer.byteLength(input.text, "utf8");
|
|
15686
15851
|
const hadSubscriber = this.hub.hasSubscriber(input.senderId);
|
|
15852
|
+
const willDeliverNow = this.hub.isReady(input.senderId);
|
|
15687
15853
|
this.replies.set(input.senderId, input.reply);
|
|
15688
|
-
this.hub.deliver(
|
|
15854
|
+
this.hub.deliver(
|
|
15855
|
+
{ senderId: input.senderId, text: input.text, waMessageId: `wa-${++this.seq}` },
|
|
15856
|
+
Date.now()
|
|
15857
|
+
);
|
|
15689
15858
|
console.error(
|
|
15690
|
-
`[whatsapp-native] op=inbound senderId=${input.senderId} accountId=${input.accountId} bytes=${bytes} delivery=${
|
|
15859
|
+
`[whatsapp-native] op=inbound senderId=${input.senderId} accountId=${input.accountId} bytes=${bytes} delivery=${willDeliverNow ? "immediate" : "queued"}`
|
|
15691
15860
|
);
|
|
15692
15861
|
if (!hadSubscriber && !this.spawning.has(input.senderId)) {
|
|
15693
15862
|
this.spawning.add(input.senderId);
|
|
@@ -15711,6 +15880,71 @@ var WaGateway = class {
|
|
|
15711
15880
|
}
|
|
15712
15881
|
};
|
|
15713
15882
|
|
|
15883
|
+
// app/lib/whatsapp/inbound/resolve-admin-userid.ts
|
|
15884
|
+
function classifyAdminUserId(users, admins, senderPhone) {
|
|
15885
|
+
const matched = users.find((u) => typeof u.phone === "string" && phonesMatch(u.phone, senderPhone));
|
|
15886
|
+
if (!matched) return { kind: "no-users-binding" };
|
|
15887
|
+
return admins.some((a) => a.userId === matched.userId) ? { kind: "resolved", userId: matched.userId } : { kind: "userid-not-admin", userId: matched.userId };
|
|
15888
|
+
}
|
|
15889
|
+
function pickAdminUserId(users, admins, senderPhone) {
|
|
15890
|
+
const r = classifyAdminUserId(users, admins, senderPhone);
|
|
15891
|
+
return r.kind === "resolved" ? r.userId : null;
|
|
15892
|
+
}
|
|
15893
|
+
function resolveAdminUserId(params) {
|
|
15894
|
+
try {
|
|
15895
|
+
const users = readUsersFile2(USERS_FILE);
|
|
15896
|
+
if (!users) return null;
|
|
15897
|
+
const account = listValidAccounts().find((a) => a.accountId === params.accountId);
|
|
15898
|
+
const admins = account?.config.admins ?? [];
|
|
15899
|
+
const direct = pickAdminUserId(users, admins, params.senderPhone);
|
|
15900
|
+
if (direct) return direct;
|
|
15901
|
+
const mapped = resolveViaLidMap(readLidMap() ?? {}, params.senderPhone);
|
|
15902
|
+
if (!mapped) return null;
|
|
15903
|
+
const viaLid = pickAdminUserId(users, admins, mapped);
|
|
15904
|
+
if (viaLid) {
|
|
15905
|
+
console.error(`[lid-map] resolved-via-cache lid=${params.senderPhone} userId=${viaLid.slice(0, 8)}`);
|
|
15906
|
+
}
|
|
15907
|
+
return viaLid;
|
|
15908
|
+
} catch {
|
|
15909
|
+
return null;
|
|
15910
|
+
}
|
|
15911
|
+
}
|
|
15912
|
+
|
|
15913
|
+
// app/lib/whatsapp/inbound/channel-admin-binding-drift.ts
|
|
15914
|
+
function findChannelAdminBindingDrift(adminPhones, admins, users) {
|
|
15915
|
+
const drift = [];
|
|
15916
|
+
for (const phone of adminPhones) {
|
|
15917
|
+
const r = classifyAdminUserId(users, admins, phone);
|
|
15918
|
+
if (r.kind === "no-users-binding") drift.push({ phone, reason: "no-users-binding" });
|
|
15919
|
+
else if (r.kind === "userid-not-admin") drift.push({ phone, reason: "userid-not-admin", userId: r.userId });
|
|
15920
|
+
}
|
|
15921
|
+
return drift;
|
|
15922
|
+
}
|
|
15923
|
+
function warnOnChannelAdminBindingDrift() {
|
|
15924
|
+
let users;
|
|
15925
|
+
try {
|
|
15926
|
+
users = readUsersFile2(USERS_FILE) ?? [];
|
|
15927
|
+
} catch {
|
|
15928
|
+
return;
|
|
15929
|
+
}
|
|
15930
|
+
let accounts;
|
|
15931
|
+
try {
|
|
15932
|
+
accounts = listValidAccounts();
|
|
15933
|
+
} catch {
|
|
15934
|
+
return;
|
|
15935
|
+
}
|
|
15936
|
+
for (const acct of accounts) {
|
|
15937
|
+
const adminPhones = readAdminPhones(acct.accountDir);
|
|
15938
|
+
const drift = findChannelAdminBindingDrift(adminPhones, acct.config.admins ?? [], users);
|
|
15939
|
+
for (const d of drift) {
|
|
15940
|
+
const tail = d.reason === "userid-not-admin" ? ` userId=${d.userId}` : "";
|
|
15941
|
+
console.error(
|
|
15942
|
+
`[admin-identity] binding-drift accountId=${acct.accountId} phone=${d.phone} reason=${d.reason}${tail}`
|
|
15943
|
+
);
|
|
15944
|
+
}
|
|
15945
|
+
}
|
|
15946
|
+
}
|
|
15947
|
+
|
|
15714
15948
|
// app/lib/admin-sse-registry.ts
|
|
15715
15949
|
var activeAdminSSEControllers = /* @__PURE__ */ new Set();
|
|
15716
15950
|
function broadcastAdminShutdown(reason) {
|
|
@@ -15741,7 +15975,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
15741
15975
|
|
|
15742
15976
|
// ../lib/entitlement/src/index.ts
|
|
15743
15977
|
import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
|
|
15744
|
-
import { existsSync as
|
|
15978
|
+
import { existsSync as existsSync23, readFileSync as readFileSync22, statSync as statSync9 } from "fs";
|
|
15745
15979
|
import { resolve as resolve25 } from "path";
|
|
15746
15980
|
|
|
15747
15981
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -15790,7 +16024,7 @@ function resolveEntitlement(brand, account) {
|
|
|
15790
16024
|
return logResolved(implicitTrust(account), null);
|
|
15791
16025
|
}
|
|
15792
16026
|
const entitlementPath = resolve25(brand.configDir, "entitlement.json");
|
|
15793
|
-
if (!
|
|
16027
|
+
if (!existsSync23(entitlementPath)) {
|
|
15794
16028
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
15795
16029
|
}
|
|
15796
16030
|
const stat7 = statSync9(entitlementPath);
|
|
@@ -15805,7 +16039,7 @@ function resolveEntitlement(brand, account) {
|
|
|
15805
16039
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
15806
16040
|
let pubkeyPem;
|
|
15807
16041
|
try {
|
|
15808
|
-
pubkeyPem =
|
|
16042
|
+
pubkeyPem = readFileSync22(pubkeyPath(brand), "utf-8");
|
|
15809
16043
|
} catch (err) {
|
|
15810
16044
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
15811
16045
|
reason: "pubkey-missing"
|
|
@@ -15819,7 +16053,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
15819
16053
|
}
|
|
15820
16054
|
let envelope;
|
|
15821
16055
|
try {
|
|
15822
|
-
envelope = JSON.parse(
|
|
16056
|
+
envelope = JSON.parse(readFileSync22(entitlementPath, "utf-8"));
|
|
15823
16057
|
} catch {
|
|
15824
16058
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
15825
16059
|
}
|
|
@@ -15982,12 +16216,12 @@ function clientFrom(c) {
|
|
|
15982
16216
|
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
15983
16217
|
var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join18(PLATFORM_ROOT9, "config", "brand.json") : "";
|
|
15984
16218
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
15985
|
-
if (BRAND_JSON_PATH && !
|
|
16219
|
+
if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
|
|
15986
16220
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
15987
16221
|
}
|
|
15988
|
-
if (BRAND_JSON_PATH &&
|
|
16222
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
15989
16223
|
try {
|
|
15990
|
-
const parsed = JSON.parse(
|
|
16224
|
+
const parsed = JSON.parse(readFileSync23(BRAND_JSON_PATH, "utf-8"));
|
|
15991
16225
|
BRAND = { ...BRAND, ...parsed };
|
|
15992
16226
|
} catch (err) {
|
|
15993
16227
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -16009,8 +16243,8 @@ var brandLoginOpts = {
|
|
|
16009
16243
|
var ALIAS_DOMAINS_PATH = join18(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
16010
16244
|
function loadAliasDomains() {
|
|
16011
16245
|
try {
|
|
16012
|
-
if (!
|
|
16013
|
-
const parsed = JSON.parse(
|
|
16246
|
+
if (!existsSync24(ALIAS_DOMAINS_PATH)) return null;
|
|
16247
|
+
const parsed = JSON.parse(readFileSync23(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
16014
16248
|
if (!Array.isArray(parsed)) {
|
|
16015
16249
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
16016
16250
|
return null;
|
|
@@ -16039,8 +16273,11 @@ var waGateway = new WaGateway({
|
|
|
16039
16273
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
16040
16274
|
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve26(process.env.MAXY_PLATFORM_ROOT ?? join18(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
16041
16275
|
ensureChannelSession: async ({ accountId, senderId, gatewayUrl, serverPath }) => {
|
|
16276
|
+
const userId = resolveAdminUserId({ accountId, senderPhone: senderId }) ?? void 0;
|
|
16277
|
+
console.error(`[whatsapp-native] op=admin-identity senderId=${senderId} userId=${userId ?? "owner-fallback"}`);
|
|
16042
16278
|
const result = await managerRcSpawn({
|
|
16043
16279
|
sessionId: adminSessionIdFor(accountId, senderId),
|
|
16280
|
+
userId,
|
|
16044
16281
|
waChannel: { senderId, gatewayUrl, serverPath },
|
|
16045
16282
|
logContext: `channel=whatsapp-native senderId=${senderId}`
|
|
16046
16283
|
});
|
|
@@ -16050,6 +16287,7 @@ var waGateway = new WaGateway({
|
|
|
16050
16287
|
}
|
|
16051
16288
|
});
|
|
16052
16289
|
app43.route("/", waGateway.routes());
|
|
16290
|
+
waGateway.startSweeper();
|
|
16053
16291
|
app43.use("*", clientIpMiddleware);
|
|
16054
16292
|
app43.use("*", async (c, next) => {
|
|
16055
16293
|
await next();
|
|
@@ -16372,14 +16610,14 @@ app43.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
16372
16610
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
16373
16611
|
return c.text("Forbidden", 403);
|
|
16374
16612
|
}
|
|
16375
|
-
if (!
|
|
16613
|
+
if (!existsSync24(filePath)) {
|
|
16376
16614
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
16377
16615
|
return c.text("Not found", 404);
|
|
16378
16616
|
}
|
|
16379
16617
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
16380
16618
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
16381
16619
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
16382
|
-
const body =
|
|
16620
|
+
const body = readFileSync23(filePath);
|
|
16383
16621
|
return c.body(body, 200, {
|
|
16384
16622
|
"Content-Type": contentType,
|
|
16385
16623
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -16402,14 +16640,14 @@ app43.get("/generated/:filename", (c) => {
|
|
|
16402
16640
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
16403
16641
|
return c.text("Forbidden", 403);
|
|
16404
16642
|
}
|
|
16405
|
-
if (!
|
|
16643
|
+
if (!existsSync24(filePath)) {
|
|
16406
16644
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
16407
16645
|
return c.text("Not found", 404);
|
|
16408
16646
|
}
|
|
16409
16647
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
16410
16648
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
16411
16649
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
16412
|
-
const body =
|
|
16650
|
+
const body = readFileSync23(filePath);
|
|
16413
16651
|
return c.body(body, 200, {
|
|
16414
16652
|
"Content-Type": contentType,
|
|
16415
16653
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -16422,9 +16660,9 @@ app43.route("/v", visitor_consent_default);
|
|
|
16422
16660
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
16423
16661
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
16424
16662
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
16425
|
-
if (BRAND_JSON_PATH &&
|
|
16663
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
16426
16664
|
try {
|
|
16427
|
-
const fullBrand = JSON.parse(
|
|
16665
|
+
const fullBrand = JSON.parse(readFileSync23(BRAND_JSON_PATH, "utf-8"));
|
|
16428
16666
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
16429
16667
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
16430
16668
|
} catch {
|
|
@@ -16442,8 +16680,8 @@ function readInstalledVersion() {
|
|
|
16442
16680
|
try {
|
|
16443
16681
|
if (!PLATFORM_ROOT9) return "unknown";
|
|
16444
16682
|
const versionFile = join18(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
|
|
16445
|
-
if (!
|
|
16446
|
-
const content =
|
|
16683
|
+
if (!existsSync24(versionFile)) return "unknown";
|
|
16684
|
+
const content = readFileSync23(versionFile, "utf-8").trim();
|
|
16447
16685
|
return content || "unknown";
|
|
16448
16686
|
} catch {
|
|
16449
16687
|
return "unknown";
|
|
@@ -16484,7 +16722,7 @@ var clientErrorReporterScript = `<script>
|
|
|
16484
16722
|
function cachedHtml(file) {
|
|
16485
16723
|
let html = htmlCache.get(file);
|
|
16486
16724
|
if (!html) {
|
|
16487
|
-
html =
|
|
16725
|
+
html = readFileSync23(resolve26(process.cwd(), "public", file), "utf-8");
|
|
16488
16726
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
16489
16727
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
16490
16728
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -16505,8 +16743,8 @@ function loadBrandingCache(agentSlug) {
|
|
|
16505
16743
|
const accountId = getDefaultAccountId();
|
|
16506
16744
|
if (!accountId) return null;
|
|
16507
16745
|
const cachePath = join18(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
16508
|
-
if (!
|
|
16509
|
-
return JSON.parse(
|
|
16746
|
+
if (!existsSync24(cachePath)) return null;
|
|
16747
|
+
return JSON.parse(readFileSync23(cachePath, "utf-8"));
|
|
16510
16748
|
} catch {
|
|
16511
16749
|
return null;
|
|
16512
16750
|
}
|
|
@@ -16594,7 +16832,7 @@ app43.use("/vnc-popout.html", logViewerFetch);
|
|
|
16594
16832
|
app43.get("/vnc-popout.html", (c) => {
|
|
16595
16833
|
let html = htmlCache.get("vnc-popout.html");
|
|
16596
16834
|
if (!html) {
|
|
16597
|
-
html =
|
|
16835
|
+
html = readFileSync23(resolve26(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
16598
16836
|
const name = escapeHtml(BRAND.productName);
|
|
16599
16837
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
16600
16838
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -16748,11 +16986,11 @@ try {
|
|
|
16748
16986
|
var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
16749
16987
|
async function runAdminUserReconcileTick() {
|
|
16750
16988
|
try {
|
|
16751
|
-
if (!
|
|
16989
|
+
if (!existsSync24(USERS_FILE)) {
|
|
16752
16990
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
16753
16991
|
return;
|
|
16754
16992
|
}
|
|
16755
|
-
const usersRaw =
|
|
16993
|
+
const usersRaw = readFileSync23(USERS_FILE, "utf-8").trim();
|
|
16756
16994
|
if (!usersRaw) {
|
|
16757
16995
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
16758
16996
|
return;
|
|
@@ -16817,6 +17055,7 @@ reconcileEnabledPlugins(bootAccount?.accountDir, bootAccount?.config, bootAccoun
|
|
|
16817
17055
|
for (const acct of listValidAccounts()) {
|
|
16818
17056
|
cleanupLeakedPremiumSubs(acct.accountDir, acct.config, acct.accountId);
|
|
16819
17057
|
}
|
|
17058
|
+
warnOnChannelAdminBindingDrift();
|
|
16820
17059
|
var bootEnabled = Array.isArray(bootAccountConfig?.enabledPlugins) ? bootAccountConfig.enabledPlugins : [];
|
|
16821
17060
|
var bootDelivered = [];
|
|
16822
17061
|
var bootDistMissing = [];
|