@rubytech/create-maxy-code 0.1.275 → 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/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +1 -4
- package/payload/platform/plugins/admin/mcp/dist/index.js +0 -8
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -3
- package/payload/platform/plugins/docs/references/admin-identity-gate.md +77 -81
- 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/scripts/setup-account.sh +0 -5
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +0 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- 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 -17
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +4 -5
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.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/premium-plugins/writer-craft/mcp/dist/index.js +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts +2 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/src/index.ts +1 -1
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-distil-profile.ts +3 -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 +551 -405
- package/payload/platform/plugins/admin/hooks/__tests__/pin-identity-gate.test.sh +0 -96
- package/payload/platform/plugins/admin/hooks/pin-identity-gate.sh +0 -146
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
|
|
@@ -604,46 +605,6 @@ var require_dist2 = __commonJS({
|
|
|
604
605
|
}
|
|
605
606
|
});
|
|
606
607
|
|
|
607
|
-
// ../lib/require-port-env/dist/index.js
|
|
608
|
-
var require_dist3 = __commonJS({
|
|
609
|
-
"../lib/require-port-env/dist/index.js"(exports) {
|
|
610
|
-
"use strict";
|
|
611
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
612
|
-
exports.MissingPortEnvError = void 0;
|
|
613
|
-
exports.requirePortEnv = requirePortEnv3;
|
|
614
|
-
var MissingPortEnvError = class extends Error {
|
|
615
|
-
envName;
|
|
616
|
-
tag;
|
|
617
|
-
failLine;
|
|
618
|
-
constructor(envName, tag, failLine, message) {
|
|
619
|
-
super(message);
|
|
620
|
-
this.name = "MissingPortEnvError";
|
|
621
|
-
this.envName = envName;
|
|
622
|
-
this.tag = tag;
|
|
623
|
-
this.failLine = failLine;
|
|
624
|
-
}
|
|
625
|
-
};
|
|
626
|
-
exports.MissingPortEnvError = MissingPortEnvError;
|
|
627
|
-
var VALID_PORT_MIN = 1;
|
|
628
|
-
var VALID_PORT_MAX = 65535;
|
|
629
|
-
function requirePortEnv3(envName, options = {}) {
|
|
630
|
-
const tag = options.tag ?? "port-env";
|
|
631
|
-
const failLine = options.failLine ?? "missing-port-env";
|
|
632
|
-
const raw = process.env[envName];
|
|
633
|
-
if (raw === void 0 || raw === "") {
|
|
634
|
-
const msg = `[${tag}] ${failLine} reason=missing-env env=${envName}`;
|
|
635
|
-
throw new MissingPortEnvError(envName, tag, failLine, msg);
|
|
636
|
-
}
|
|
637
|
-
const parsed = Number.parseInt(raw, 10);
|
|
638
|
-
if (!Number.isInteger(parsed) || parsed < VALID_PORT_MIN || parsed > VALID_PORT_MAX || String(parsed) !== raw.trim()) {
|
|
639
|
-
const msg = `[${tag}] ${failLine} reason=invalid-port-env env=${envName} value=${JSON.stringify(raw)}`;
|
|
640
|
-
throw new MissingPortEnvError(envName, tag, failLine, msg);
|
|
641
|
-
}
|
|
642
|
-
return parsed;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
});
|
|
646
|
-
|
|
647
608
|
// ../lib/graph-write/dist/audit.js
|
|
648
609
|
var require_audit = __commonJS({
|
|
649
610
|
"../lib/graph-write/dist/audit.js"(exports) {
|
|
@@ -725,7 +686,7 @@ var require_conversation_provenance = __commonJS({
|
|
|
725
686
|
"use strict";
|
|
726
687
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
727
688
|
exports.injectConversationProvenance = injectConversationProvenance;
|
|
728
|
-
var index_js_1 =
|
|
689
|
+
var index_js_1 = require_dist3();
|
|
729
690
|
async function injectConversationProvenance(params) {
|
|
730
691
|
const { session, relationships, accountId, writeLabels, conversationNodeId, logNamespace, tool } = params;
|
|
731
692
|
const original = [...relationships];
|
|
@@ -775,7 +736,7 @@ var require_conversation_provenance = __commonJS({
|
|
|
775
736
|
});
|
|
776
737
|
|
|
777
738
|
// ../lib/graph-write/dist/index.js
|
|
778
|
-
var
|
|
739
|
+
var require_dist3 = __commonJS({
|
|
779
740
|
"../lib/graph-write/dist/index.js"(exports) {
|
|
780
741
|
"use strict";
|
|
781
742
|
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -1240,7 +1201,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1240
1201
|
};
|
|
1241
1202
|
|
|
1242
1203
|
// server/index.ts
|
|
1243
|
-
import { readFileSync as
|
|
1204
|
+
import { readFileSync as readFileSync23, existsSync as existsSync24, watchFile } from "fs";
|
|
1244
1205
|
import { resolve as resolve26, join as join18, basename as basename6 } from "path";
|
|
1245
1206
|
import { homedir as homedir2 } from "os";
|
|
1246
1207
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
@@ -1528,7 +1489,7 @@ async function ensureAuth() {
|
|
|
1528
1489
|
}
|
|
1529
1490
|
|
|
1530
1491
|
// server/routes/health.ts
|
|
1531
|
-
import { existsSync as
|
|
1492
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
|
|
1532
1493
|
import { createConnection as createConnection2 } from "net";
|
|
1533
1494
|
|
|
1534
1495
|
// app/lib/network.ts
|
|
@@ -2229,6 +2190,49 @@ async function resolveJidToE164(jid, lidMapping) {
|
|
|
2229
2190
|
return null;
|
|
2230
2191
|
}
|
|
2231
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
|
+
|
|
2232
2236
|
// app/lib/whatsapp/inbound/extract.ts
|
|
2233
2237
|
import {
|
|
2234
2238
|
extractMessageContent,
|
|
@@ -2783,7 +2787,7 @@ async function ensureWhatsAppConversation(input) {
|
|
|
2783
2787
|
}
|
|
2784
2788
|
|
|
2785
2789
|
// ../lib/account-enumeration/src/index.ts
|
|
2786
|
-
import { readdirSync, readFileSync as
|
|
2790
|
+
import { readdirSync, readFileSync as readFileSync3 } from "fs";
|
|
2787
2791
|
import { resolve } from "path";
|
|
2788
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;
|
|
2789
2793
|
var cache = /* @__PURE__ */ new Map();
|
|
@@ -2805,7 +2809,7 @@ function enumerateValidAccountIds(accountsDir) {
|
|
|
2805
2809
|
if (!UUID_RE.test(name)) continue;
|
|
2806
2810
|
const configPath2 = resolve(accountsDir, name, "account.json");
|
|
2807
2811
|
try {
|
|
2808
|
-
JSON.parse(
|
|
2812
|
+
JSON.parse(readFileSync3(configPath2, "utf-8"));
|
|
2809
2813
|
valid.push(name);
|
|
2810
2814
|
} catch (err) {
|
|
2811
2815
|
const code = err.code;
|
|
@@ -3917,6 +3921,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
3917
3921
|
const isGroup = isGroupJid(remoteJid);
|
|
3918
3922
|
const senderJid = isGroup ? msg.key.participant ?? remoteJid : remoteJid;
|
|
3919
3923
|
const senderPhone = await resolveJidToE164(senderJid, conn.lidMapping) ?? senderJid;
|
|
3924
|
+
const lidCapture = lidCaptureFor(senderJid, senderPhone);
|
|
3925
|
+
if (lidCapture) recordLidMapping(lidCapture.numericLid, lidCapture.e164);
|
|
3920
3926
|
const selfPhone = conn.selfPhone ?? "";
|
|
3921
3927
|
if (extracted.mediaType === "audio" && mediaResult?.path) {
|
|
3922
3928
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
@@ -4037,7 +4043,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4037
4043
|
// app/lib/vnc.ts
|
|
4038
4044
|
import { spawnSync, execFileSync } from "child_process";
|
|
4039
4045
|
import { createConnection } from "net";
|
|
4040
|
-
import { mkdirSync, readFileSync as
|
|
4046
|
+
import { mkdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
4041
4047
|
import { resolve as resolve2 } from "path";
|
|
4042
4048
|
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "..");
|
|
4043
4049
|
var VNC_SCRIPT = resolve2(PLATFORM_ROOT2, "scripts/vnc.sh");
|
|
@@ -4095,7 +4101,7 @@ function discoverNativeDisplay() {
|
|
|
4095
4101
|
const leaderPid = leaderResult.stdout?.trim();
|
|
4096
4102
|
if (leaderPid) {
|
|
4097
4103
|
try {
|
|
4098
|
-
const environ =
|
|
4104
|
+
const environ = readFileSync4(`/proc/${leaderPid}/environ`, "utf8");
|
|
4099
4105
|
const match = environ.split("\0").find((e) => e.startsWith("WAYLAND_DISPLAY="));
|
|
4100
4106
|
if (match) waylandDisplay = match.split("=")[1];
|
|
4101
4107
|
} catch {
|
|
@@ -4233,7 +4239,7 @@ function killChromium() {
|
|
|
4233
4239
|
function writeChromiumWrapper() {
|
|
4234
4240
|
mkdirSync(BIN_DIR, { recursive: true });
|
|
4235
4241
|
const wrapperPath = resolve2(BIN_DIR, "chromium");
|
|
4236
|
-
|
|
4242
|
+
writeFileSync3(wrapperPath, `#!/bin/bash
|
|
4237
4243
|
LOG="${LOG_DIR}/chromium.log"
|
|
4238
4244
|
echo "==== [$(date)] chromium wrapper ====" >> "$LOG"
|
|
4239
4245
|
echo " DISPLAY=$DISPLAY WAYLAND=$WAYLAND_DISPLAY XDG_SESSION_TYPE=$XDG_SESSION_TYPE" >> "$LOG"
|
|
@@ -4354,8 +4360,8 @@ app.get("/", async (c) => {
|
|
|
4354
4360
|
const browserTransport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
|
|
4355
4361
|
let pinConfigured = false;
|
|
4356
4362
|
try {
|
|
4357
|
-
if (
|
|
4358
|
-
const raw =
|
|
4363
|
+
if (existsSync3(USERS_FILE)) {
|
|
4364
|
+
const raw = readFileSync5(USERS_FILE, "utf-8").trim();
|
|
4359
4365
|
if (raw) {
|
|
4360
4366
|
const users = JSON.parse(raw);
|
|
4361
4367
|
pinConfigured = Array.isArray(users) && users.length > 0;
|
|
@@ -4674,6 +4680,7 @@ async function managerRcSpawn(opts) {
|
|
|
4674
4680
|
closeAfterTurn: opts.closeAfterTurn,
|
|
4675
4681
|
sliceToken: opts.sliceToken,
|
|
4676
4682
|
personId: opts.personId,
|
|
4683
|
+
userId: opts.userId,
|
|
4677
4684
|
waChannel: opts.waChannel
|
|
4678
4685
|
})
|
|
4679
4686
|
}).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
|
|
@@ -5793,13 +5800,13 @@ var chat_default = app2;
|
|
|
5793
5800
|
|
|
5794
5801
|
// server/routes/whatsapp.ts
|
|
5795
5802
|
import { join as join6, resolve as resolve7 } from "path";
|
|
5796
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
5803
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync7, existsSync as existsSync5 } from "fs";
|
|
5797
5804
|
|
|
5798
5805
|
// app/lib/whatsapp/login.ts
|
|
5799
5806
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
5800
5807
|
|
|
5801
5808
|
// app/lib/whatsapp/config-persist.ts
|
|
5802
|
-
import { readFileSync as
|
|
5809
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "fs";
|
|
5803
5810
|
import { resolve as resolve6, join as join4 } from "path";
|
|
5804
5811
|
var TAG17 = "[whatsapp:config]";
|
|
5805
5812
|
function configPath(accountDir) {
|
|
@@ -5807,12 +5814,12 @@ function configPath(accountDir) {
|
|
|
5807
5814
|
}
|
|
5808
5815
|
function readConfig(accountDir) {
|
|
5809
5816
|
const path2 = configPath(accountDir);
|
|
5810
|
-
if (!
|
|
5811
|
-
return JSON.parse(
|
|
5817
|
+
if (!existsSync4(path2)) throw new Error(`account.json not found at ${path2}`);
|
|
5818
|
+
return JSON.parse(readFileSync6(path2, "utf-8"));
|
|
5812
5819
|
}
|
|
5813
5820
|
function writeConfig(accountDir, config) {
|
|
5814
5821
|
const path2 = configPath(accountDir);
|
|
5815
|
-
|
|
5822
|
+
writeFileSync4(path2, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
5816
5823
|
}
|
|
5817
5824
|
function reloadManagerConfig(accountDir) {
|
|
5818
5825
|
try {
|
|
@@ -5953,7 +5960,7 @@ function setPublicAgent(accountDir, slug) {
|
|
|
5953
5960
|
return { ok: false, error: "Agent slug cannot be empty." };
|
|
5954
5961
|
}
|
|
5955
5962
|
const agentConfigPath = join4(accountDir, "agents", trimmed, "config.json");
|
|
5956
|
-
if (!
|
|
5963
|
+
if (!existsSync4(agentConfigPath)) {
|
|
5957
5964
|
return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
|
|
5958
5965
|
}
|
|
5959
5966
|
try {
|
|
@@ -6017,7 +6024,7 @@ function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
|
|
|
6017
6024
|
if (!trimmedGroup) return { ok: false, error: "Group JID cannot be empty." };
|
|
6018
6025
|
if (!trimmedAccount) return { ok: false, error: "Account ID cannot be empty." };
|
|
6019
6026
|
const agentConfigPath = join4(accountDir, "agents", trimmedSlug, "config.json");
|
|
6020
|
-
if (!
|
|
6027
|
+
if (!existsSync4(agentConfigPath)) {
|
|
6021
6028
|
return { ok: false, error: `Agent "${trimmedSlug}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
|
|
6022
6029
|
}
|
|
6023
6030
|
try {
|
|
@@ -6703,15 +6710,15 @@ app3.post("/config", async (c) => {
|
|
|
6703
6710
|
case "list-public-agents": {
|
|
6704
6711
|
const agentsDir = resolve7(account.accountDir, "agents");
|
|
6705
6712
|
const agents = [];
|
|
6706
|
-
if (
|
|
6713
|
+
if (existsSync5(agentsDir)) {
|
|
6707
6714
|
try {
|
|
6708
6715
|
const entries = readdirSync3(agentsDir, { withFileTypes: true });
|
|
6709
6716
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
6710
6717
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
6711
6718
|
const configPath2 = resolve7(agentsDir, entry.name, "config.json");
|
|
6712
|
-
if (!
|
|
6719
|
+
if (!existsSync5(configPath2)) continue;
|
|
6713
6720
|
try {
|
|
6714
|
-
const config = JSON.parse(
|
|
6721
|
+
const config = JSON.parse(readFileSync7(configPath2, "utf-8"));
|
|
6715
6722
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
6716
6723
|
} catch {
|
|
6717
6724
|
console.error(`${TAG20} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
@@ -6981,11 +6988,11 @@ var whatsapp_default = app3;
|
|
|
6981
6988
|
|
|
6982
6989
|
// server/routes/onboarding.ts
|
|
6983
6990
|
import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
|
|
6984
|
-
import { openSync, closeSync, writeFileSync as
|
|
6991
|
+
import { openSync, closeSync, writeFileSync as writeFileSync6, writeSync, existsSync as existsSync7, readFileSync as readFileSync9, unlinkSync } from "fs";
|
|
6985
6992
|
import { createHash as createHash2, randomUUID as randomUUID7 } from "crypto";
|
|
6986
6993
|
|
|
6987
6994
|
// ../lib/admins-write/src/index.ts
|
|
6988
|
-
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";
|
|
6989
6996
|
import { dirname, join as join7 } from "path";
|
|
6990
6997
|
function logLine(input, result) {
|
|
6991
6998
|
const userIdShort = input.userId.slice(0, 8);
|
|
@@ -6996,15 +7003,15 @@ function logLine(input, result) {
|
|
|
6996
7003
|
function writeFileAtomic(filePath, contents, mode) {
|
|
6997
7004
|
mkdirSync2(dirname(filePath), { recursive: true });
|
|
6998
7005
|
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
6999
|
-
|
|
7006
|
+
writeFileSync5(tempPath, contents, mode !== void 0 ? { mode } : void 0);
|
|
7000
7007
|
renameSync(tempPath, filePath);
|
|
7001
7008
|
}
|
|
7002
7009
|
function writeAdminEntry(input) {
|
|
7003
7010
|
const result = { usersJsonResult: "noop", accountJsonResult: "noop" };
|
|
7004
7011
|
try {
|
|
7005
7012
|
let users = [];
|
|
7006
|
-
if (
|
|
7007
|
-
const raw =
|
|
7013
|
+
if (existsSync6(input.usersFile)) {
|
|
7014
|
+
const raw = readFileSync8(input.usersFile, "utf-8").trim();
|
|
7008
7015
|
if (raw) {
|
|
7009
7016
|
const parsed = JSON.parse(raw);
|
|
7010
7017
|
if (!Array.isArray(parsed)) {
|
|
@@ -7029,10 +7036,10 @@ function writeAdminEntry(input) {
|
|
|
7029
7036
|
}
|
|
7030
7037
|
try {
|
|
7031
7038
|
const accountJsonPath = join7(input.accountDir, "account.json");
|
|
7032
|
-
if (!
|
|
7039
|
+
if (!existsSync6(accountJsonPath)) {
|
|
7033
7040
|
throw new Error(`account.json not found at ${accountJsonPath}`);
|
|
7034
7041
|
}
|
|
7035
|
-
const config = JSON.parse(
|
|
7042
|
+
const config = JSON.parse(readFileSync8(accountJsonPath, "utf-8"));
|
|
7036
7043
|
const admins = config.admins ?? [];
|
|
7037
7044
|
if (admins.some((a) => a.userId === input.userId)) {
|
|
7038
7045
|
result.accountJsonResult = "noop";
|
|
@@ -7056,9 +7063,9 @@ function checkAdminAuthInvariant(input) {
|
|
|
7056
7063
|
usersWithoutAccount: []
|
|
7057
7064
|
};
|
|
7058
7065
|
const usersUserIds = /* @__PURE__ */ new Set();
|
|
7059
|
-
if (
|
|
7066
|
+
if (existsSync6(input.usersFile)) {
|
|
7060
7067
|
try {
|
|
7061
|
-
const raw =
|
|
7068
|
+
const raw = readFileSync8(input.usersFile, "utf-8").trim();
|
|
7062
7069
|
if (raw) {
|
|
7063
7070
|
const users = JSON.parse(raw);
|
|
7064
7071
|
for (const u of users) {
|
|
@@ -7071,7 +7078,7 @@ function checkAdminAuthInvariant(input) {
|
|
|
7071
7078
|
}
|
|
7072
7079
|
}
|
|
7073
7080
|
const accountUserIds = /* @__PURE__ */ new Set();
|
|
7074
|
-
if (
|
|
7081
|
+
if (existsSync6(input.accountsDir)) {
|
|
7075
7082
|
let entries;
|
|
7076
7083
|
try {
|
|
7077
7084
|
entries = readdirSync4(input.accountsDir);
|
|
@@ -7090,10 +7097,10 @@ function checkAdminAuthInvariant(input) {
|
|
|
7090
7097
|
continue;
|
|
7091
7098
|
}
|
|
7092
7099
|
const accountJsonPath = join7(accountDir, "account.json");
|
|
7093
|
-
if (!
|
|
7100
|
+
if (!existsSync6(accountJsonPath)) continue;
|
|
7094
7101
|
let admins = [];
|
|
7095
7102
|
try {
|
|
7096
|
-
const config = JSON.parse(
|
|
7103
|
+
const config = JSON.parse(readFileSync8(accountJsonPath, "utf-8"));
|
|
7097
7104
|
admins = config.admins ?? [];
|
|
7098
7105
|
} catch (err) {
|
|
7099
7106
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -7133,8 +7140,8 @@ function hashPin(pin) {
|
|
|
7133
7140
|
return createHash2("sha256").update(pin).digest("hex");
|
|
7134
7141
|
}
|
|
7135
7142
|
function readUsersFile() {
|
|
7136
|
-
if (!
|
|
7137
|
-
const raw =
|
|
7143
|
+
if (!existsSync7(USERS_FILE)) return null;
|
|
7144
|
+
const raw = readFileSync9(USERS_FILE, "utf-8").trim();
|
|
7138
7145
|
if (!raw) return [];
|
|
7139
7146
|
return JSON.parse(raw);
|
|
7140
7147
|
}
|
|
@@ -7199,7 +7206,7 @@ app4.post("/claude-auth", async (c) => {
|
|
|
7199
7206
|
return c.json({ launched: cdpReady });
|
|
7200
7207
|
}
|
|
7201
7208
|
ensureLogDir();
|
|
7202
|
-
|
|
7209
|
+
writeFileSync6(logPath("claude-auth"), "");
|
|
7203
7210
|
const claudeAuthLogFd = openSync(logPath("claude-auth"), "a");
|
|
7204
7211
|
const onClaudeOutput = (chunk) => writeSync(claudeAuthLogFd, chunk);
|
|
7205
7212
|
if (process.platform === "darwin") {
|
|
@@ -7288,9 +7295,9 @@ app4.post("/set-pin", async (c) => {
|
|
|
7288
7295
|
console.log(`[set-pin] wrote users.json + account.json admins: userId=${userId.slice(0, 8)}\u2026 role=owner`);
|
|
7289
7296
|
if (process.platform === "linux") {
|
|
7290
7297
|
let installOwner = null;
|
|
7291
|
-
if (
|
|
7298
|
+
if (existsSync7(INSTALL_OWNER_FILE)) {
|
|
7292
7299
|
try {
|
|
7293
|
-
const raw =
|
|
7300
|
+
const raw = readFileSync9(INSTALL_OWNER_FILE, "utf-8").trim();
|
|
7294
7301
|
if (raw.length > 0) installOwner = raw;
|
|
7295
7302
|
} catch (err) {
|
|
7296
7303
|
console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -7363,7 +7370,7 @@ app4.delete("/set-pin", async (c) => {
|
|
|
7363
7370
|
unlinkSync(USERS_FILE);
|
|
7364
7371
|
console.log(`[set-pin] cleared users.json (last entry removed): userId=${matchedUser.userId.slice(0, 8)}\u2026`);
|
|
7365
7372
|
} else {
|
|
7366
|
-
|
|
7373
|
+
writeFileSync6(USERS_FILE, JSON.stringify(remaining), { mode: 384 });
|
|
7367
7374
|
console.log(`[set-pin] removed entry from users.json: userId=${matchedUser.userId.slice(0, 8)}\u2026 remaining=${remaining.length}`);
|
|
7368
7375
|
}
|
|
7369
7376
|
return c.json({ ok: true });
|
|
@@ -7371,7 +7378,7 @@ app4.delete("/set-pin", async (c) => {
|
|
|
7371
7378
|
var onboarding_default = app4;
|
|
7372
7379
|
|
|
7373
7380
|
// server/routes/client-error.ts
|
|
7374
|
-
import { appendFileSync, existsSync as
|
|
7381
|
+
import { appendFileSync, existsSync as existsSync8, renameSync as renameSync2, statSync as statSync3 } from "fs";
|
|
7375
7382
|
import { join as join8 } from "path";
|
|
7376
7383
|
var CLIENT_ERRORS_LOG = join8(LOG_DIR, "client-errors.log");
|
|
7377
7384
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
@@ -7416,7 +7423,7 @@ function stackHead(stack) {
|
|
|
7416
7423
|
}
|
|
7417
7424
|
function rotateIfNeeded() {
|
|
7418
7425
|
try {
|
|
7419
|
-
if (!
|
|
7426
|
+
if (!existsSync8(CLIENT_ERRORS_LOG)) return;
|
|
7420
7427
|
const stats = statSync3(CLIENT_ERRORS_LOG);
|
|
7421
7428
|
if (stats.size < MAX_LOG_SIZE) return;
|
|
7422
7429
|
renameSync2(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
|
|
@@ -7534,14 +7541,14 @@ import { randomUUID as randomUUID8 } from "crypto";
|
|
|
7534
7541
|
|
|
7535
7542
|
// app/lib/admin-identity/pin-validator.ts
|
|
7536
7543
|
import { createHash as createHash3 } from "crypto";
|
|
7537
|
-
import { existsSync as
|
|
7544
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
7538
7545
|
import { join as join9 } from "path";
|
|
7539
7546
|
function hashPin2(pin) {
|
|
7540
7547
|
return createHash3("sha256").update(pin).digest("hex");
|
|
7541
7548
|
}
|
|
7542
7549
|
function readUsersFile2(usersFilePath) {
|
|
7543
|
-
if (!
|
|
7544
|
-
const raw =
|
|
7550
|
+
if (!existsSync9(usersFilePath)) return null;
|
|
7551
|
+
const raw = readFileSync10(usersFilePath, "utf-8").trim();
|
|
7545
7552
|
if (!raw) return [];
|
|
7546
7553
|
return JSON.parse(raw);
|
|
7547
7554
|
}
|
|
@@ -7560,7 +7567,7 @@ function validatePin(pin, usersFilePath) {
|
|
|
7560
7567
|
function scanForOrphanedAccountAdmins(users, accountsDir) {
|
|
7561
7568
|
const known = new Set(users.map((u) => u.userId));
|
|
7562
7569
|
const orphans = [];
|
|
7563
|
-
if (!
|
|
7570
|
+
if (!existsSync9(accountsDir)) return orphans;
|
|
7564
7571
|
let entries;
|
|
7565
7572
|
try {
|
|
7566
7573
|
entries = readdirSync5(accountsDir);
|
|
@@ -7576,9 +7583,9 @@ function scanForOrphanedAccountAdmins(users, accountsDir) {
|
|
|
7576
7583
|
continue;
|
|
7577
7584
|
}
|
|
7578
7585
|
const accountJsonPath = join9(accountDir, "account.json");
|
|
7579
|
-
if (!
|
|
7586
|
+
if (!existsSync9(accountJsonPath)) continue;
|
|
7580
7587
|
try {
|
|
7581
|
-
const config = JSON.parse(
|
|
7588
|
+
const config = JSON.parse(readFileSync10(accountJsonPath, "utf-8"));
|
|
7582
7589
|
const admins = config.admins ?? [];
|
|
7583
7590
|
for (const a of admins) {
|
|
7584
7591
|
if (typeof a.userId === "string" && !known.has(a.userId)) orphans.push(a.userId);
|
|
@@ -7758,18 +7765,18 @@ app6.post("/", async (c) => {
|
|
|
7758
7765
|
var session_default = app6;
|
|
7759
7766
|
|
|
7760
7767
|
// server/routes/admin/logs.ts
|
|
7761
|
-
import { existsSync as
|
|
7768
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync11, statSync as statSync5 } from "fs";
|
|
7762
7769
|
import { resolve as resolve8, basename as basename2 } from "path";
|
|
7763
7770
|
|
|
7764
7771
|
// app/lib/logs-read-resolve.ts
|
|
7765
|
-
import { existsSync as
|
|
7772
|
+
import { existsSync as existsSync10 } from "fs";
|
|
7766
7773
|
import { join as join10 } from "path";
|
|
7767
7774
|
function resolveSessionLogPaths(filename, logDirs) {
|
|
7768
7775
|
const tried = [filename];
|
|
7769
7776
|
const hits = [];
|
|
7770
7777
|
for (const dir of logDirs) {
|
|
7771
7778
|
const fullPath = join10(dir, filename);
|
|
7772
|
-
if (
|
|
7779
|
+
if (existsSync10(fullPath)) {
|
|
7773
7780
|
hits.push({ path: fullPath, dir });
|
|
7774
7781
|
}
|
|
7775
7782
|
}
|
|
@@ -7797,7 +7804,7 @@ app7.get("/", async (c) => {
|
|
|
7797
7804
|
const filePath = resolve8(dir, safe);
|
|
7798
7805
|
searched.push(filePath);
|
|
7799
7806
|
try {
|
|
7800
|
-
const buffer =
|
|
7807
|
+
const buffer = readFileSync11(filePath);
|
|
7801
7808
|
const onDiskBytes = statSync5(filePath).size;
|
|
7802
7809
|
const headers = {
|
|
7803
7810
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -7862,7 +7869,7 @@ app7.get("/", async (c) => {
|
|
|
7862
7869
|
console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
|
|
7863
7870
|
try {
|
|
7864
7871
|
const filename = basename2(hit.path);
|
|
7865
|
-
const buffer =
|
|
7872
|
+
const buffer = readFileSync11(hit.path);
|
|
7866
7873
|
const onDiskBytes = statSync5(hit.path).size;
|
|
7867
7874
|
const headers = {
|
|
7868
7875
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -7897,7 +7904,7 @@ app7.get("/", async (c) => {
|
|
|
7897
7904
|
const seen = /* @__PURE__ */ new Set();
|
|
7898
7905
|
const logs = {};
|
|
7899
7906
|
for (const dir of logDirs) {
|
|
7900
|
-
if (!
|
|
7907
|
+
if (!existsSync11(dir)) continue;
|
|
7901
7908
|
let files;
|
|
7902
7909
|
try {
|
|
7903
7910
|
files = readdirSync6(dir).filter((f) => f.endsWith(".log"));
|
|
@@ -7909,7 +7916,7 @@ app7.get("/", async (c) => {
|
|
|
7909
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 }) => {
|
|
7910
7917
|
seen.add(name);
|
|
7911
7918
|
try {
|
|
7912
|
-
const content =
|
|
7919
|
+
const content = readFileSync11(resolve8(dir, name));
|
|
7913
7920
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
7914
7921
|
logs[name] = tail.trim() || "(empty)";
|
|
7915
7922
|
} catch (err) {
|
|
@@ -7948,7 +7955,7 @@ var claude_info_default = app8;
|
|
|
7948
7955
|
|
|
7949
7956
|
// server/routes/admin/attachment.ts
|
|
7950
7957
|
import { readFile as readFile2, readdir } from "fs/promises";
|
|
7951
|
-
import { existsSync as
|
|
7958
|
+
import { existsSync as existsSync12 } from "fs";
|
|
7952
7959
|
import { resolve as resolve9 } from "path";
|
|
7953
7960
|
var app9 = new Hono();
|
|
7954
7961
|
app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
@@ -7962,11 +7969,11 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
7962
7969
|
return new Response("Not found", { status: 404 });
|
|
7963
7970
|
}
|
|
7964
7971
|
const dir = resolve9(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
7965
|
-
if (!
|
|
7972
|
+
if (!existsSync12(dir)) {
|
|
7966
7973
|
return new Response("Not found", { status: 404 });
|
|
7967
7974
|
}
|
|
7968
7975
|
const metaPath = resolve9(dir, `${attachmentId}.meta.json`);
|
|
7969
|
-
if (!
|
|
7976
|
+
if (!existsSync12(metaPath)) {
|
|
7970
7977
|
return new Response("Not found", { status: 404 });
|
|
7971
7978
|
}
|
|
7972
7979
|
let meta;
|
|
@@ -7994,13 +8001,13 @@ var attachment_default = app9;
|
|
|
7994
8001
|
|
|
7995
8002
|
// server/routes/admin/agents.ts
|
|
7996
8003
|
import { resolve as resolve10 } from "path";
|
|
7997
|
-
import { readdirSync as readdirSync7, readFileSync as
|
|
8004
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync12, existsSync as existsSync13, rmSync } from "fs";
|
|
7998
8005
|
var app10 = new Hono();
|
|
7999
8006
|
app10.get("/", (c) => {
|
|
8000
8007
|
const account = resolveAccount();
|
|
8001
8008
|
if (!account) return c.json({ agents: [] });
|
|
8002
8009
|
const agentsDir = resolve10(account.accountDir, "agents");
|
|
8003
|
-
if (!
|
|
8010
|
+
if (!existsSync13(agentsDir)) return c.json({ agents: [] });
|
|
8004
8011
|
const agents = [];
|
|
8005
8012
|
try {
|
|
8006
8013
|
const entries = readdirSync7(agentsDir, { withFileTypes: true });
|
|
@@ -8008,9 +8015,9 @@ app10.get("/", (c) => {
|
|
|
8008
8015
|
if (!entry.isDirectory()) continue;
|
|
8009
8016
|
if (entry.name === "admin") continue;
|
|
8010
8017
|
const configPath2 = resolve10(agentsDir, entry.name, "config.json");
|
|
8011
|
-
if (!
|
|
8018
|
+
if (!existsSync13(configPath2)) continue;
|
|
8012
8019
|
try {
|
|
8013
|
-
const config = JSON.parse(
|
|
8020
|
+
const config = JSON.parse(readFileSync12(configPath2, "utf-8"));
|
|
8014
8021
|
agents.push({
|
|
8015
8022
|
slug: entry.name,
|
|
8016
8023
|
displayName: config.displayName ?? entry.name,
|
|
@@ -8037,7 +8044,7 @@ app10.delete("/:slug", async (c) => {
|
|
|
8037
8044
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
8038
8045
|
}
|
|
8039
8046
|
const agentDir = resolve10(account.accountDir, "agents", slug);
|
|
8040
|
-
if (!
|
|
8047
|
+
if (!existsSync13(agentDir)) {
|
|
8041
8048
|
return c.json({ error: "Agent not found" }, 404);
|
|
8042
8049
|
}
|
|
8043
8050
|
try {
|
|
@@ -8067,7 +8074,7 @@ app10.post("/:slug/project", async (c) => {
|
|
|
8067
8074
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
8068
8075
|
}
|
|
8069
8076
|
const agentDir = resolve10(account.accountDir, "agents", slug);
|
|
8070
|
-
if (!
|
|
8077
|
+
if (!existsSync13(agentDir)) {
|
|
8071
8078
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
8072
8079
|
}
|
|
8073
8080
|
try {
|
|
@@ -8083,7 +8090,7 @@ var agents_default = app10;
|
|
|
8083
8090
|
// server/routes/admin/sessions.ts
|
|
8084
8091
|
import crypto2 from "crypto";
|
|
8085
8092
|
import { resolve as resolvePath } from "path";
|
|
8086
|
-
import { existsSync as
|
|
8093
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync3, createWriteStream } from "fs";
|
|
8087
8094
|
|
|
8088
8095
|
// ../lib/admin-conversation-purge/src/index.ts
|
|
8089
8096
|
async function purgeAdminConversationJsonls(args) {
|
|
@@ -8243,7 +8250,7 @@ function validateAndShapeAttachments(raws, conversationAccountId, sessionId, mes
|
|
|
8243
8250
|
let reason = null;
|
|
8244
8251
|
if (!a.attachmentId || !a.filename || !a.mimeType || !a.storagePath) reason = "schema-fail";
|
|
8245
8252
|
else if (a.accountId !== conversationAccountId) reason = "account-mismatch";
|
|
8246
|
-
else if (!
|
|
8253
|
+
else if (!existsSync14(a.storagePath)) reason = "missing-file";
|
|
8247
8254
|
if (reason) {
|
|
8248
8255
|
invalid++;
|
|
8249
8256
|
try {
|
|
@@ -8434,11 +8441,11 @@ app11.delete("/:id", requireAdminSession, async (c) => {
|
|
|
8434
8441
|
const owned = await verifyConversationOwnership(sessionId, accountId);
|
|
8435
8442
|
if (!owned) return c.json({ error: "Conversation not found" }, 404);
|
|
8436
8443
|
const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-conversation-delete" });
|
|
8437
|
-
const
|
|
8444
|
+
const managerBase5 = `http://127.0.0.1:${managerPort}`;
|
|
8438
8445
|
const outcome = await cascadeAdminConversationDelete({
|
|
8439
8446
|
sessionId,
|
|
8440
8447
|
accountId,
|
|
8441
|
-
managerBase:
|
|
8448
|
+
managerBase: managerBase5
|
|
8442
8449
|
});
|
|
8443
8450
|
if (outcome.ok) return c.json({ ok: true, claudeSessionIds: outcome.claudeSessionIds });
|
|
8444
8451
|
if (outcome.reason === "pty-still-alive") {
|
|
@@ -8770,7 +8777,7 @@ app11.put("/:id/label", requireAdminSession, async (c) => {
|
|
|
8770
8777
|
var sessions_default = app11;
|
|
8771
8778
|
|
|
8772
8779
|
// app/lib/claude-agent/spawn-context.ts
|
|
8773
|
-
import { existsSync as
|
|
8780
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13, readdirSync as readdirSync8, statSync as statSync6 } from "fs";
|
|
8774
8781
|
import { dirname as dirname2, resolve as resolve11, join as join11 } from "path";
|
|
8775
8782
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
8776
8783
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
@@ -8805,9 +8812,9 @@ function extractPluginToolDescriptions(pluginDir) {
|
|
|
8805
8812
|
];
|
|
8806
8813
|
let raw = null;
|
|
8807
8814
|
for (const path2 of candidates) {
|
|
8808
|
-
if (!
|
|
8815
|
+
if (!existsSync15(path2)) continue;
|
|
8809
8816
|
try {
|
|
8810
|
-
raw =
|
|
8817
|
+
raw = readFileSync13(path2, "utf-8");
|
|
8811
8818
|
break;
|
|
8812
8819
|
} catch {
|
|
8813
8820
|
}
|
|
@@ -8857,7 +8864,7 @@ function parseSkillPathsFromFrontmatter(fm) {
|
|
|
8857
8864
|
function listPluginDirs() {
|
|
8858
8865
|
const out = /* @__PURE__ */ new Map();
|
|
8859
8866
|
const platformPlugins = resolve11(PLATFORM_ROOT, "plugins");
|
|
8860
|
-
if (
|
|
8867
|
+
if (existsSync15(platformPlugins)) {
|
|
8861
8868
|
for (const name of readdirSync8(platformPlugins)) {
|
|
8862
8869
|
const dir = join11(platformPlugins, name);
|
|
8863
8870
|
try {
|
|
@@ -8867,10 +8874,10 @@ function listPluginDirs() {
|
|
|
8867
8874
|
}
|
|
8868
8875
|
}
|
|
8869
8876
|
const premiumRoot = resolve11(dirname2(PLATFORM_ROOT), "premium-plugins");
|
|
8870
|
-
if (
|
|
8877
|
+
if (existsSync15(premiumRoot)) {
|
|
8871
8878
|
for (const bundle of readdirSync8(premiumRoot)) {
|
|
8872
8879
|
const bundlePlugins = join11(premiumRoot, bundle, "plugins");
|
|
8873
|
-
if (!
|
|
8880
|
+
if (!existsSync15(bundlePlugins)) continue;
|
|
8874
8881
|
try {
|
|
8875
8882
|
for (const name of readdirSync8(bundlePlugins)) {
|
|
8876
8883
|
const dir = join11(bundlePlugins, name);
|
|
@@ -8887,9 +8894,9 @@ function listPluginDirs() {
|
|
|
8887
8894
|
}
|
|
8888
8895
|
function readEnabledPlugins(accountId) {
|
|
8889
8896
|
const accountFile = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
8890
|
-
if (!
|
|
8897
|
+
if (!existsSync15(accountFile)) return /* @__PURE__ */ new Set();
|
|
8891
8898
|
try {
|
|
8892
|
-
const parsed = JSON.parse(
|
|
8899
|
+
const parsed = JSON.parse(readFileSync13(accountFile, "utf-8"));
|
|
8893
8900
|
return new Set(
|
|
8894
8901
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
8895
8902
|
);
|
|
@@ -8898,10 +8905,10 @@ function readEnabledPlugins(accountId) {
|
|
|
8898
8905
|
}
|
|
8899
8906
|
}
|
|
8900
8907
|
function readFrontmatter(path2) {
|
|
8901
|
-
if (!
|
|
8908
|
+
if (!existsSync15(path2)) return null;
|
|
8902
8909
|
let raw;
|
|
8903
8910
|
try {
|
|
8904
|
-
raw =
|
|
8911
|
+
raw = readFileSync13(path2, "utf-8");
|
|
8905
8912
|
} catch {
|
|
8906
8913
|
return null;
|
|
8907
8914
|
}
|
|
@@ -8933,7 +8940,7 @@ ${fm}
|
|
|
8933
8940
|
`plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
|
|
8934
8941
|
);
|
|
8935
8942
|
if (toolNames.length > 0 && descMap.size === 0) {
|
|
8936
|
-
const hasSource =
|
|
8943
|
+
const hasSource = existsSync15(join11(dir, "mcp/dist/index.js")) || existsSync15(join11(dir, "mcp/src/index.ts"));
|
|
8937
8944
|
if (hasSource) {
|
|
8938
8945
|
console.warn(
|
|
8939
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`
|
|
@@ -8960,7 +8967,7 @@ ${skillFm}
|
|
|
8960
8967
|
}
|
|
8961
8968
|
function computeSpecialistDomains(accountId) {
|
|
8962
8969
|
const specialistsDir = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
|
|
8963
|
-
if (!
|
|
8970
|
+
if (!existsSync15(specialistsDir)) return [];
|
|
8964
8971
|
let entries;
|
|
8965
8972
|
try {
|
|
8966
8973
|
entries = readdirSync8(specialistsDir);
|
|
@@ -9006,10 +9013,10 @@ ${fm}
|
|
|
9006
9013
|
}
|
|
9007
9014
|
function computeDormantPlugins(accountId) {
|
|
9008
9015
|
const accountFile = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
9009
|
-
if (!
|
|
9016
|
+
if (!existsSync15(accountFile)) return [];
|
|
9010
9017
|
let enabled;
|
|
9011
9018
|
try {
|
|
9012
|
-
const raw =
|
|
9019
|
+
const raw = readFileSync13(accountFile, "utf-8");
|
|
9013
9020
|
const parsed = JSON.parse(raw);
|
|
9014
9021
|
enabled = new Set(
|
|
9015
9022
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
@@ -9021,7 +9028,7 @@ function computeDormantPlugins(accountId) {
|
|
|
9021
9028
|
return [];
|
|
9022
9029
|
}
|
|
9023
9030
|
const pluginsDir = resolve11(PLATFORM_ROOT, "plugins");
|
|
9024
|
-
if (!
|
|
9031
|
+
if (!existsSync15(pluginsDir)) return [];
|
|
9025
9032
|
let entries;
|
|
9026
9033
|
try {
|
|
9027
9034
|
entries = readdirSync8(pluginsDir);
|
|
@@ -9032,10 +9039,10 @@ function computeDormantPlugins(accountId) {
|
|
|
9032
9039
|
for (const name of entries) {
|
|
9033
9040
|
if (enabled.has(name)) continue;
|
|
9034
9041
|
const manifestPath = resolve11(pluginsDir, name, "PLUGIN.md");
|
|
9035
|
-
if (!
|
|
9042
|
+
if (!existsSync15(manifestPath)) continue;
|
|
9036
9043
|
let manifest;
|
|
9037
9044
|
try {
|
|
9038
|
-
manifest =
|
|
9045
|
+
manifest = readFileSync13(manifestPath, "utf-8");
|
|
9039
9046
|
} catch {
|
|
9040
9047
|
continue;
|
|
9041
9048
|
}
|
|
@@ -9049,13 +9056,13 @@ function computeDormantPlugins(accountId) {
|
|
|
9049
9056
|
}
|
|
9050
9057
|
|
|
9051
9058
|
// server/routes/admin/claude-sessions.ts
|
|
9052
|
-
import { existsSync as
|
|
9059
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
|
|
9053
9060
|
import { resolve as resolve12 } from "path";
|
|
9054
9061
|
function readTunnelState(brandConfigDir) {
|
|
9055
9062
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
9056
|
-
if (!
|
|
9063
|
+
if (!existsSync16(statePath)) return null;
|
|
9057
9064
|
try {
|
|
9058
|
-
const parsed = JSON.parse(
|
|
9065
|
+
const parsed = JSON.parse(readFileSync14(statePath, "utf-8"));
|
|
9059
9066
|
const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
|
|
9060
9067
|
const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
|
|
9061
9068
|
const domain = typeof parsed.domain === "string" ? parsed.domain : null;
|
|
@@ -9069,7 +9076,7 @@ function resolveTunnelUrl() {
|
|
|
9069
9076
|
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve12(process.cwd(), "..");
|
|
9070
9077
|
let brand;
|
|
9071
9078
|
try {
|
|
9072
|
-
brand = JSON.parse(
|
|
9079
|
+
brand = JSON.parse(readFileSync14(resolve12(platformRoot2, "config", "brand.json"), "utf-8"));
|
|
9073
9080
|
} catch {
|
|
9074
9081
|
return null;
|
|
9075
9082
|
}
|
|
@@ -9282,18 +9289,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
9282
9289
|
]);
|
|
9283
9290
|
var app14 = new Hono();
|
|
9284
9291
|
app14.post("/", async (c) => {
|
|
9285
|
-
const
|
|
9292
|
+
const TAG30 = "[admin:events]";
|
|
9286
9293
|
let body;
|
|
9287
9294
|
try {
|
|
9288
9295
|
body = await c.req.json();
|
|
9289
9296
|
} catch (err) {
|
|
9290
9297
|
const detail = err instanceof Error ? err.message : String(err);
|
|
9291
|
-
console.error(`${
|
|
9298
|
+
console.error(`${TAG30} reject reason=body-not-json detail=${detail}`);
|
|
9292
9299
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
9293
9300
|
}
|
|
9294
9301
|
const event = typeof body.event === "string" ? body.event : "";
|
|
9295
9302
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
9296
|
-
console.error(`${
|
|
9303
|
+
console.error(`${TAG30} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
9297
9304
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
9298
9305
|
}
|
|
9299
9306
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -12106,7 +12113,7 @@ app18.post("/", requireAdminSession, async (c) => {
|
|
|
12106
12113
|
return c.json({ error: "AdminConversation node is missing sessionId" }, 500);
|
|
12107
12114
|
}
|
|
12108
12115
|
const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "graph-page-cascade" });
|
|
12109
|
-
const
|
|
12116
|
+
const managerBase5 = `http://127.0.0.1:${managerPort}`;
|
|
12110
12117
|
try {
|
|
12111
12118
|
await session.close();
|
|
12112
12119
|
} catch {
|
|
@@ -12114,7 +12121,7 @@ app18.post("/", requireAdminSession, async (c) => {
|
|
|
12114
12121
|
const outcome = await cascadeAdminConversationDelete({
|
|
12115
12122
|
sessionId,
|
|
12116
12123
|
accountId,
|
|
12117
|
-
managerBase:
|
|
12124
|
+
managerBase: managerBase5
|
|
12118
12125
|
});
|
|
12119
12126
|
const elapsed2 = Date.now() - started;
|
|
12120
12127
|
const labelSummary2 = preflightLabels.join(",");
|
|
@@ -12494,7 +12501,7 @@ var graph_default_view_default = app21;
|
|
|
12494
12501
|
// server/routes/admin/sidebar-artefacts.ts
|
|
12495
12502
|
import { readdir as readdir4, stat as stat5 } from "fs/promises";
|
|
12496
12503
|
import { resolve as resolve16, relative as relative3, isAbsolute, sep as sep5, basename as basename5 } from "path";
|
|
12497
|
-
import { existsSync as
|
|
12504
|
+
import { existsSync as existsSync17 } from "fs";
|
|
12498
12505
|
var LIMIT = 50;
|
|
12499
12506
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
12500
12507
|
function toDataRootRelPath(absPath) {
|
|
@@ -12509,27 +12516,27 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
12509
12516
|
return c.json({ error: "Account not found for session" }, 401);
|
|
12510
12517
|
}
|
|
12511
12518
|
const start = Date.now();
|
|
12512
|
-
const
|
|
12513
|
-
if (
|
|
12519
|
+
const accountFiles = await fetchAccountFileArtefacts(accountId);
|
|
12520
|
+
if (accountFiles === null) {
|
|
12514
12521
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
12515
12522
|
}
|
|
12516
12523
|
const accountDir = resolve16(ACCOUNTS_DIR, accountId);
|
|
12517
12524
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
12518
|
-
const artefacts = [...
|
|
12525
|
+
const artefacts = [...accountFiles, ...agents].sort(
|
|
12519
12526
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
12520
12527
|
).slice(0, LIMIT);
|
|
12521
12528
|
const ms = Date.now() - start;
|
|
12522
12529
|
console.log(
|
|
12523
|
-
`[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}`
|
|
12524
12531
|
);
|
|
12525
12532
|
return c.json({ artefacts });
|
|
12526
12533
|
});
|
|
12527
|
-
async function
|
|
12534
|
+
async function fetchAccountFileArtefacts(accountId) {
|
|
12528
12535
|
const session = getSession();
|
|
12529
12536
|
try {
|
|
12530
12537
|
const result = await session.run(
|
|
12531
12538
|
`MATCH (f:FileArtifact { accountId: $accountId })
|
|
12532
|
-
WHERE f.relativePath
|
|
12539
|
+
WHERE f.relativePath STARTS WITH 'accounts/'
|
|
12533
12540
|
RETURN f.relativePath AS relativePath,
|
|
12534
12541
|
f.displayName AS displayName,
|
|
12535
12542
|
f.mimeType AS mimeType,
|
|
@@ -12544,9 +12551,9 @@ async function fetchOutputArtefacts(accountId) {
|
|
|
12544
12551
|
const mimeType = r.get("mimeType") ?? "";
|
|
12545
12552
|
const modifiedAt = r.get("modifiedAt") ?? "";
|
|
12546
12553
|
return {
|
|
12547
|
-
id: `
|
|
12554
|
+
id: `account-file:${relativePath}`,
|
|
12548
12555
|
name: displayName ?? basename5(relativePath),
|
|
12549
|
-
kind: "
|
|
12556
|
+
kind: "account-file",
|
|
12550
12557
|
updatedAt: modifiedAt,
|
|
12551
12558
|
mimeType,
|
|
12552
12559
|
downloadPath: relativePath,
|
|
@@ -12600,7 +12607,7 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
12600
12607
|
async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
12601
12608
|
const names = /* @__PURE__ */ new Set();
|
|
12602
12609
|
for (const dir of [overrideDir, bundledDir]) {
|
|
12603
|
-
if (!
|
|
12610
|
+
if (!existsSync17(dir)) continue;
|
|
12604
12611
|
try {
|
|
12605
12612
|
const entries = await readdir4(dir);
|
|
12606
12613
|
for (const entry of entries) {
|
|
@@ -12615,7 +12622,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
12615
12622
|
}
|
|
12616
12623
|
async function readAgentTemplateRow(inp) {
|
|
12617
12624
|
let chosenPath = null;
|
|
12618
|
-
if (
|
|
12625
|
+
if (existsSync17(inp.overridePath)) {
|
|
12619
12626
|
try {
|
|
12620
12627
|
validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
|
|
12621
12628
|
chosenPath = inp.overridePath;
|
|
@@ -12626,7 +12633,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
12626
12633
|
);
|
|
12627
12634
|
return null;
|
|
12628
12635
|
}
|
|
12629
|
-
} else if (
|
|
12636
|
+
} else if (existsSync17(inp.bundledPath)) {
|
|
12630
12637
|
if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
|
|
12631
12638
|
console.error(
|
|
12632
12639
|
`[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
|
|
@@ -12668,7 +12675,7 @@ function isWithin(target, root) {
|
|
|
12668
12675
|
var sidebar_artefacts_default = app22;
|
|
12669
12676
|
|
|
12670
12677
|
// server/routes/admin/sidebar-sessions.ts
|
|
12671
|
-
import { readdirSync as readdirSync9, readFileSync as
|
|
12678
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync15, statSync as statSync7 } from "fs";
|
|
12672
12679
|
import { join as join14, resolve as resolve17 } from "path";
|
|
12673
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;
|
|
12674
12681
|
var PID_FILE_RE = /^([0-9]+)\.json$/;
|
|
@@ -12738,7 +12745,7 @@ function sessionIdToPidMap(configDir2) {
|
|
|
12738
12745
|
if (!Number.isFinite(pid) || pid <= 0) continue;
|
|
12739
12746
|
let body;
|
|
12740
12747
|
try {
|
|
12741
|
-
body =
|
|
12748
|
+
body = readFileSync15(join14(sessionsDir, entry.name), "utf8");
|
|
12742
12749
|
} catch {
|
|
12743
12750
|
continue;
|
|
12744
12751
|
}
|
|
@@ -12760,7 +12767,7 @@ function loadUserTitles(accountDir) {
|
|
|
12760
12767
|
const path2 = join14(accountDir, "session-titles.json");
|
|
12761
12768
|
let raw;
|
|
12762
12769
|
try {
|
|
12763
|
-
raw =
|
|
12770
|
+
raw = readFileSync15(path2, "utf8");
|
|
12764
12771
|
} catch {
|
|
12765
12772
|
return out;
|
|
12766
12773
|
}
|
|
@@ -12785,7 +12792,7 @@ function loadUserTitles(accountDir) {
|
|
|
12785
12792
|
function readSidecarMeta(metaPath) {
|
|
12786
12793
|
let raw;
|
|
12787
12794
|
try {
|
|
12788
|
-
raw =
|
|
12795
|
+
raw = readFileSync15(metaPath, "utf8");
|
|
12789
12796
|
} catch {
|
|
12790
12797
|
return { bridgeIds: [], role: null, channel: null };
|
|
12791
12798
|
}
|
|
@@ -12875,7 +12882,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
12875
12882
|
let body;
|
|
12876
12883
|
let mtimeMs;
|
|
12877
12884
|
try {
|
|
12878
|
-
body =
|
|
12885
|
+
body = readFileSync15(path2, "utf8");
|
|
12879
12886
|
mtimeMs = statSync7(path2).mtimeMs;
|
|
12880
12887
|
} catch {
|
|
12881
12888
|
continue;
|
|
@@ -12912,7 +12919,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
12912
12919
|
var sidebar_sessions_default = app23;
|
|
12913
12920
|
|
|
12914
12921
|
// server/routes/admin/session-delete.ts
|
|
12915
|
-
import { existsSync as
|
|
12922
|
+
import { existsSync as existsSync18, readdirSync as readdirSync10, readFileSync as readFileSync16, unlinkSync as unlinkSync2 } from "fs";
|
|
12916
12923
|
import { join as join15, resolve as resolve18 } from "path";
|
|
12917
12924
|
var app24 = new Hono();
|
|
12918
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;
|
|
@@ -12936,7 +12943,7 @@ function findPidForSession(configDir2, sessionId) {
|
|
|
12936
12943
|
if (!Number.isFinite(pid) || pid <= 0) continue;
|
|
12937
12944
|
let body;
|
|
12938
12945
|
try {
|
|
12939
|
-
body =
|
|
12946
|
+
body = readFileSync16(join15(sessionsDir, entry.name), "utf8");
|
|
12940
12947
|
} catch {
|
|
12941
12948
|
continue;
|
|
12942
12949
|
}
|
|
@@ -13059,7 +13066,7 @@ app24.post("/", requireAdminSession, async (c) => {
|
|
|
13059
13066
|
}
|
|
13060
13067
|
let deleted = false;
|
|
13061
13068
|
try {
|
|
13062
|
-
if (
|
|
13069
|
+
if (existsSync18(jsonlPath)) {
|
|
13063
13070
|
unlinkSync2(jsonlPath);
|
|
13064
13071
|
deleted = true;
|
|
13065
13072
|
}
|
|
@@ -13072,17 +13079,17 @@ app24.post("/", requireAdminSession, async (c) => {
|
|
|
13072
13079
|
return c.json({ error: `unlink failed: ${msg}` }, 500);
|
|
13073
13080
|
}
|
|
13074
13081
|
try {
|
|
13075
|
-
if (
|
|
13082
|
+
if (existsSync18(sidecarPath)) unlinkSync2(sidecarPath);
|
|
13076
13083
|
} catch {
|
|
13077
13084
|
}
|
|
13078
13085
|
if (pid !== null) {
|
|
13079
13086
|
try {
|
|
13080
13087
|
const pidFile = join15(configDir2, "sessions", `${pid}.json`);
|
|
13081
|
-
if (
|
|
13088
|
+
if (existsSync18(pidFile)) unlinkSync2(pidFile);
|
|
13082
13089
|
} catch {
|
|
13083
13090
|
}
|
|
13084
13091
|
}
|
|
13085
|
-
if (
|
|
13092
|
+
if (existsSync18(jsonlPath)) {
|
|
13086
13093
|
console.error(`[sessions-delete] resurrection jsonlPath=${jsonlPath} sessionId=${sessionId}`);
|
|
13087
13094
|
return c.json(
|
|
13088
13095
|
{ error: "jsonl-resurrected", jsonlPath, pidKilled, waitForExitMs, escalatedToSIGKILL },
|
|
@@ -13261,14 +13268,14 @@ app26.get("/", async (c) => {
|
|
|
13261
13268
|
var system_stats_default = app26;
|
|
13262
13269
|
|
|
13263
13270
|
// server/routes/admin/health.ts
|
|
13264
|
-
import { existsSync as
|
|
13271
|
+
import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
|
|
13265
13272
|
import { resolve as resolve19, join as join16 } from "path";
|
|
13266
13273
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
|
|
13267
13274
|
var brandHostname = "maxy";
|
|
13268
13275
|
var brandJsonPath = join16(PLATFORM_ROOT6, "config", "brand.json");
|
|
13269
|
-
if (
|
|
13276
|
+
if (existsSync19(brandJsonPath)) {
|
|
13270
13277
|
try {
|
|
13271
|
-
const brand = JSON.parse(
|
|
13278
|
+
const brand = JSON.parse(readFileSync17(brandJsonPath, "utf-8"));
|
|
13272
13279
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
13273
13280
|
} catch {
|
|
13274
13281
|
}
|
|
@@ -13277,8 +13284,8 @@ var VERSION_FILE = resolve19(PLATFORM_ROOT6, `config/.${brandHostname}-version`)
|
|
|
13277
13284
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
13278
13285
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
13279
13286
|
function readVersion() {
|
|
13280
|
-
if (!
|
|
13281
|
-
return
|
|
13287
|
+
if (!existsSync19(VERSION_FILE)) return "unknown";
|
|
13288
|
+
return readFileSync17(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
13282
13289
|
}
|
|
13283
13290
|
async function probeConversationDb() {
|
|
13284
13291
|
let session;
|
|
@@ -13752,61 +13759,9 @@ app32.post("/", async (c) => {
|
|
|
13752
13759
|
});
|
|
13753
13760
|
var access_session_evict_default = app32;
|
|
13754
13761
|
|
|
13755
|
-
// server/routes/admin/identity.ts
|
|
13756
|
-
var import_dist4 = __toESM(require_dist3(), 1);
|
|
13757
|
-
var app33 = new Hono();
|
|
13758
|
-
var TAG28 = "[admin-identity]";
|
|
13759
|
-
function managerBase5() {
|
|
13760
|
-
const port2 = (0, import_dist4.requirePortEnv)("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-identity" });
|
|
13761
|
-
return `http://127.0.0.1:${port2}`;
|
|
13762
|
-
}
|
|
13763
|
-
app33.post("/validate", async (c) => {
|
|
13764
|
-
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
13765
|
-
const isLoopback = remoteAddr === "127.0.0.1" || remoteAddr === "::1" || remoteAddr === "::ffff:127.0.0.1";
|
|
13766
|
-
if (!isLoopback || c.req.header("x-forwarded-for")) {
|
|
13767
|
-
return c.json({ error: "forbidden" }, 403);
|
|
13768
|
-
}
|
|
13769
|
-
let body;
|
|
13770
|
-
try {
|
|
13771
|
-
body = await c.req.json();
|
|
13772
|
-
} catch {
|
|
13773
|
-
return c.json({ error: "bad-request" }, 400);
|
|
13774
|
-
}
|
|
13775
|
-
const pin = typeof body.pin === "string" ? body.pin : "";
|
|
13776
|
-
const sessionId = typeof body.sessionId === "string" ? body.sessionId : void 0;
|
|
13777
|
-
if (!pin) return c.json({ error: "bad-request" }, 400);
|
|
13778
|
-
const fireStop = () => {
|
|
13779
|
-
if (sessionId) {
|
|
13780
|
-
void fetch(`${managerBase5()}/${encodeURIComponent(sessionId)}/stop`, { method: "POST" }).catch(
|
|
13781
|
-
() => {
|
|
13782
|
-
}
|
|
13783
|
-
);
|
|
13784
|
-
}
|
|
13785
|
-
};
|
|
13786
|
-
const verdict = validatePin(pin, USERS_FILE);
|
|
13787
|
-
if (verdict.kind !== "match") {
|
|
13788
|
-
if (verdict.kind === "miss") fireStop();
|
|
13789
|
-
console.log(`${TAG28} op=endpoint-validate result=${verdict.kind}${sessionId ? ` sessionId=${sessionId.slice(0, 8)}` : ""}`);
|
|
13790
|
-
return c.json({ kind: verdict.kind });
|
|
13791
|
-
}
|
|
13792
|
-
const { userId } = verdict;
|
|
13793
|
-
const accounts = resolveUserAccounts(userId);
|
|
13794
|
-
if (accounts.length === 0) {
|
|
13795
|
-
fireStop();
|
|
13796
|
-
console.log(`${TAG28} op=endpoint-validate result=no-account userId=${userId.slice(0, 8)}`);
|
|
13797
|
-
return c.json({ kind: "miss" });
|
|
13798
|
-
}
|
|
13799
|
-
const accountId = accounts[0].accountId;
|
|
13800
|
-
const { userName } = await resolveUserIdentity(accountId, userId);
|
|
13801
|
-
const aboutOwner = await resolveOwnerProfileBlock(accountId, userId);
|
|
13802
|
-
console.log(`${TAG28} op=endpoint-validate result=match userId=${userId.slice(0, 8)} aboutOwner=${aboutOwner.ok ? "ok" : `unresolved:${aboutOwner.reason}`}`);
|
|
13803
|
-
return c.json({ kind: "match", userId, userName, aboutOwner });
|
|
13804
|
-
});
|
|
13805
|
-
var identity_default = app33;
|
|
13806
|
-
|
|
13807
13762
|
// server/routes/admin/browser.ts
|
|
13808
|
-
var
|
|
13809
|
-
|
|
13763
|
+
var app33 = new Hono();
|
|
13764
|
+
app33.post("/launch", requireAdminSession, async (c) => {
|
|
13810
13765
|
try {
|
|
13811
13766
|
const transport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
|
|
13812
13767
|
console.error(`[admin/browser/launch] op=request transport=${transport}`);
|
|
@@ -13834,44 +13789,43 @@ app34.post("/launch", requireAdminSession, async (c) => {
|
|
|
13834
13789
|
);
|
|
13835
13790
|
}
|
|
13836
13791
|
});
|
|
13837
|
-
var browser_default =
|
|
13792
|
+
var browser_default = app33;
|
|
13838
13793
|
|
|
13839
13794
|
// server/routes/admin/index.ts
|
|
13840
|
-
var
|
|
13841
|
-
|
|
13842
|
-
|
|
13843
|
-
|
|
13844
|
-
|
|
13845
|
-
|
|
13846
|
-
|
|
13847
|
-
|
|
13848
|
-
|
|
13849
|
-
|
|
13850
|
-
|
|
13851
|
-
|
|
13852
|
-
|
|
13853
|
-
|
|
13854
|
-
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
|
|
13868
|
-
|
|
13869
|
-
|
|
13870
|
-
var admin_default = app35;
|
|
13795
|
+
var app34 = new Hono();
|
|
13796
|
+
app34.route("/session", session_default);
|
|
13797
|
+
app34.route("/logs", logs_default);
|
|
13798
|
+
app34.route("/claude-info", claude_info_default);
|
|
13799
|
+
app34.route("/attachment", attachment_default);
|
|
13800
|
+
app34.route("/agents", agents_default);
|
|
13801
|
+
app34.route("/sessions", sessions_default);
|
|
13802
|
+
app34.route("/claude-sessions", claude_sessions_default);
|
|
13803
|
+
app34.route("/log-ingest", log_ingest_default);
|
|
13804
|
+
app34.route("/events", events_default);
|
|
13805
|
+
app34.route("/files", files_default);
|
|
13806
|
+
app34.route("/graph-search", graph_search_default);
|
|
13807
|
+
app34.route("/graph-subgraph", graph_subgraph_default);
|
|
13808
|
+
app34.route("/graph-delete", graph_delete_default);
|
|
13809
|
+
app34.route("/graph-restore", graph_restore_default);
|
|
13810
|
+
app34.route("/graph-labels-in-graph", graph_labels_in_graph_default);
|
|
13811
|
+
app34.route("/graph-default-view", graph_default_view_default);
|
|
13812
|
+
app34.route("/sidebar-artefacts", sidebar_artefacts_default);
|
|
13813
|
+
app34.route("/sidebar-sessions", sidebar_sessions_default);
|
|
13814
|
+
app34.route("/session-delete", session_delete_default);
|
|
13815
|
+
app34.route("/browser", browser_default);
|
|
13816
|
+
app34.route("/session-rc-spawn", session_rc_spawn_default);
|
|
13817
|
+
app34.route("/system-stats", system_stats_default);
|
|
13818
|
+
app34.route("/health-brand", health_default2);
|
|
13819
|
+
app34.route("/linkedin-ingest", linkedin_ingest_default);
|
|
13820
|
+
app34.route("/post-turn-context", post_turn_context_default);
|
|
13821
|
+
app34.route("/public-session-context", public_session_context_default);
|
|
13822
|
+
app34.route("/public-session-exit", public_session_exit_default);
|
|
13823
|
+
app34.route("/access-session-evict", access_session_evict_default);
|
|
13824
|
+
var admin_default = app34;
|
|
13871
13825
|
|
|
13872
13826
|
// app/lib/access-gate.ts
|
|
13873
13827
|
import neo4j4 from "neo4j-driver";
|
|
13874
|
-
import { readFileSync as
|
|
13828
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
13875
13829
|
import { resolve as resolve20 } from "path";
|
|
13876
13830
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
13877
13831
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
@@ -13880,7 +13834,7 @@ function readPassword() {
|
|
|
13880
13834
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
13881
13835
|
const passwordFile = resolve20(PLATFORM_ROOT7, "config/.neo4j-password");
|
|
13882
13836
|
try {
|
|
13883
|
-
return
|
|
13837
|
+
return readFileSync18(passwordFile, "utf-8").trim();
|
|
13884
13838
|
} catch {
|
|
13885
13839
|
throw new Error(
|
|
13886
13840
|
`Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
|
|
@@ -14078,11 +14032,11 @@ async function generateNewMagicToken(grantId) {
|
|
|
14078
14032
|
}
|
|
14079
14033
|
|
|
14080
14034
|
// server/routes/access/verify-token.ts
|
|
14081
|
-
var
|
|
14035
|
+
var TAG28 = "[access-verify]";
|
|
14082
14036
|
var MINT_TAG = "[access-session-mint]";
|
|
14083
14037
|
var COOKIE_NAME = "__access_session";
|
|
14084
|
-
var
|
|
14085
|
-
|
|
14038
|
+
var app35 = new Hono();
|
|
14039
|
+
app35.post("/", async (c) => {
|
|
14086
14040
|
const ip = c.var.clientIp || "unknown";
|
|
14087
14041
|
let body;
|
|
14088
14042
|
try {
|
|
@@ -14097,39 +14051,39 @@ app36.post("/", async (c) => {
|
|
|
14097
14051
|
}
|
|
14098
14052
|
const rateMsg = checkAccessRateLimit(ip, agentSlug);
|
|
14099
14053
|
if (rateMsg) {
|
|
14100
|
-
console.error(`${
|
|
14054
|
+
console.error(`${TAG28} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
|
|
14101
14055
|
return c.json({ error: rateMsg }, 429);
|
|
14102
14056
|
}
|
|
14103
14057
|
const grant = await findGrantByMagicToken(token);
|
|
14104
14058
|
if (!grant) {
|
|
14105
14059
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
14106
|
-
console.error(`${
|
|
14060
|
+
console.error(`${TAG28} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
|
|
14107
14061
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
14108
14062
|
}
|
|
14109
14063
|
if (grant.agentSlug !== agentSlug) {
|
|
14110
14064
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
14111
14065
|
console.error(
|
|
14112
|
-
`${
|
|
14066
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
|
|
14113
14067
|
);
|
|
14114
14068
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
14115
14069
|
}
|
|
14116
14070
|
if (grant.status === "expired" || grant.status === "revoked") {
|
|
14117
14071
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
14118
14072
|
console.error(
|
|
14119
|
-
`${
|
|
14073
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
|
|
14120
14074
|
);
|
|
14121
14075
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
14122
14076
|
}
|
|
14123
14077
|
if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
|
|
14124
14078
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
14125
14079
|
console.error(
|
|
14126
|
-
`${
|
|
14080
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
|
|
14127
14081
|
);
|
|
14128
14082
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
14129
14083
|
}
|
|
14130
14084
|
if (!grant.sliceToken) {
|
|
14131
14085
|
console.error(
|
|
14132
|
-
`${
|
|
14086
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
|
|
14133
14087
|
);
|
|
14134
14088
|
return c.json({ error: "grant-misconfigured" }, 500);
|
|
14135
14089
|
}
|
|
@@ -14145,12 +14099,12 @@ app36.post("/", async (c) => {
|
|
|
14145
14099
|
await consumeMagicTokenAndActivate(grant.grantId);
|
|
14146
14100
|
} catch (err) {
|
|
14147
14101
|
console.error(
|
|
14148
|
-
`${
|
|
14102
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
14149
14103
|
);
|
|
14150
14104
|
return c.json({ error: "verification-failed" }, 500);
|
|
14151
14105
|
}
|
|
14152
14106
|
clearAccessRateLimit(ip, agentSlug);
|
|
14153
|
-
console.log(`${
|
|
14107
|
+
console.log(`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
|
|
14154
14108
|
console.log(
|
|
14155
14109
|
`${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
|
|
14156
14110
|
);
|
|
@@ -14164,7 +14118,7 @@ app36.post("/", async (c) => {
|
|
|
14164
14118
|
displayName: grant.displayName
|
|
14165
14119
|
});
|
|
14166
14120
|
});
|
|
14167
|
-
var verify_token_default =
|
|
14121
|
+
var verify_token_default = app35;
|
|
14168
14122
|
|
|
14169
14123
|
// app/lib/access-email.ts
|
|
14170
14124
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -14226,10 +14180,10 @@ async function sendMagicLinkEmail(payload) {
|
|
|
14226
14180
|
}
|
|
14227
14181
|
|
|
14228
14182
|
// server/routes/access/request-magic-link.ts
|
|
14229
|
-
var
|
|
14230
|
-
var
|
|
14183
|
+
var TAG29 = "[access-request-link]";
|
|
14184
|
+
var app36 = new Hono();
|
|
14231
14185
|
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
14232
|
-
|
|
14186
|
+
app36.post("/", async (c) => {
|
|
14233
14187
|
let body;
|
|
14234
14188
|
try {
|
|
14235
14189
|
body = await c.req.json();
|
|
@@ -14243,18 +14197,18 @@ app37.post("/", async (c) => {
|
|
|
14243
14197
|
}
|
|
14244
14198
|
const rateMsg = checkRequestLinkRateLimit(contactValue);
|
|
14245
14199
|
if (rateMsg) {
|
|
14246
|
-
console.error(`${
|
|
14200
|
+
console.error(`${TAG29} contactValue=${maskContact(contactValue)} result=rate-limited`);
|
|
14247
14201
|
return c.json({ error: rateMsg }, 429);
|
|
14248
14202
|
}
|
|
14249
14203
|
recordRequestLinkAttempt(contactValue);
|
|
14250
14204
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
14251
14205
|
if (!accountId) {
|
|
14252
|
-
console.error(`${
|
|
14206
|
+
console.error(`${TAG29} contactValue=${maskContact(contactValue)} result=no-account-id`);
|
|
14253
14207
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
14254
14208
|
}
|
|
14255
14209
|
const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
|
|
14256
14210
|
if (!grant) {
|
|
14257
|
-
console.log(`${
|
|
14211
|
+
console.log(`${TAG29} contactValue=${maskContact(contactValue)} result=notfound`);
|
|
14258
14212
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
14259
14213
|
}
|
|
14260
14214
|
let token;
|
|
@@ -14262,7 +14216,7 @@ app37.post("/", async (c) => {
|
|
|
14262
14216
|
token = await generateNewMagicToken(grant.grantId);
|
|
14263
14217
|
} catch (err) {
|
|
14264
14218
|
console.error(
|
|
14265
|
-
`${
|
|
14219
|
+
`${TAG29} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
14266
14220
|
);
|
|
14267
14221
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
14268
14222
|
}
|
|
@@ -14294,25 +14248,25 @@ app37.post("/", async (c) => {
|
|
|
14294
14248
|
});
|
|
14295
14249
|
if (!sendResult.ok) {
|
|
14296
14250
|
console.error(
|
|
14297
|
-
`${
|
|
14251
|
+
`${TAG29} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
|
|
14298
14252
|
);
|
|
14299
14253
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
14300
14254
|
}
|
|
14301
14255
|
console.log(
|
|
14302
|
-
`${
|
|
14256
|
+
`${TAG29} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
|
|
14303
14257
|
);
|
|
14304
14258
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
14305
14259
|
});
|
|
14306
|
-
var request_magic_link_default =
|
|
14260
|
+
var request_magic_link_default = app36;
|
|
14307
14261
|
|
|
14308
14262
|
// server/routes/access/index.ts
|
|
14309
|
-
var
|
|
14310
|
-
|
|
14311
|
-
|
|
14312
|
-
var access_default =
|
|
14263
|
+
var app37 = new Hono();
|
|
14264
|
+
app37.route("/verify-token", verify_token_default);
|
|
14265
|
+
app37.route("/request-magic-link", request_magic_link_default);
|
|
14266
|
+
var access_default = app37;
|
|
14313
14267
|
|
|
14314
14268
|
// server/routes/sites.ts
|
|
14315
|
-
import { existsSync as
|
|
14269
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
|
|
14316
14270
|
import { resolve as resolve22 } from "path";
|
|
14317
14271
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
14318
14272
|
var MIME = {
|
|
@@ -14344,8 +14298,8 @@ function getExt(p) {
|
|
|
14344
14298
|
if (idx < p.lastIndexOf("/")) return "";
|
|
14345
14299
|
return p.slice(idx).toLowerCase();
|
|
14346
14300
|
}
|
|
14347
|
-
var
|
|
14348
|
-
|
|
14301
|
+
var app38 = new Hono();
|
|
14302
|
+
app38.get("/:rel{.*}", (c) => {
|
|
14349
14303
|
const reqPath = c.req.path;
|
|
14350
14304
|
const rawRel = c.req.param("rel") ?? "";
|
|
14351
14305
|
const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
@@ -14378,7 +14332,7 @@ app39.get("/:rel{.*}", (c) => {
|
|
|
14378
14332
|
}
|
|
14379
14333
|
let stat7;
|
|
14380
14334
|
try {
|
|
14381
|
-
stat7 =
|
|
14335
|
+
stat7 = existsSync20(filePath) ? statSync8(filePath) : null;
|
|
14382
14336
|
} catch {
|
|
14383
14337
|
stat7 = null;
|
|
14384
14338
|
}
|
|
@@ -14397,7 +14351,7 @@ app39.get("/:rel{.*}", (c) => {
|
|
|
14397
14351
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
14398
14352
|
return c.text("Forbidden", 403);
|
|
14399
14353
|
}
|
|
14400
|
-
if (!
|
|
14354
|
+
if (!existsSync20(filePath)) {
|
|
14401
14355
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
14402
14356
|
return c.text("Not found", 404);
|
|
14403
14357
|
}
|
|
@@ -14416,7 +14370,7 @@ app39.get("/:rel{.*}", (c) => {
|
|
|
14416
14370
|
}
|
|
14417
14371
|
let body;
|
|
14418
14372
|
try {
|
|
14419
|
-
body =
|
|
14373
|
+
body = readFileSync19(realPath);
|
|
14420
14374
|
} catch (err) {
|
|
14421
14375
|
const code = err?.code;
|
|
14422
14376
|
if (code === "EISDIR") {
|
|
@@ -14448,11 +14402,11 @@ app39.get("/:rel{.*}", (c) => {
|
|
|
14448
14402
|
"X-Content-Type-Options": "nosniff"
|
|
14449
14403
|
});
|
|
14450
14404
|
});
|
|
14451
|
-
var sites_default =
|
|
14405
|
+
var sites_default = app38;
|
|
14452
14406
|
|
|
14453
14407
|
// app/lib/visitor-token.ts
|
|
14454
14408
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
14455
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
14409
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync20, writeFileSync as writeFileSync7 } from "fs";
|
|
14456
14410
|
import { dirname as dirname4 } from "path";
|
|
14457
14411
|
var TOKEN_PREFIX = "v1.";
|
|
14458
14412
|
var SECRET_BYTES = 32;
|
|
@@ -14461,7 +14415,7 @@ var cachedSecret = null;
|
|
|
14461
14415
|
function getSecret() {
|
|
14462
14416
|
if (cachedSecret) return cachedSecret;
|
|
14463
14417
|
try {
|
|
14464
|
-
const hex2 =
|
|
14418
|
+
const hex2 = readFileSync20(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
14465
14419
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
14466
14420
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
14467
14421
|
return cachedSecret;
|
|
@@ -14471,11 +14425,11 @@ function getSecret() {
|
|
|
14471
14425
|
const fresh = randomBytes(SECRET_BYTES).toString("hex");
|
|
14472
14426
|
try {
|
|
14473
14427
|
mkdirSync4(dirname4(VISITOR_TOKEN_SECRET_FILE), { recursive: true, mode: 448 });
|
|
14474
|
-
|
|
14428
|
+
writeFileSync7(VISITOR_TOKEN_SECRET_FILE, fresh, { mode: 384, flag: "wx" });
|
|
14475
14429
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
14476
14430
|
} catch {
|
|
14477
14431
|
}
|
|
14478
|
-
const hex =
|
|
14432
|
+
const hex = readFileSync20(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
14479
14433
|
cachedSecret = Buffer.from(hex, "hex");
|
|
14480
14434
|
return cachedSecret;
|
|
14481
14435
|
}
|
|
@@ -14528,7 +14482,7 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
14528
14482
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
14529
14483
|
|
|
14530
14484
|
// app/lib/brand-config.ts
|
|
14531
|
-
import { existsSync as
|
|
14485
|
+
import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
|
|
14532
14486
|
import { join as join17 } from "path";
|
|
14533
14487
|
var cached2 = null;
|
|
14534
14488
|
var cachedAttempted = false;
|
|
@@ -14538,9 +14492,9 @@ function readBrandConfig() {
|
|
|
14538
14492
|
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
|
|
14539
14493
|
if (!platformRoot2) return null;
|
|
14540
14494
|
const brandPath = join17(platformRoot2, "config", "brand.json");
|
|
14541
|
-
if (!
|
|
14495
|
+
if (!existsSync21(brandPath)) return null;
|
|
14542
14496
|
try {
|
|
14543
|
-
cached2 = JSON.parse(
|
|
14497
|
+
cached2 = JSON.parse(readFileSync21(brandPath, "utf-8"));
|
|
14544
14498
|
return cached2;
|
|
14545
14499
|
} catch {
|
|
14546
14500
|
return null;
|
|
@@ -14548,7 +14502,7 @@ function readBrandConfig() {
|
|
|
14548
14502
|
}
|
|
14549
14503
|
|
|
14550
14504
|
// server/routes/visitor-consent.ts
|
|
14551
|
-
var
|
|
14505
|
+
var app39 = new Hono();
|
|
14552
14506
|
var CONSENT_COOKIE_NAME = "mxy_consent";
|
|
14553
14507
|
var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
|
|
14554
14508
|
var DEFAULT_CONSENT_COPY = {
|
|
@@ -14593,17 +14547,17 @@ function siteSlugFromReferer(referer) {
|
|
|
14593
14547
|
return "";
|
|
14594
14548
|
}
|
|
14595
14549
|
}
|
|
14596
|
-
|
|
14550
|
+
app39.options("/consent", (c) => {
|
|
14597
14551
|
const origin = getOrigin(c);
|
|
14598
14552
|
setCorsHeaders(c, origin);
|
|
14599
14553
|
return c.body(null, 204);
|
|
14600
14554
|
});
|
|
14601
|
-
|
|
14555
|
+
app39.options("/brand-config", (c) => {
|
|
14602
14556
|
const origin = getOrigin(c);
|
|
14603
14557
|
setCorsHeaders(c, origin);
|
|
14604
14558
|
return c.body(null, 204);
|
|
14605
14559
|
});
|
|
14606
|
-
|
|
14560
|
+
app39.post("/consent", async (c) => {
|
|
14607
14561
|
const origin = getOrigin(c);
|
|
14608
14562
|
setCorsHeaders(c, origin);
|
|
14609
14563
|
let raw;
|
|
@@ -14643,7 +14597,7 @@ app40.post("/consent", async (c) => {
|
|
|
14643
14597
|
console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
|
|
14644
14598
|
return c.body(null, 204);
|
|
14645
14599
|
});
|
|
14646
|
-
|
|
14600
|
+
app39.get("/brand-config", (c) => {
|
|
14647
14601
|
const origin = getOrigin(c);
|
|
14648
14602
|
setCorsHeaders(c, origin);
|
|
14649
14603
|
const brand = readBrandConfig();
|
|
@@ -14653,7 +14607,7 @@ app40.get("/brand-config", (c) => {
|
|
|
14653
14607
|
c.header("Cache-Control", "public, max-age=300");
|
|
14654
14608
|
return c.json({ consent: { copy, palette } });
|
|
14655
14609
|
});
|
|
14656
|
-
var visitor_consent_default =
|
|
14610
|
+
var visitor_consent_default = app39;
|
|
14657
14611
|
|
|
14658
14612
|
// server/routes/listings.ts
|
|
14659
14613
|
function getCookie(headerValue, name) {
|
|
@@ -14680,8 +14634,8 @@ function appendConsentParams(pageUrl, token) {
|
|
|
14680
14634
|
}
|
|
14681
14635
|
var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
|
|
14682
14636
|
var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
|
|
14683
|
-
var
|
|
14684
|
-
|
|
14637
|
+
var app40 = new Hono();
|
|
14638
|
+
app40.get("/:slug/click", async (c) => {
|
|
14685
14639
|
const slug = c.req.param("slug") ?? "";
|
|
14686
14640
|
const rawSession = c.req.query("session") ?? "";
|
|
14687
14641
|
const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
|
|
@@ -14745,10 +14699,10 @@ app41.get("/:slug/click", async (c) => {
|
|
|
14745
14699
|
console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
14746
14700
|
return c.redirect(redirectUrl, 302);
|
|
14747
14701
|
});
|
|
14748
|
-
var listings_default =
|
|
14702
|
+
var listings_default = app40;
|
|
14749
14703
|
|
|
14750
14704
|
// server/routes/visitor-event.ts
|
|
14751
|
-
var
|
|
14705
|
+
var app41 = new Hono();
|
|
14752
14706
|
var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
|
|
14753
14707
|
var buckets = /* @__PURE__ */ new Map();
|
|
14754
14708
|
var RATE_LIMIT = 60;
|
|
@@ -14815,12 +14769,12 @@ function originAllowed(origin, allowlist) {
|
|
|
14815
14769
|
return false;
|
|
14816
14770
|
}
|
|
14817
14771
|
}
|
|
14818
|
-
|
|
14772
|
+
app41.options("/event", (c) => {
|
|
14819
14773
|
const origin = getOrigin2(c);
|
|
14820
14774
|
setCorsHeaders2(c, origin);
|
|
14821
14775
|
return c.body(null, 204);
|
|
14822
14776
|
});
|
|
14823
|
-
|
|
14777
|
+
app41.post("/event", async (c) => {
|
|
14824
14778
|
const origin = getOrigin2(c);
|
|
14825
14779
|
setCorsHeaders2(c, origin);
|
|
14826
14780
|
const ua = c.req.header("user-agent") ?? "";
|
|
@@ -15025,17 +14979,17 @@ async function writeEvent(opts) {
|
|
|
15025
14979
|
);
|
|
15026
14980
|
}
|
|
15027
14981
|
}
|
|
15028
|
-
var visitor_event_default =
|
|
14982
|
+
var visitor_event_default = app41;
|
|
15029
14983
|
|
|
15030
14984
|
// server/routes/session.ts
|
|
15031
14985
|
import { resolve as resolve23 } from "path";
|
|
15032
|
-
import { existsSync as
|
|
14986
|
+
import { existsSync as existsSync22, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
15033
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;
|
|
15034
14988
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
15035
14989
|
try {
|
|
15036
14990
|
const cacheDir = resolve23(MAXY_DIR, "branding-cache", accountId);
|
|
15037
14991
|
mkdirSync5(cacheDir, { recursive: true });
|
|
15038
|
-
|
|
14992
|
+
writeFileSync8(resolve23(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
15039
14993
|
} catch (err) {
|
|
15040
14994
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
15041
14995
|
}
|
|
@@ -15071,8 +15025,8 @@ function withVisitorCookie(response, visitorId) {
|
|
|
15071
15025
|
headers
|
|
15072
15026
|
});
|
|
15073
15027
|
}
|
|
15074
|
-
var
|
|
15075
|
-
|
|
15028
|
+
var app42 = new Hono();
|
|
15029
|
+
app42.post("/", async (c) => {
|
|
15076
15030
|
let body;
|
|
15077
15031
|
try {
|
|
15078
15032
|
body = await c.req.json();
|
|
@@ -15124,7 +15078,7 @@ app43.post("/", async (c) => {
|
|
|
15124
15078
|
let agentConfig = null;
|
|
15125
15079
|
if (account) {
|
|
15126
15080
|
const agentDir = resolve23(account.accountDir, "agents", agentSlug);
|
|
15127
|
-
if (!
|
|
15081
|
+
if (!existsSync22(agentDir) || !existsSync22(resolve23(agentDir, "config.json"))) {
|
|
15128
15082
|
return c.json({ error: "Agent not found" }, 404);
|
|
15129
15083
|
}
|
|
15130
15084
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -15283,10 +15237,10 @@ app43.post("/", async (c) => {
|
|
|
15283
15237
|
newVisitorId
|
|
15284
15238
|
);
|
|
15285
15239
|
});
|
|
15286
|
-
var session_default2 =
|
|
15240
|
+
var session_default2 = app42;
|
|
15287
15241
|
|
|
15288
15242
|
// app/lib/graph-health.ts
|
|
15289
|
-
var
|
|
15243
|
+
var import_dist4 = __toESM(require_dist3(), 1);
|
|
15290
15244
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
15291
15245
|
function renderLabelTop(rows) {
|
|
15292
15246
|
return rows.map((b) => {
|
|
@@ -15331,7 +15285,7 @@ async function runGraphHealthTick() {
|
|
|
15331
15285
|
console.error(
|
|
15332
15286
|
`[graph-health] userprofile-multi accounts=${upAccounts} top=${upTop.length > 0 ? upTop.join(",") : "none"}`
|
|
15333
15287
|
);
|
|
15334
|
-
const indexed = [...
|
|
15288
|
+
const indexed = [...import_dist4.VECTOR_INDEXED_LABELS];
|
|
15335
15289
|
const embCount = await session.run(
|
|
15336
15290
|
`MATCH (n) WHERE n.embedding IS NULL AND any(l IN labels(n) WHERE l IN $indexed)
|
|
15337
15291
|
RETURN count(n) AS total`,
|
|
@@ -15516,44 +15470,115 @@ var InboundHub = class {
|
|
|
15516
15470
|
state(senderId) {
|
|
15517
15471
|
let s = this.senders.get(senderId);
|
|
15518
15472
|
if (!s) {
|
|
15519
|
-
s = { subscriber: null,
|
|
15473
|
+
s = { subscriber: null, ready: false, inFlight: [] };
|
|
15520
15474
|
this.senders.set(senderId, s);
|
|
15521
15475
|
}
|
|
15522
15476
|
return s;
|
|
15523
15477
|
}
|
|
15524
|
-
/** Route an inbound message
|
|
15525
|
-
*
|
|
15526
|
-
|
|
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) {
|
|
15527
15484
|
const s = this.state(payload.senderId);
|
|
15528
|
-
if (s.subscriber) {
|
|
15485
|
+
if (s.subscriber && s.ready) {
|
|
15529
15486
|
s.subscriber(payload);
|
|
15487
|
+
s.inFlight.push({ payload, state: "drained", enqueuedAt: now, drainedAt: now });
|
|
15530
15488
|
} else {
|
|
15531
|
-
s.
|
|
15489
|
+
s.inFlight.push({ payload, state: "queued", enqueuedAt: now, drainedAt: null });
|
|
15532
15490
|
}
|
|
15533
15491
|
}
|
|
15534
|
-
/** A sender's channel server connected.
|
|
15535
|
-
*
|
|
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. */
|
|
15536
15495
|
attach(senderId, subscriber) {
|
|
15537
15496
|
const s = this.state(senderId);
|
|
15538
15497
|
s.subscriber = subscriber;
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
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
|
+
}
|
|
15543
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;
|
|
15544
15528
|
}
|
|
15545
15529
|
/** A sender's channel server disconnected (session ended/restarting).
|
|
15546
|
-
* Subsequent messages queue again until re-attach, so nothing is lost
|
|
15547
|
-
* 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. */
|
|
15548
15532
|
detach(senderId) {
|
|
15549
15533
|
const s = this.senders.get(senderId);
|
|
15550
|
-
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;
|
|
15551
15570
|
}
|
|
15552
|
-
/** Whether a sender currently has a live channel server. The gateway
|
|
15553
|
-
* 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. */
|
|
15554
15573
|
hasSubscriber(senderId) {
|
|
15555
15574
|
return this.senders.get(senderId)?.subscriber != null;
|
|
15556
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
|
+
}
|
|
15557
15582
|
};
|
|
15558
15583
|
|
|
15559
15584
|
// node_modules/hono/dist/utils/stream.js
|
|
@@ -15707,8 +15732,8 @@ var streamSSE = (c, cb, onError) => {
|
|
|
15707
15732
|
|
|
15708
15733
|
// app/lib/whatsapp/gateway/routes.ts
|
|
15709
15734
|
function createWaChannelRoutes(deps) {
|
|
15710
|
-
const
|
|
15711
|
-
|
|
15735
|
+
const app44 = new Hono();
|
|
15736
|
+
app44.get("/wa-channel/inbound", (c) => {
|
|
15712
15737
|
const senderId = c.req.query("senderId");
|
|
15713
15738
|
if (!senderId) return c.json({ error: "senderId required" }, 400);
|
|
15714
15739
|
return streamSSE(c, async (stream2) => {
|
|
@@ -15726,7 +15751,7 @@ function createWaChannelRoutes(deps) {
|
|
|
15726
15751
|
}
|
|
15727
15752
|
});
|
|
15728
15753
|
});
|
|
15729
|
-
|
|
15754
|
+
app44.post("/wa-channel/reply", async (c) => {
|
|
15730
15755
|
const body = await c.req.json().catch(() => null);
|
|
15731
15756
|
const senderId = body?.senderId;
|
|
15732
15757
|
const text = body?.text;
|
|
@@ -15745,7 +15770,7 @@ function createWaChannelRoutes(deps) {
|
|
|
15745
15770
|
console.error(`[whatsapp-native] op=reply-dispatch senderId=${senderId} bytes=${bytes}`);
|
|
15746
15771
|
return c.json({ ok: true });
|
|
15747
15772
|
});
|
|
15748
|
-
|
|
15773
|
+
app44.post("/wa-channel/ready", async (c) => {
|
|
15749
15774
|
const body = await c.req.json().catch(() => null);
|
|
15750
15775
|
const senderId = body?.senderId;
|
|
15751
15776
|
if (typeof senderId !== "string") {
|
|
@@ -15754,10 +15779,23 @@ function createWaChannelRoutes(deps) {
|
|
|
15754
15779
|
deps.onReady?.(senderId);
|
|
15755
15780
|
return c.json({ ok: true });
|
|
15756
15781
|
});
|
|
15757
|
-
|
|
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
|
+
});
|
|
15792
|
+
return app44;
|
|
15758
15793
|
}
|
|
15759
15794
|
|
|
15760
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;
|
|
15761
15799
|
var WaGateway = class {
|
|
15762
15800
|
constructor(deps) {
|
|
15763
15801
|
this.deps = deps;
|
|
@@ -15770,17 +15808,55 @@ var WaGateway = class {
|
|
|
15770
15808
|
routes() {
|
|
15771
15809
|
return createWaChannelRoutes({
|
|
15772
15810
|
hub: this.hub,
|
|
15773
|
-
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
|
+
}
|
|
15774
15822
|
});
|
|
15775
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
|
+
}
|
|
15776
15848
|
/** Handle one inbound admin WhatsApp message. */
|
|
15777
15849
|
async handleInbound(input) {
|
|
15778
15850
|
const bytes = Buffer.byteLength(input.text, "utf8");
|
|
15779
15851
|
const hadSubscriber = this.hub.hasSubscriber(input.senderId);
|
|
15852
|
+
const willDeliverNow = this.hub.isReady(input.senderId);
|
|
15780
15853
|
this.replies.set(input.senderId, input.reply);
|
|
15781
|
-
this.hub.deliver(
|
|
15854
|
+
this.hub.deliver(
|
|
15855
|
+
{ senderId: input.senderId, text: input.text, waMessageId: `wa-${++this.seq}` },
|
|
15856
|
+
Date.now()
|
|
15857
|
+
);
|
|
15782
15858
|
console.error(
|
|
15783
|
-
`[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"}`
|
|
15784
15860
|
);
|
|
15785
15861
|
if (!hadSubscriber && !this.spawning.has(input.senderId)) {
|
|
15786
15862
|
this.spawning.add(input.senderId);
|
|
@@ -15804,6 +15880,71 @@ var WaGateway = class {
|
|
|
15804
15880
|
}
|
|
15805
15881
|
};
|
|
15806
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
|
+
|
|
15807
15948
|
// app/lib/admin-sse-registry.ts
|
|
15808
15949
|
var activeAdminSSEControllers = /* @__PURE__ */ new Set();
|
|
15809
15950
|
function broadcastAdminShutdown(reason) {
|
|
@@ -15834,7 +15975,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
15834
15975
|
|
|
15835
15976
|
// ../lib/entitlement/src/index.ts
|
|
15836
15977
|
import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
|
|
15837
|
-
import { existsSync as
|
|
15978
|
+
import { existsSync as existsSync23, readFileSync as readFileSync22, statSync as statSync9 } from "fs";
|
|
15838
15979
|
import { resolve as resolve25 } from "path";
|
|
15839
15980
|
|
|
15840
15981
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -15883,7 +16024,7 @@ function resolveEntitlement(brand, account) {
|
|
|
15883
16024
|
return logResolved(implicitTrust(account), null);
|
|
15884
16025
|
}
|
|
15885
16026
|
const entitlementPath = resolve25(brand.configDir, "entitlement.json");
|
|
15886
|
-
if (!
|
|
16027
|
+
if (!existsSync23(entitlementPath)) {
|
|
15887
16028
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
15888
16029
|
}
|
|
15889
16030
|
const stat7 = statSync9(entitlementPath);
|
|
@@ -15898,7 +16039,7 @@ function resolveEntitlement(brand, account) {
|
|
|
15898
16039
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
15899
16040
|
let pubkeyPem;
|
|
15900
16041
|
try {
|
|
15901
|
-
pubkeyPem =
|
|
16042
|
+
pubkeyPem = readFileSync22(pubkeyPath(brand), "utf-8");
|
|
15902
16043
|
} catch (err) {
|
|
15903
16044
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
15904
16045
|
reason: "pubkey-missing"
|
|
@@ -15912,7 +16053,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
15912
16053
|
}
|
|
15913
16054
|
let envelope;
|
|
15914
16055
|
try {
|
|
15915
|
-
envelope = JSON.parse(
|
|
16056
|
+
envelope = JSON.parse(readFileSync22(entitlementPath, "utf-8"));
|
|
15916
16057
|
} catch {
|
|
15917
16058
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
15918
16059
|
}
|
|
@@ -16075,12 +16216,12 @@ function clientFrom(c) {
|
|
|
16075
16216
|
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
16076
16217
|
var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join18(PLATFORM_ROOT9, "config", "brand.json") : "";
|
|
16077
16218
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
16078
|
-
if (BRAND_JSON_PATH && !
|
|
16219
|
+
if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
|
|
16079
16220
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
16080
16221
|
}
|
|
16081
|
-
if (BRAND_JSON_PATH &&
|
|
16222
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
16082
16223
|
try {
|
|
16083
|
-
const parsed = JSON.parse(
|
|
16224
|
+
const parsed = JSON.parse(readFileSync23(BRAND_JSON_PATH, "utf-8"));
|
|
16084
16225
|
BRAND = { ...BRAND, ...parsed };
|
|
16085
16226
|
} catch (err) {
|
|
16086
16227
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -16102,8 +16243,8 @@ var brandLoginOpts = {
|
|
|
16102
16243
|
var ALIAS_DOMAINS_PATH = join18(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
16103
16244
|
function loadAliasDomains() {
|
|
16104
16245
|
try {
|
|
16105
|
-
if (!
|
|
16106
|
-
const parsed = JSON.parse(
|
|
16246
|
+
if (!existsSync24(ALIAS_DOMAINS_PATH)) return null;
|
|
16247
|
+
const parsed = JSON.parse(readFileSync23(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
16107
16248
|
if (!Array.isArray(parsed)) {
|
|
16108
16249
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
16109
16250
|
return null;
|
|
@@ -16127,13 +16268,16 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
16127
16268
|
function isPublicHost(host) {
|
|
16128
16269
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
16129
16270
|
}
|
|
16130
|
-
var
|
|
16271
|
+
var app43 = new Hono();
|
|
16131
16272
|
var waGateway = new WaGateway({
|
|
16132
16273
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
16133
16274
|
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve26(process.env.MAXY_PLATFORM_ROOT ?? join18(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
16134
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"}`);
|
|
16135
16278
|
const result = await managerRcSpawn({
|
|
16136
16279
|
sessionId: adminSessionIdFor(accountId, senderId),
|
|
16280
|
+
userId,
|
|
16137
16281
|
waChannel: { senderId, gatewayUrl, serverPath },
|
|
16138
16282
|
logContext: `channel=whatsapp-native senderId=${senderId}`
|
|
16139
16283
|
});
|
|
@@ -16142,9 +16286,10 @@ var waGateway = new WaGateway({
|
|
|
16142
16286
|
}
|
|
16143
16287
|
}
|
|
16144
16288
|
});
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16289
|
+
app43.route("/", waGateway.routes());
|
|
16290
|
+
waGateway.startSweeper();
|
|
16291
|
+
app43.use("*", clientIpMiddleware);
|
|
16292
|
+
app43.use("*", async (c, next) => {
|
|
16148
16293
|
await next();
|
|
16149
16294
|
c.header("X-Content-Type-Options", "nosniff");
|
|
16150
16295
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
@@ -16154,7 +16299,7 @@ app44.use("*", async (c, next) => {
|
|
|
16154
16299
|
);
|
|
16155
16300
|
});
|
|
16156
16301
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
16157
|
-
|
|
16302
|
+
app43.use("*", async (c, next) => {
|
|
16158
16303
|
if (!HTTP_LOG_PATHS.has(c.req.path)) {
|
|
16159
16304
|
await next();
|
|
16160
16305
|
return;
|
|
@@ -16172,7 +16317,7 @@ app44.use("*", async (c, next) => {
|
|
|
16172
16317
|
});
|
|
16173
16318
|
}
|
|
16174
16319
|
});
|
|
16175
|
-
|
|
16320
|
+
app43.use("*", async (c, next) => {
|
|
16176
16321
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
16177
16322
|
if (!isPublicHost(host)) {
|
|
16178
16323
|
await next();
|
|
@@ -16203,7 +16348,7 @@ function resolveRemoteAuthOpts() {
|
|
|
16203
16348
|
return brandLoginOpts;
|
|
16204
16349
|
}
|
|
16205
16350
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
16206
|
-
|
|
16351
|
+
app43.post("/__remote-auth/login", async (c) => {
|
|
16207
16352
|
const client = clientFrom(c);
|
|
16208
16353
|
const clientIp = client.ip || "unknown";
|
|
16209
16354
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -16248,7 +16393,7 @@ app44.post("/__remote-auth/login", async (c) => {
|
|
|
16248
16393
|
}
|
|
16249
16394
|
});
|
|
16250
16395
|
});
|
|
16251
|
-
|
|
16396
|
+
app43.get("/__remote-auth/logout", (c) => {
|
|
16252
16397
|
const client = clientFrom(c);
|
|
16253
16398
|
const clientIp = client.ip || "unknown";
|
|
16254
16399
|
console.error(`[remote-auth] logout ip=${clientIp}`);
|
|
@@ -16261,7 +16406,7 @@ app44.get("/__remote-auth/logout", (c) => {
|
|
|
16261
16406
|
}
|
|
16262
16407
|
});
|
|
16263
16408
|
});
|
|
16264
|
-
|
|
16409
|
+
app43.post("/__remote-auth/change-password", async (c) => {
|
|
16265
16410
|
const client = clientFrom(c);
|
|
16266
16411
|
const clientIp = client.ip || "unknown";
|
|
16267
16412
|
const rateLimited = checkRateLimit(client);
|
|
@@ -16312,13 +16457,13 @@ app44.post("/__remote-auth/change-password", async (c) => {
|
|
|
16312
16457
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
16313
16458
|
}
|
|
16314
16459
|
});
|
|
16315
|
-
|
|
16460
|
+
app43.get("/__remote-auth/setup", (c) => {
|
|
16316
16461
|
if (isRemoteAuthConfigured()) {
|
|
16317
16462
|
return c.redirect("/");
|
|
16318
16463
|
}
|
|
16319
16464
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
|
|
16320
16465
|
});
|
|
16321
|
-
|
|
16466
|
+
app43.post("/__remote-auth/set-initial-password", async (c) => {
|
|
16322
16467
|
if (isRemoteAuthConfigured()) {
|
|
16323
16468
|
return c.redirect("/");
|
|
16324
16469
|
}
|
|
@@ -16356,10 +16501,10 @@ app44.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
16356
16501
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
16357
16502
|
}
|
|
16358
16503
|
});
|
|
16359
|
-
|
|
16504
|
+
app43.get("/api/remote-auth/status", (c) => {
|
|
16360
16505
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
16361
16506
|
});
|
|
16362
|
-
|
|
16507
|
+
app43.post("/api/remote-auth/set-password", async (c) => {
|
|
16363
16508
|
let body;
|
|
16364
16509
|
try {
|
|
16365
16510
|
body = await c.req.json();
|
|
@@ -16390,9 +16535,9 @@ app44.post("/api/remote-auth/set-password", async (c) => {
|
|
|
16390
16535
|
return c.json({ error: "Failed to save password" }, 500);
|
|
16391
16536
|
}
|
|
16392
16537
|
});
|
|
16393
|
-
|
|
16538
|
+
app43.route("/api/_client-error", client_error_default);
|
|
16394
16539
|
console.log("[client-error-route] mounted");
|
|
16395
|
-
|
|
16540
|
+
app43.use("*", async (c, next) => {
|
|
16396
16541
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
16397
16542
|
const path2 = c.req.path;
|
|
16398
16543
|
if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
|
|
@@ -16425,13 +16570,13 @@ app44.use("*", async (c, next) => {
|
|
|
16425
16570
|
console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
|
|
16426
16571
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
|
|
16427
16572
|
});
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
|
|
16573
|
+
app43.route("/api/health", health_default);
|
|
16574
|
+
app43.route("/api/chat", chat_default);
|
|
16575
|
+
app43.route("/api/whatsapp", whatsapp_default);
|
|
16576
|
+
app43.route("/api/onboarding", onboarding_default);
|
|
16577
|
+
app43.route("/api/admin", admin_default);
|
|
16578
|
+
app43.route("/api/access", access_default);
|
|
16579
|
+
app43.route("/api/session", session_default2);
|
|
16435
16580
|
var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
|
|
16436
16581
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
16437
16582
|
var IMAGE_MIME = {
|
|
@@ -16443,7 +16588,7 @@ var IMAGE_MIME = {
|
|
|
16443
16588
|
".svg": "image/svg+xml",
|
|
16444
16589
|
".ico": "image/x-icon"
|
|
16445
16590
|
};
|
|
16446
|
-
|
|
16591
|
+
app43.get("/agent-assets/:slug/:filename", (c) => {
|
|
16447
16592
|
const slug = c.req.param("slug");
|
|
16448
16593
|
const filename = c.req.param("filename");
|
|
16449
16594
|
if (!SAFE_SLUG_RE2.test(slug)) {
|
|
@@ -16465,20 +16610,20 @@ app44.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
16465
16610
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
16466
16611
|
return c.text("Forbidden", 403);
|
|
16467
16612
|
}
|
|
16468
|
-
if (!
|
|
16613
|
+
if (!existsSync24(filePath)) {
|
|
16469
16614
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
16470
16615
|
return c.text("Not found", 404);
|
|
16471
16616
|
}
|
|
16472
16617
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
16473
16618
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
16474
16619
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
16475
|
-
const body =
|
|
16620
|
+
const body = readFileSync23(filePath);
|
|
16476
16621
|
return c.body(body, 200, {
|
|
16477
16622
|
"Content-Type": contentType,
|
|
16478
16623
|
"Cache-Control": "public, max-age=3600"
|
|
16479
16624
|
});
|
|
16480
16625
|
});
|
|
16481
|
-
|
|
16626
|
+
app43.get("/generated/:filename", (c) => {
|
|
16482
16627
|
const filename = c.req.param("filename");
|
|
16483
16628
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
16484
16629
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -16495,29 +16640,29 @@ app44.get("/generated/:filename", (c) => {
|
|
|
16495
16640
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
16496
16641
|
return c.text("Forbidden", 403);
|
|
16497
16642
|
}
|
|
16498
|
-
if (!
|
|
16643
|
+
if (!existsSync24(filePath)) {
|
|
16499
16644
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
16500
16645
|
return c.text("Not found", 404);
|
|
16501
16646
|
}
|
|
16502
16647
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
16503
16648
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
16504
16649
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
16505
|
-
const body =
|
|
16650
|
+
const body = readFileSync23(filePath);
|
|
16506
16651
|
return c.body(body, 200, {
|
|
16507
16652
|
"Content-Type": contentType,
|
|
16508
16653
|
"Cache-Control": "public, max-age=86400"
|
|
16509
16654
|
});
|
|
16510
16655
|
});
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
|
|
16656
|
+
app43.route("/sites", sites_default);
|
|
16657
|
+
app43.route("/listings", listings_default);
|
|
16658
|
+
app43.route("/v", visitor_event_default);
|
|
16659
|
+
app43.route("/v", visitor_consent_default);
|
|
16515
16660
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
16516
16661
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
16517
16662
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
16518
|
-
if (BRAND_JSON_PATH &&
|
|
16663
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
16519
16664
|
try {
|
|
16520
|
-
const fullBrand = JSON.parse(
|
|
16665
|
+
const fullBrand = JSON.parse(readFileSync23(BRAND_JSON_PATH, "utf-8"));
|
|
16521
16666
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
16522
16667
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
16523
16668
|
} catch {
|
|
@@ -16535,8 +16680,8 @@ function readInstalledVersion() {
|
|
|
16535
16680
|
try {
|
|
16536
16681
|
if (!PLATFORM_ROOT9) return "unknown";
|
|
16537
16682
|
const versionFile = join18(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
|
|
16538
|
-
if (!
|
|
16539
|
-
const content =
|
|
16683
|
+
if (!existsSync24(versionFile)) return "unknown";
|
|
16684
|
+
const content = readFileSync23(versionFile, "utf-8").trim();
|
|
16540
16685
|
return content || "unknown";
|
|
16541
16686
|
} catch {
|
|
16542
16687
|
return "unknown";
|
|
@@ -16577,7 +16722,7 @@ var clientErrorReporterScript = `<script>
|
|
|
16577
16722
|
function cachedHtml(file) {
|
|
16578
16723
|
let html = htmlCache.get(file);
|
|
16579
16724
|
if (!html) {
|
|
16580
|
-
html =
|
|
16725
|
+
html = readFileSync23(resolve26(process.cwd(), "public", file), "utf-8");
|
|
16581
16726
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
16582
16727
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
16583
16728
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -16598,8 +16743,8 @@ function loadBrandingCache(agentSlug) {
|
|
|
16598
16743
|
const accountId = getDefaultAccountId();
|
|
16599
16744
|
if (!accountId) return null;
|
|
16600
16745
|
const cachePath = join18(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
16601
|
-
if (!
|
|
16602
|
-
return JSON.parse(
|
|
16746
|
+
if (!existsSync24(cachePath)) return null;
|
|
16747
|
+
return JSON.parse(readFileSync23(cachePath, "utf-8"));
|
|
16603
16748
|
} catch {
|
|
16604
16749
|
return null;
|
|
16605
16750
|
}
|
|
@@ -16642,7 +16787,7 @@ function escapeHtml(s) {
|
|
|
16642
16787
|
function agentUnavailableHtml() {
|
|
16643
16788
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(BRAND.productName)}</title></head><body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1.5rem;color:#222"><h1 style="font-size:1.25rem">Agent unavailable</h1><p>This agent isn't available right now. If you reached this page from a saved link, the agent may have been turned off.</p></body></html>`;
|
|
16644
16789
|
}
|
|
16645
|
-
|
|
16790
|
+
app43.get("/", (c) => {
|
|
16646
16791
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
16647
16792
|
if (isPublicHost(host)) {
|
|
16648
16793
|
const defaultSlug = resolveDefaultSlug();
|
|
@@ -16658,12 +16803,12 @@ app44.get("/", (c) => {
|
|
|
16658
16803
|
}
|
|
16659
16804
|
return c.html(cachedHtml("index.html"));
|
|
16660
16805
|
});
|
|
16661
|
-
|
|
16806
|
+
app43.get("/public", (c) => {
|
|
16662
16807
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
16663
16808
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
16664
16809
|
return c.html(cachedHtml("public.html"));
|
|
16665
16810
|
});
|
|
16666
|
-
|
|
16811
|
+
app43.get("/chat", (c) => {
|
|
16667
16812
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
16668
16813
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
16669
16814
|
return c.html(cachedHtml("public.html"));
|
|
@@ -16682,12 +16827,12 @@ async function logViewerFetch(c, next) {
|
|
|
16682
16827
|
duration_ms: Date.now() - start
|
|
16683
16828
|
});
|
|
16684
16829
|
}
|
|
16685
|
-
|
|
16686
|
-
|
|
16687
|
-
|
|
16830
|
+
app43.use("/vnc-viewer.html", logViewerFetch);
|
|
16831
|
+
app43.use("/vnc-popout.html", logViewerFetch);
|
|
16832
|
+
app43.get("/vnc-popout.html", (c) => {
|
|
16688
16833
|
let html = htmlCache.get("vnc-popout.html");
|
|
16689
16834
|
if (!html) {
|
|
16690
|
-
html =
|
|
16835
|
+
html = readFileSync23(resolve26(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
16691
16836
|
const name = escapeHtml(BRAND.productName);
|
|
16692
16837
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
16693
16838
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -16697,7 +16842,7 @@ app44.get("/vnc-popout.html", (c) => {
|
|
|
16697
16842
|
}
|
|
16698
16843
|
return c.html(html);
|
|
16699
16844
|
});
|
|
16700
|
-
|
|
16845
|
+
app43.post("/api/vnc/client-event", async (c) => {
|
|
16701
16846
|
let body;
|
|
16702
16847
|
try {
|
|
16703
16848
|
body = await c.req.json();
|
|
@@ -16718,30 +16863,30 @@ app44.post("/api/vnc/client-event", async (c) => {
|
|
|
16718
16863
|
});
|
|
16719
16864
|
return c.json({ ok: true });
|
|
16720
16865
|
});
|
|
16721
|
-
|
|
16866
|
+
app43.get("/g/:slug", (c) => {
|
|
16722
16867
|
return c.html(brandedPublicHtml());
|
|
16723
16868
|
});
|
|
16724
|
-
|
|
16869
|
+
app43.get("/graph", (c) => {
|
|
16725
16870
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
16726
16871
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
16727
16872
|
return c.html(cachedHtml("graph.html"));
|
|
16728
16873
|
});
|
|
16729
|
-
|
|
16874
|
+
app43.get("/sessions", (c) => {
|
|
16730
16875
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
16731
16876
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
16732
16877
|
return c.html(cachedHtml("sessions.html"));
|
|
16733
16878
|
});
|
|
16734
|
-
|
|
16879
|
+
app43.get("/data", (c) => {
|
|
16735
16880
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
16736
16881
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
16737
16882
|
return c.html(cachedHtml("data.html"));
|
|
16738
16883
|
});
|
|
16739
|
-
|
|
16884
|
+
app43.get("/browser", (c) => {
|
|
16740
16885
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
16741
16886
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
16742
16887
|
return c.html(cachedHtml("browser.html"));
|
|
16743
16888
|
});
|
|
16744
|
-
|
|
16889
|
+
app43.get("/:slug", async (c, next) => {
|
|
16745
16890
|
const slug = c.req.param("slug");
|
|
16746
16891
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
16747
16892
|
const account = resolveAccount();
|
|
@@ -16756,13 +16901,13 @@ app44.get("/:slug", async (c, next) => {
|
|
|
16756
16901
|
await next();
|
|
16757
16902
|
});
|
|
16758
16903
|
if (brandFaviconPath !== "/favicon.ico") {
|
|
16759
|
-
|
|
16904
|
+
app43.get("/favicon.ico", (c) => {
|
|
16760
16905
|
c.header("Cache-Control", "public, max-age=300");
|
|
16761
16906
|
return c.redirect(brandFaviconPath, 302);
|
|
16762
16907
|
});
|
|
16763
16908
|
}
|
|
16764
|
-
|
|
16765
|
-
|
|
16909
|
+
app43.use("/*", serveStatic({ root: "./public" }));
|
|
16910
|
+
app43.all("*", (c) => {
|
|
16766
16911
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
16767
16912
|
const path2 = c.req.path;
|
|
16768
16913
|
if (isPublicHost(host)) {
|
|
@@ -16776,7 +16921,7 @@ app44.all("*", (c) => {
|
|
|
16776
16921
|
});
|
|
16777
16922
|
var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
16778
16923
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
16779
|
-
var httpServer = serve({ fetch:
|
|
16924
|
+
var httpServer = serve({ fetch: app43.fetch, port, hostname });
|
|
16780
16925
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
16781
16926
|
startAuthHealthHeartbeat();
|
|
16782
16927
|
{
|
|
@@ -16813,7 +16958,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
16813
16958
|
}
|
|
16814
16959
|
try {
|
|
16815
16960
|
const registered = [];
|
|
16816
|
-
for (const r of
|
|
16961
|
+
for (const r of app43.routes ?? []) {
|
|
16817
16962
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
16818
16963
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
16819
16964
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -16841,11 +16986,11 @@ try {
|
|
|
16841
16986
|
var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
16842
16987
|
async function runAdminUserReconcileTick() {
|
|
16843
16988
|
try {
|
|
16844
|
-
if (!
|
|
16989
|
+
if (!existsSync24(USERS_FILE)) {
|
|
16845
16990
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
16846
16991
|
return;
|
|
16847
16992
|
}
|
|
16848
|
-
const usersRaw =
|
|
16993
|
+
const usersRaw = readFileSync23(USERS_FILE, "utf-8").trim();
|
|
16849
16994
|
if (!usersRaw) {
|
|
16850
16995
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
16851
16996
|
return;
|
|
@@ -16910,6 +17055,7 @@ reconcileEnabledPlugins(bootAccount?.accountDir, bootAccount?.config, bootAccoun
|
|
|
16910
17055
|
for (const acct of listValidAccounts()) {
|
|
16911
17056
|
cleanupLeakedPremiumSubs(acct.accountDir, acct.config, acct.accountId);
|
|
16912
17057
|
}
|
|
17058
|
+
warnOnChannelAdminBindingDrift();
|
|
16913
17059
|
var bootEnabled = Array.isArray(bootAccountConfig?.enabledPlugins) ? bootAccountConfig.enabledPlugins : [];
|
|
16914
17060
|
var bootDelivered = [];
|
|
16915
17061
|
var bootDistMissing = [];
|