@rubytech/create-realagent 1.0.676 → 1.0.677
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +77 -166
- package/dist/uninstall.js +24 -0
- package/package.json +1 -1
- package/payload/platform/plugins/docs/references/deployment.md +2 -3
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/docs/references/troubleshooting.md +2 -2
- package/payload/platform/scripts/vnc.sh +34 -13
- package/payload/platform/templates/systemd/maxy-edge.service +33 -0
- package/payload/server/maxy-edge.js +2 -0
- package/payload/server/server.js +265 -583
- package/payload/platform/templates/dotfiles/.tmux.conf +0 -1
- package/payload/platform/templates/systemd/maxy-ttyd.service +0 -25
package/payload/server/server.js
CHANGED
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
invalidateRemoteSession,
|
|
17
17
|
isPasswordValid,
|
|
18
18
|
isRemoteAuthConfigured,
|
|
19
|
-
newCorrId,
|
|
20
19
|
recordFailedAttempt,
|
|
21
20
|
renderLoginPage,
|
|
22
21
|
resolveClientIp,
|
|
@@ -2530,7 +2529,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
2530
2529
|
});
|
|
2531
2530
|
if (!chunk) {
|
|
2532
2531
|
if (i === 1) {
|
|
2533
|
-
await new Promise((
|
|
2532
|
+
await new Promise((resolve28) => setTimeout(resolve28));
|
|
2534
2533
|
maxReadCount = 3;
|
|
2535
2534
|
continue;
|
|
2536
2535
|
}
|
|
@@ -2897,317 +2896,9 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
2897
2896
|
|
|
2898
2897
|
// server/index.ts
|
|
2899
2898
|
import { readFileSync as readFileSync24, existsSync as existsSync23, watchFile } from "fs";
|
|
2900
|
-
import { resolve as
|
|
2899
|
+
import { resolve as resolve27, join as join12, basename as basename7 } from "path";
|
|
2901
2900
|
import { homedir as homedir4 } from "os";
|
|
2902
2901
|
|
|
2903
|
-
// server/ws-proxy-terminal.ts
|
|
2904
|
-
import { createConnection } from "net";
|
|
2905
|
-
|
|
2906
|
-
// app/lib/terminal-logger.ts
|
|
2907
|
-
import { appendFileSync, mkdirSync } from "fs";
|
|
2908
|
-
import { resolve } from "path";
|
|
2909
|
-
var TERMINAL_LOG_FILE = resolve(LOG_DIR, "terminal.log");
|
|
2910
|
-
try {
|
|
2911
|
-
mkdirSync(LOG_DIR, { recursive: true });
|
|
2912
|
-
} catch (err) {
|
|
2913
|
-
console.error(`[terminal-log-fail] mkdir ${LOG_DIR} failed: ${err.message}`);
|
|
2914
|
-
}
|
|
2915
|
-
function terminalLog(phase, fields = {}) {
|
|
2916
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
2917
|
-
const kv = Object.entries(fields).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
|
|
2918
|
-
const line = kv.length > 0 ? `[${ts}] [terminal-${phase}] ${kv}
|
|
2919
|
-
` : `[${ts}] [terminal-${phase}]
|
|
2920
|
-
`;
|
|
2921
|
-
try {
|
|
2922
|
-
appendFileSync(TERMINAL_LOG_FILE, line);
|
|
2923
|
-
} catch (err) {
|
|
2924
|
-
console.error(`[terminal-log-fail] ${err.message} \u2014 dropped: ${line.slice(0, 300).trim()}`);
|
|
2925
|
-
}
|
|
2926
|
-
}
|
|
2927
|
-
|
|
2928
|
-
// server/ws-proxy-terminal.ts
|
|
2929
|
-
var WS_PATH = "/admin/terminal/ws";
|
|
2930
|
-
var UPSTREAM_TIMEOUT_MS = 5e3;
|
|
2931
|
-
var FLOW_TICK_MS = 5e3;
|
|
2932
|
-
var FLOW_IDLE_EMIT_MS = 3e4;
|
|
2933
|
-
var HOP_BY_HOP = /* @__PURE__ */ new Set([
|
|
2934
|
-
"connection",
|
|
2935
|
-
"keep-alive",
|
|
2936
|
-
"proxy-authenticate",
|
|
2937
|
-
"proxy-authorization",
|
|
2938
|
-
"te",
|
|
2939
|
-
"trailer",
|
|
2940
|
-
"transfer-encoding",
|
|
2941
|
-
"upgrade"
|
|
2942
|
-
]);
|
|
2943
|
-
function attachTerminalWsProxy(server, opts) {
|
|
2944
|
-
const upstreamHost = opts.upstreamHost ?? "127.0.0.1";
|
|
2945
|
-
const upstreamPort = opts.upstreamPort ?? 7681;
|
|
2946
|
-
server.on("upgrade", (req, clientSocket, head) => {
|
|
2947
|
-
try {
|
|
2948
|
-
handleUpgrade(req, clientSocket, head, {
|
|
2949
|
-
isPublicHost: opts.isPublicHost,
|
|
2950
|
-
upstreamHost,
|
|
2951
|
-
upstreamPort
|
|
2952
|
-
});
|
|
2953
|
-
} catch (err) {
|
|
2954
|
-
terminalLog("ws-upgrade", {
|
|
2955
|
-
decision: "rejected",
|
|
2956
|
-
reason: "handler-exception",
|
|
2957
|
-
err: err.message
|
|
2958
|
-
});
|
|
2959
|
-
clientSocket.destroy();
|
|
2960
|
-
}
|
|
2961
|
-
});
|
|
2962
|
-
}
|
|
2963
|
-
function handleUpgrade(req, clientSocket, head, opts) {
|
|
2964
|
-
const url = req.url ?? "";
|
|
2965
|
-
const qsIndex = url.indexOf("?");
|
|
2966
|
-
const pathname = qsIndex === -1 ? url : url.slice(0, qsIndex);
|
|
2967
|
-
if (pathname !== WS_PATH) {
|
|
2968
|
-
return;
|
|
2969
|
-
}
|
|
2970
|
-
const corrId = newCorrId();
|
|
2971
|
-
const query = qsIndex === -1 ? "" : url.slice(qsIndex + 1);
|
|
2972
|
-
const rawClientCorrId = parseQueryParam(query, "corrId");
|
|
2973
|
-
const clientCorrId = sanitizeClientCorrId(rawClientCorrId);
|
|
2974
|
-
const hostHeader = (req.headers.host ?? "").split(":")[0];
|
|
2975
|
-
const originHeader = headerString(req.headers.origin);
|
|
2976
|
-
const remote = req.socket.remoteAddress;
|
|
2977
|
-
const xff = headerString(req.headers["x-forwarded-for"]);
|
|
2978
|
-
const decision = canAccessAdmin({
|
|
2979
|
-
host: hostHeader,
|
|
2980
|
-
remoteAddress: remote,
|
|
2981
|
-
xForwardedFor: xff,
|
|
2982
|
-
cookieHeader: headerString(req.headers.cookie),
|
|
2983
|
-
isPublicHost: opts.isPublicHost
|
|
2984
|
-
});
|
|
2985
|
-
if (!decision.allow) {
|
|
2986
|
-
const status = decision.reason === "public-host" ? 404 : 401;
|
|
2987
|
-
terminalLog("ws-upgrade", {
|
|
2988
|
-
corrId,
|
|
2989
|
-
clientCorrId: clientCorrId ?? null,
|
|
2990
|
-
decision: "rejected",
|
|
2991
|
-
reason: decision.reason,
|
|
2992
|
-
ip: remote,
|
|
2993
|
-
xff: xff ?? null,
|
|
2994
|
-
origin: originHeader ?? null,
|
|
2995
|
-
host: hostHeader
|
|
2996
|
-
});
|
|
2997
|
-
writeStatusAndDestroy(clientSocket, status, decision.reason === "public-host" ? "Not Found" : "Unauthorized");
|
|
2998
|
-
return;
|
|
2999
|
-
}
|
|
3000
|
-
const originHost = parseOriginHost(originHeader);
|
|
3001
|
-
if (!originHost) {
|
|
3002
|
-
terminalLog("ws-upgrade", {
|
|
3003
|
-
corrId,
|
|
3004
|
-
clientCorrId: clientCorrId ?? null,
|
|
3005
|
-
decision: "rejected",
|
|
3006
|
-
reason: "origin-missing-or-invalid",
|
|
3007
|
-
origin: originHeader ?? null,
|
|
3008
|
-
host: hostHeader,
|
|
3009
|
-
ip: remote
|
|
3010
|
-
});
|
|
3011
|
-
writeStatusAndDestroy(clientSocket, 403, "Forbidden");
|
|
3012
|
-
return;
|
|
3013
|
-
}
|
|
3014
|
-
if (originHost !== hostHeader) {
|
|
3015
|
-
terminalLog("ws-upgrade", {
|
|
3016
|
-
corrId,
|
|
3017
|
-
clientCorrId: clientCorrId ?? null,
|
|
3018
|
-
decision: "rejected",
|
|
3019
|
-
reason: "origin-mismatch",
|
|
3020
|
-
origin_host: originHost,
|
|
3021
|
-
host: hostHeader,
|
|
3022
|
-
ip: remote
|
|
3023
|
-
});
|
|
3024
|
-
writeStatusAndDestroy(clientSocket, 403, "Forbidden");
|
|
3025
|
-
return;
|
|
3026
|
-
}
|
|
3027
|
-
if (opts.isPublicHost(originHost)) {
|
|
3028
|
-
terminalLog("ws-upgrade", {
|
|
3029
|
-
corrId,
|
|
3030
|
-
clientCorrId: clientCorrId ?? null,
|
|
3031
|
-
decision: "rejected",
|
|
3032
|
-
reason: "origin-public-host",
|
|
3033
|
-
origin_host: originHost,
|
|
3034
|
-
host: hostHeader,
|
|
3035
|
-
ip: remote
|
|
3036
|
-
});
|
|
3037
|
-
writeStatusAndDestroy(clientSocket, 403, "Forbidden");
|
|
3038
|
-
return;
|
|
3039
|
-
}
|
|
3040
|
-
terminalLog("ws-upgrade", {
|
|
3041
|
-
corrId,
|
|
3042
|
-
clientCorrId: clientCorrId ?? null,
|
|
3043
|
-
decision: "accepted",
|
|
3044
|
-
ip: remote,
|
|
3045
|
-
xff: xff ?? null,
|
|
3046
|
-
origin: originHeader ?? null,
|
|
3047
|
-
host: hostHeader,
|
|
3048
|
-
sec_ws_version: headerString(req.headers["sec-websocket-version"]) ?? null,
|
|
3049
|
-
sec_ws_protocol: headerString(req.headers["sec-websocket-protocol"]) ?? null
|
|
3050
|
-
});
|
|
3051
|
-
const upstream = createConnection({ host: opts.upstreamHost, port: opts.upstreamPort });
|
|
3052
|
-
upstream.setTimeout(UPSTREAM_TIMEOUT_MS);
|
|
3053
|
-
let bytesClientToUpstream = 0;
|
|
3054
|
-
let bytesUpstreamToClient = 0;
|
|
3055
|
-
let closedBy = null;
|
|
3056
|
-
let proxyOpened = false;
|
|
3057
|
-
let lastUpstreamByteTs = 0;
|
|
3058
|
-
let flowInterval = null;
|
|
3059
|
-
let lastEmittedAt = 0;
|
|
3060
|
-
let lastEmittedUpstreamBytes = 0;
|
|
3061
|
-
const finish = (side, reason) => {
|
|
3062
|
-
if (closedBy) return;
|
|
3063
|
-
closedBy = side;
|
|
3064
|
-
if (flowInterval) {
|
|
3065
|
-
clearInterval(flowInterval);
|
|
3066
|
-
flowInterval = null;
|
|
3067
|
-
}
|
|
3068
|
-
if (proxyOpened) {
|
|
3069
|
-
terminalLog("proxy-close", {
|
|
3070
|
-
corrId,
|
|
3071
|
-
closedBy: side,
|
|
3072
|
-
reason,
|
|
3073
|
-
clientBytes: bytesClientToUpstream,
|
|
3074
|
-
upstreamBytes: bytesUpstreamToClient
|
|
3075
|
-
});
|
|
3076
|
-
}
|
|
3077
|
-
clientSocket.destroy();
|
|
3078
|
-
upstream.destroy();
|
|
3079
|
-
};
|
|
3080
|
-
upstream.once("connect", () => {
|
|
3081
|
-
upstream.setTimeout(0);
|
|
3082
|
-
proxyOpened = true;
|
|
3083
|
-
terminalLog("proxy-open", {
|
|
3084
|
-
corrId,
|
|
3085
|
-
upstream: `${opts.upstreamHost}:${opts.upstreamPort}`
|
|
3086
|
-
});
|
|
3087
|
-
const lines = [];
|
|
3088
|
-
lines.push(`${req.method ?? "GET"} ${WS_PATH} HTTP/${req.httpVersion}`);
|
|
3089
|
-
lines.push(`host: ${opts.upstreamHost}:${opts.upstreamPort}`);
|
|
3090
|
-
for (const [name, value] of Object.entries(req.headers)) {
|
|
3091
|
-
if (name === "host") continue;
|
|
3092
|
-
if (HOP_BY_HOP.has(name)) continue;
|
|
3093
|
-
if (value == null) continue;
|
|
3094
|
-
if (Array.isArray(value)) {
|
|
3095
|
-
for (const v of value) lines.push(`${name}: ${v}`);
|
|
3096
|
-
} else {
|
|
3097
|
-
lines.push(`${name}: ${value}`);
|
|
3098
|
-
}
|
|
3099
|
-
}
|
|
3100
|
-
const upgradeHeader = headerString(req.headers.upgrade);
|
|
3101
|
-
const connectionHeader = headerString(req.headers.connection);
|
|
3102
|
-
if (upgradeHeader) lines.push(`upgrade: ${upgradeHeader}`);
|
|
3103
|
-
if (connectionHeader) lines.push(`connection: ${connectionHeader}`);
|
|
3104
|
-
upstream.write(lines.join("\r\n") + "\r\n\r\n");
|
|
3105
|
-
if (head && head.length > 0) upstream.write(head);
|
|
3106
|
-
clientSocket.on("data", (chunk) => {
|
|
3107
|
-
bytesClientToUpstream += chunk.length;
|
|
3108
|
-
});
|
|
3109
|
-
upstream.on("data", (chunk) => {
|
|
3110
|
-
bytesUpstreamToClient += chunk.length;
|
|
3111
|
-
lastUpstreamByteTs = Date.now();
|
|
3112
|
-
});
|
|
3113
|
-
clientSocket.pipe(upstream);
|
|
3114
|
-
upstream.pipe(clientSocket);
|
|
3115
|
-
lastEmittedAt = Date.now();
|
|
3116
|
-
lastEmittedUpstreamBytes = 0;
|
|
3117
|
-
flowInterval = setInterval(() => {
|
|
3118
|
-
const now = Date.now();
|
|
3119
|
-
const idleMs = lastUpstreamByteTs === 0 ? now - lastEmittedAt : now - lastUpstreamByteTs;
|
|
3120
|
-
const upstreamMoved = bytesUpstreamToClient !== lastEmittedUpstreamBytes;
|
|
3121
|
-
const sinceLastEmit = now - lastEmittedAt;
|
|
3122
|
-
if (upstreamMoved || sinceLastEmit >= FLOW_IDLE_EMIT_MS) {
|
|
3123
|
-
terminalLog("proxy-flow", {
|
|
3124
|
-
corrId,
|
|
3125
|
-
clientBytes: bytesClientToUpstream,
|
|
3126
|
-
upstreamBytes: bytesUpstreamToClient,
|
|
3127
|
-
idleMs
|
|
3128
|
-
});
|
|
3129
|
-
lastEmittedAt = now;
|
|
3130
|
-
lastEmittedUpstreamBytes = bytesUpstreamToClient;
|
|
3131
|
-
}
|
|
3132
|
-
}, FLOW_TICK_MS);
|
|
3133
|
-
clientSocket.once("close", (hadError) => {
|
|
3134
|
-
finish("client", hadError ? "error" : "normal");
|
|
3135
|
-
});
|
|
3136
|
-
upstream.once("close", (hadError) => {
|
|
3137
|
-
finish("upstream", hadError ? "error" : "normal");
|
|
3138
|
-
});
|
|
3139
|
-
clientSocket.once("error", (err) => {
|
|
3140
|
-
terminalLog("proxy-error", { corrId, side: "client", err: err.message });
|
|
3141
|
-
finish("client", "error");
|
|
3142
|
-
});
|
|
3143
|
-
upstream.once("error", (err) => {
|
|
3144
|
-
terminalLog("proxy-error", { corrId, side: "upstream", err: err.message });
|
|
3145
|
-
finish("upstream", "error");
|
|
3146
|
-
});
|
|
3147
|
-
});
|
|
3148
|
-
upstream.once("timeout", () => {
|
|
3149
|
-
if (proxyOpened) return;
|
|
3150
|
-
terminalLog("proxy-error", {
|
|
3151
|
-
corrId,
|
|
3152
|
-
side: "upstream-connect",
|
|
3153
|
-
err: "timeout",
|
|
3154
|
-
timeout_ms: UPSTREAM_TIMEOUT_MS
|
|
3155
|
-
});
|
|
3156
|
-
writeStatusAndDestroy(clientSocket, 504, "Gateway Timeout");
|
|
3157
|
-
upstream.destroy();
|
|
3158
|
-
});
|
|
3159
|
-
upstream.once("error", (err) => {
|
|
3160
|
-
if (proxyOpened) return;
|
|
3161
|
-
terminalLog("proxy-error", {
|
|
3162
|
-
corrId,
|
|
3163
|
-
side: "upstream-connect",
|
|
3164
|
-
err: err.message
|
|
3165
|
-
});
|
|
3166
|
-
writeStatusAndDestroy(clientSocket, 502, "Bad Gateway");
|
|
3167
|
-
upstream.destroy();
|
|
3168
|
-
});
|
|
3169
|
-
}
|
|
3170
|
-
function parseQueryParam(query, key) {
|
|
3171
|
-
if (!query) return null;
|
|
3172
|
-
for (const pair of query.split("&")) {
|
|
3173
|
-
const eq = pair.indexOf("=");
|
|
3174
|
-
const k = eq === -1 ? pair : pair.slice(0, eq);
|
|
3175
|
-
if (k !== key) continue;
|
|
3176
|
-
const v = eq === -1 ? "" : pair.slice(eq + 1);
|
|
3177
|
-
try {
|
|
3178
|
-
return decodeURIComponent(v);
|
|
3179
|
-
} catch {
|
|
3180
|
-
return null;
|
|
3181
|
-
}
|
|
3182
|
-
}
|
|
3183
|
-
return null;
|
|
3184
|
-
}
|
|
3185
|
-
function headerString(value) {
|
|
3186
|
-
if (value == null) return void 0;
|
|
3187
|
-
return Array.isArray(value) ? value[0] : value;
|
|
3188
|
-
}
|
|
3189
|
-
function parseOriginHost(origin) {
|
|
3190
|
-
if (!origin) return null;
|
|
3191
|
-
try {
|
|
3192
|
-
return new URL(origin).hostname;
|
|
3193
|
-
} catch {
|
|
3194
|
-
return null;
|
|
3195
|
-
}
|
|
3196
|
-
}
|
|
3197
|
-
function writeStatusAndDestroy(socket, status, statusText) {
|
|
3198
|
-
try {
|
|
3199
|
-
socket.write(
|
|
3200
|
-
`HTTP/1.1 ${status} ${statusText}\r
|
|
3201
|
-
Connection: close\r
|
|
3202
|
-
Content-Length: 0\r
|
|
3203
|
-
\r
|
|
3204
|
-
`
|
|
3205
|
-
);
|
|
3206
|
-
} catch {
|
|
3207
|
-
}
|
|
3208
|
-
socket.destroy();
|
|
3209
|
-
}
|
|
3210
|
-
|
|
3211
2902
|
// app/lib/agent-slug-pattern.ts
|
|
3212
2903
|
var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
|
|
3213
2904
|
|
|
@@ -3215,9 +2906,9 @@ var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
|
|
|
3215
2906
|
import Anthropic3 from "@anthropic-ai/sdk";
|
|
3216
2907
|
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
3217
2908
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3218
|
-
import { resolve as
|
|
2909
|
+
import { resolve as resolve5, join as join3 } from "path";
|
|
3219
2910
|
import { platform as osPlatform } from "os";
|
|
3220
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync5, mkdirSync as
|
|
2911
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync5, mkdirSync as mkdirSync4, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync3, cpSync, rmSync as rmSync2, appendFileSync, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
|
|
3221
2912
|
import { lookup as dnsLookup } from "dns/promises";
|
|
3222
2913
|
import { createConnection as netConnect } from "net";
|
|
3223
2914
|
import { StringDecoder } from "string_decoder";
|
|
@@ -3226,12 +2917,12 @@ import { StringDecoder } from "string_decoder";
|
|
|
3226
2917
|
var import_dist = __toESM(require_dist());
|
|
3227
2918
|
import {
|
|
3228
2919
|
existsSync as existsSync2,
|
|
3229
|
-
mkdirSync
|
|
2920
|
+
mkdirSync,
|
|
3230
2921
|
readFileSync,
|
|
3231
2922
|
unlinkSync,
|
|
3232
2923
|
writeFileSync
|
|
3233
2924
|
} from "fs";
|
|
3234
|
-
import { resolve
|
|
2925
|
+
import { resolve, join as join2, dirname } from "path";
|
|
3235
2926
|
import { homedir } from "os";
|
|
3236
2927
|
var cachedKeyFilePath = null;
|
|
3237
2928
|
function resolveKeyFilePath() {
|
|
@@ -3262,7 +2953,7 @@ function resolveKeyFilePath() {
|
|
|
3262
2953
|
throw err;
|
|
3263
2954
|
}
|
|
3264
2955
|
}
|
|
3265
|
-
cachedKeyFilePath =
|
|
2956
|
+
cachedKeyFilePath = resolve(homedir(), configDirName, ".anthropic-api-key");
|
|
3266
2957
|
return cachedKeyFilePath;
|
|
3267
2958
|
}
|
|
3268
2959
|
function readKey() {
|
|
@@ -3548,11 +3239,11 @@ async function ensureAuth() {
|
|
|
3548
3239
|
|
|
3549
3240
|
// app/lib/vnc.ts
|
|
3550
3241
|
import { spawnSync, execFileSync } from "child_process";
|
|
3551
|
-
import { createConnection
|
|
3552
|
-
import { mkdirSync as
|
|
3553
|
-
import { resolve as
|
|
3554
|
-
var PLATFORM_ROOT = process.env.MAXY_PLATFORM_ROOT ??
|
|
3555
|
-
var VNC_SCRIPT =
|
|
3242
|
+
import { createConnection } from "net";
|
|
3243
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
3244
|
+
import { resolve as resolve2 } from "path";
|
|
3245
|
+
var PLATFORM_ROOT = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "..");
|
|
3246
|
+
var VNC_SCRIPT = resolve2(PLATFORM_ROOT, "scripts/vnc.sh");
|
|
3556
3247
|
var displayMode = process.env.DISPLAY_MODE ?? "virtual";
|
|
3557
3248
|
if (displayMode === "native") {
|
|
3558
3249
|
console.log(`[vnc] DISPLAY_MODE=native \u2014 local requests use desktop display, remote requests use VNC`);
|
|
@@ -3644,7 +3335,7 @@ async function waitForPort(port2, timeoutMs = 12e3) {
|
|
|
3644
3335
|
const deadline = Date.now() + timeoutMs;
|
|
3645
3336
|
while (Date.now() < deadline) {
|
|
3646
3337
|
const ready = await new Promise((res) => {
|
|
3647
|
-
const socket =
|
|
3338
|
+
const socket = createConnection(port2, "127.0.0.1");
|
|
3648
3339
|
socket.setTimeout(500);
|
|
3649
3340
|
socket.once("connect", () => {
|
|
3650
3341
|
socket.destroy();
|
|
@@ -3665,10 +3356,10 @@ async function waitForPort(port2, timeoutMs = 12e3) {
|
|
|
3665
3356
|
return false;
|
|
3666
3357
|
}
|
|
3667
3358
|
function ensureLogDir() {
|
|
3668
|
-
|
|
3359
|
+
mkdirSync2(LOG_DIR, { recursive: true });
|
|
3669
3360
|
}
|
|
3670
3361
|
function logPath(name) {
|
|
3671
|
-
return
|
|
3362
|
+
return resolve2(LOG_DIR, `${name}.log`);
|
|
3672
3363
|
}
|
|
3673
3364
|
async function ensureVnc() {
|
|
3674
3365
|
const up = await waitForPort(5900, 1e3);
|
|
@@ -3877,8 +3568,8 @@ async function ensureTerminalUpgrade(transport = "vnc") {
|
|
|
3877
3568
|
return { ok: true };
|
|
3878
3569
|
}
|
|
3879
3570
|
function writeChromiumWrapper() {
|
|
3880
|
-
|
|
3881
|
-
const wrapperPath =
|
|
3571
|
+
mkdirSync2(BIN_DIR, { recursive: true });
|
|
3572
|
+
const wrapperPath = resolve2(BIN_DIR, "chromium");
|
|
3882
3573
|
writeFileSync3(wrapperPath, `#!/bin/bash
|
|
3883
3574
|
LOG="${LOG_DIR}/chromium.log"
|
|
3884
3575
|
echo "==== [$(date)] chromium wrapper ====" >> "$LOG"
|
|
@@ -3964,12 +3655,12 @@ import neo4j from "neo4j-driver";
|
|
|
3964
3655
|
import { randomUUID } from "crypto";
|
|
3965
3656
|
import { spawn } from "child_process";
|
|
3966
3657
|
import { readFileSync as readFileSync4, readdirSync, existsSync as existsSync3, openSync, readSync, closeSync, statSync as statSync2, rmSync } from "fs";
|
|
3967
|
-
import { resolve as
|
|
3968
|
-
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
3658
|
+
import { resolve as resolve3 } from "path";
|
|
3659
|
+
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
|
|
3969
3660
|
var driver = null;
|
|
3970
3661
|
function readPassword() {
|
|
3971
3662
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
3972
|
-
const passwordFile =
|
|
3663
|
+
const passwordFile = resolve3(PLATFORM_ROOT2, "config/.neo4j-password");
|
|
3973
3664
|
try {
|
|
3974
3665
|
return readFileSync4(passwordFile, "utf-8").trim();
|
|
3975
3666
|
} catch {
|
|
@@ -4434,8 +4125,8 @@ async function persistMessage(conversationId, role, content, accountId, tokens,
|
|
|
4434
4125
|
const prev = persistMessageLocks.get(conversationId);
|
|
4435
4126
|
const waited = prev !== void 0;
|
|
4436
4127
|
let release;
|
|
4437
|
-
const mine = new Promise((
|
|
4438
|
-
release =
|
|
4128
|
+
const mine = new Promise((resolve28) => {
|
|
4129
|
+
release = resolve28;
|
|
4439
4130
|
});
|
|
4440
4131
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
4441
4132
|
persistMessageLocks.set(conversationId, chained);
|
|
@@ -4694,7 +4385,7 @@ ${userContent}`;
|
|
|
4694
4385
|
"dontAsk",
|
|
4695
4386
|
prompt
|
|
4696
4387
|
];
|
|
4697
|
-
return new Promise((
|
|
4388
|
+
return new Promise((resolve28) => {
|
|
4698
4389
|
let stdout = "";
|
|
4699
4390
|
let stderr = "";
|
|
4700
4391
|
const spawnFn = _spawnOverride ?? spawn;
|
|
@@ -4712,35 +4403,35 @@ ${userContent}`;
|
|
|
4712
4403
|
const timer = setTimeout(() => {
|
|
4713
4404
|
proc.kill("SIGTERM");
|
|
4714
4405
|
console.error("[persist] autoLabel: haiku subprocess timed out");
|
|
4715
|
-
|
|
4406
|
+
resolve28(null);
|
|
4716
4407
|
}, SESSION_LABEL_TIMEOUT_MS);
|
|
4717
4408
|
proc.on("error", (err) => {
|
|
4718
4409
|
clearTimeout(timer);
|
|
4719
4410
|
console.error(`[persist] autoLabel: subprocess error \u2014 ${err.message}`);
|
|
4720
|
-
|
|
4411
|
+
resolve28(null);
|
|
4721
4412
|
});
|
|
4722
4413
|
proc.on("close", (code) => {
|
|
4723
4414
|
clearTimeout(timer);
|
|
4724
4415
|
if (code !== 0) {
|
|
4725
4416
|
console.error(`[persist] autoLabel: subprocess exited code=${code}${stderr ? ` stderr=${stderr.trim().slice(0, 200)}` : ""}`);
|
|
4726
|
-
|
|
4417
|
+
resolve28(null);
|
|
4727
4418
|
return;
|
|
4728
4419
|
}
|
|
4729
4420
|
const text = stdout.trim();
|
|
4730
4421
|
if (!text) {
|
|
4731
4422
|
console.error("[persist] autoLabel: haiku returned empty response");
|
|
4732
|
-
|
|
4423
|
+
resolve28(null);
|
|
4733
4424
|
return;
|
|
4734
4425
|
}
|
|
4735
4426
|
if (text === "SKIP") {
|
|
4736
4427
|
console.error("[persist] autoLabel: haiku returned SKIP \u2014 messages too vague");
|
|
4737
|
-
|
|
4428
|
+
resolve28(null);
|
|
4738
4429
|
return;
|
|
4739
4430
|
}
|
|
4740
4431
|
const words = text.split(/\s+/).slice(0, SESSION_LABEL_MAX_WORDS);
|
|
4741
4432
|
const label = words.join(" ");
|
|
4742
4433
|
console.error(`[persist] autoLabel: haiku response="${label}"`);
|
|
4743
|
-
|
|
4434
|
+
resolve28(label);
|
|
4744
4435
|
});
|
|
4745
4436
|
});
|
|
4746
4437
|
}
|
|
@@ -5012,9 +4703,9 @@ var MAX_RECENT_TOOL_FAILURES = 3;
|
|
|
5012
4703
|
var RECENT_FAILURES_TAIL_BYTES = 10 * 1024;
|
|
5013
4704
|
function readRecentToolFailures(accountId, conversationId) {
|
|
5014
4705
|
try {
|
|
5015
|
-
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
5016
|
-
const logDir =
|
|
5017
|
-
const logPath2 =
|
|
4706
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
|
|
4707
|
+
const logDir = resolve3(platformRoot2, "..", "data/accounts", accountId, "logs");
|
|
4708
|
+
const logPath2 = resolve3(logDir, `claude-agent-stream-${conversationId}.log`);
|
|
5018
4709
|
if (!existsSync3(logPath2)) {
|
|
5019
4710
|
console.error(`[review-tail-skip] path=${logPath2} reason=file-missing \u2014 first turn of conversation, subprocess not yet spawned, or log rotated`);
|
|
5020
4711
|
return [];
|
|
@@ -5171,13 +4862,13 @@ ${taskLines.join("\n")}`);
|
|
|
5171
4862
|
let pendingCount = 0;
|
|
5172
4863
|
let pendingLines = [];
|
|
5173
4864
|
try {
|
|
5174
|
-
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
5175
|
-
const pendingDir =
|
|
4865
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
|
|
4866
|
+
const pendingDir = resolve3(platformRoot2, "..", "data/accounts", accountId, "pending-actions");
|
|
5176
4867
|
if (existsSync3(pendingDir)) {
|
|
5177
4868
|
const files = readdirSync(pendingDir).filter((f) => f.endsWith(".json") && !f.startsWith("."));
|
|
5178
4869
|
for (const file of files) {
|
|
5179
4870
|
try {
|
|
5180
|
-
const raw2 = readFileSync4(
|
|
4871
|
+
const raw2 = readFileSync4(resolve3(pendingDir, file), "utf-8");
|
|
5181
4872
|
const action = JSON.parse(raw2);
|
|
5182
4873
|
if (action.state === "pending") {
|
|
5183
4874
|
const inputSummary = JSON.stringify(action.hookPayload?.tool_input ?? {}).slice(0, 150);
|
|
@@ -5244,8 +4935,8 @@ ${sections.join("\n\n")}
|
|
|
5244
4935
|
}
|
|
5245
4936
|
}
|
|
5246
4937
|
async function consumeStep7FlagUI(session, accountId) {
|
|
5247
|
-
const accountDir =
|
|
5248
|
-
const flagPath =
|
|
4938
|
+
const accountDir = resolve3(PLATFORM_ROOT2, "..", "data/accounts", accountId);
|
|
4939
|
+
const flagPath = resolve3(accountDir, "onboarding", "step7-complete");
|
|
5249
4940
|
if (!existsSync3(flagPath)) return false;
|
|
5250
4941
|
let completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5251
4942
|
try {
|
|
@@ -5687,8 +5378,8 @@ ${items}
|
|
|
5687
5378
|
}
|
|
5688
5379
|
|
|
5689
5380
|
// app/lib/adherence-ledger.ts
|
|
5690
|
-
import { existsSync as existsSync4, mkdirSync as
|
|
5691
|
-
import { resolve as
|
|
5381
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, renameSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
5382
|
+
import { resolve as resolve4, dirname as dirname2 } from "path";
|
|
5692
5383
|
import lockfile from "proper-lockfile";
|
|
5693
5384
|
var LOG_TAG = "[adherence-ledger]";
|
|
5694
5385
|
var ADHERENCE_BLOCK_THRESHOLD = 5;
|
|
@@ -5703,7 +5394,7 @@ function nowIso() {
|
|
|
5703
5394
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
5704
5395
|
}
|
|
5705
5396
|
function ledgerPath(accountDir, agentName) {
|
|
5706
|
-
return
|
|
5397
|
+
return resolve4(accountDir, "agents", agentName, "adherence-ledger.json");
|
|
5707
5398
|
}
|
|
5708
5399
|
function lockPath(accountDir, agentName) {
|
|
5709
5400
|
return `${ledgerPath(accountDir, agentName)}.lock`;
|
|
@@ -5748,7 +5439,7 @@ function writeAtomic(path2, data) {
|
|
|
5748
5439
|
}
|
|
5749
5440
|
data.updated_at = nowIso();
|
|
5750
5441
|
const dir = dirname2(path2);
|
|
5751
|
-
|
|
5442
|
+
mkdirSync3(dir, { recursive: true });
|
|
5752
5443
|
const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
|
|
5753
5444
|
try {
|
|
5754
5445
|
writeFileSync4(tmp, JSON.stringify(data, null, 2), "utf-8");
|
|
@@ -5764,7 +5455,7 @@ function writeAtomic(path2, data) {
|
|
|
5764
5455
|
async function withLock(accountDir, agentName, fn) {
|
|
5765
5456
|
const path2 = ledgerPath(accountDir, agentName);
|
|
5766
5457
|
const lock = lockPath(accountDir, agentName);
|
|
5767
|
-
|
|
5458
|
+
mkdirSync3(dirname2(lock), { recursive: true });
|
|
5768
5459
|
if (!existsSync4(lock)) {
|
|
5769
5460
|
writeFileSync4(lock, "", "utf-8");
|
|
5770
5461
|
}
|
|
@@ -6252,20 +5943,20 @@ function agentLogStream(name, accountDir, conversationId) {
|
|
|
6252
5943
|
if (!conversationId) {
|
|
6253
5944
|
throw new Error(`agentLogStream: conversationId is required (name=${name}) \u2014 use preConversationLogStream for pre-session events`);
|
|
6254
5945
|
}
|
|
6255
|
-
const logDir =
|
|
6256
|
-
|
|
5946
|
+
const logDir = resolve5(accountDir, "logs");
|
|
5947
|
+
mkdirSync4(logDir, { recursive: true });
|
|
6257
5948
|
purgeOldLogs(logDir, `${name}-`);
|
|
6258
|
-
const logPath2 =
|
|
5949
|
+
const logPath2 = resolve5(logDir, `${name}-${conversationId}.log`);
|
|
6259
5950
|
const stream = createWriteStream(logPath2, { flags: "a" });
|
|
6260
5951
|
registerStreamLog(stream, { path: logPath2, conversationId, name });
|
|
6261
5952
|
return stream;
|
|
6262
5953
|
}
|
|
6263
5954
|
function preConversationLogStream(name, accountDir) {
|
|
6264
|
-
const logDir =
|
|
6265
|
-
|
|
5955
|
+
const logDir = resolve5(accountDir, "logs");
|
|
5956
|
+
mkdirSync4(logDir, { recursive: true });
|
|
6266
5957
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6267
5958
|
purgeOldLogs(logDir, `preconversation-${name}-`);
|
|
6268
|
-
const logPath2 =
|
|
5959
|
+
const logPath2 = resolve5(logDir, `preconversation-${name}-${date}.log`);
|
|
6269
5960
|
const stream = createWriteStream(logPath2, { flags: "a" });
|
|
6270
5961
|
registerStreamLog(stream, { path: logPath2, conversationId: null, name: `preconversation-${name}` });
|
|
6271
5962
|
return stream;
|
|
@@ -6284,7 +5975,7 @@ function sigtermFlushStreamLogs(reason, source) {
|
|
|
6284
5975
|
const line = `[${ts}] [server-sigterm] reason=${reason}${convPart} name=${entry.name} source=${source}
|
|
6285
5976
|
`;
|
|
6286
5977
|
try {
|
|
6287
|
-
|
|
5978
|
+
appendFileSync(entry.path, line);
|
|
6288
5979
|
} catch (err) {
|
|
6289
5980
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6290
5981
|
console.error(`[server-sigterm-flush-err] path=${entry.path} reason=${msg}`);
|
|
@@ -6303,7 +5994,7 @@ function purgeOldLogs(logDir, prefix) {
|
|
|
6303
5994
|
}
|
|
6304
5995
|
for (const file of entries) {
|
|
6305
5996
|
if (!file.startsWith(prefix)) continue;
|
|
6306
|
-
const filePath =
|
|
5997
|
+
const filePath = resolve5(logDir, file);
|
|
6307
5998
|
try {
|
|
6308
5999
|
if (statSync3(filePath).mtimeMs < cutoff) unlinkSync3(filePath);
|
|
6309
6000
|
} catch (err) {
|
|
@@ -6421,8 +6112,8 @@ function sampleProcState(pid) {
|
|
|
6421
6112
|
return `proc_err=${JSON.stringify(msg.slice(0, 60))}`;
|
|
6422
6113
|
}
|
|
6423
6114
|
}
|
|
6424
|
-
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ??
|
|
6425
|
-
var ACCOUNTS_DIR =
|
|
6115
|
+
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
|
|
6116
|
+
var ACCOUNTS_DIR = resolve5(PLATFORM_ROOT3, "..", "data/accounts");
|
|
6426
6117
|
if (!existsSync5(PLATFORM_ROOT3)) {
|
|
6427
6118
|
throw new Error(
|
|
6428
6119
|
`PLATFORM_ROOT does not exist: ${PLATFORM_ROOT3}
|
|
@@ -6431,7 +6122,7 @@ Set the MAXY_PLATFORM_ROOT environment variable to the absolute path of the plat
|
|
|
6431
6122
|
}
|
|
6432
6123
|
function resolveAccount() {
|
|
6433
6124
|
if (!existsSync5(ACCOUNTS_DIR)) return null;
|
|
6434
|
-
const usersFilePath =
|
|
6125
|
+
const usersFilePath = resolve5(PLATFORM_ROOT3, "config", "users.json");
|
|
6435
6126
|
let usersJsonUserId = null;
|
|
6436
6127
|
if (existsSync5(usersFilePath)) {
|
|
6437
6128
|
try {
|
|
@@ -6449,7 +6140,7 @@ function resolveAccount() {
|
|
|
6449
6140
|
let fallback = null;
|
|
6450
6141
|
for (const entry of entries) {
|
|
6451
6142
|
if (!entry.isDirectory()) continue;
|
|
6452
|
-
const configPath2 =
|
|
6143
|
+
const configPath2 = resolve5(ACCOUNTS_DIR, entry.name, "account.json");
|
|
6453
6144
|
if (!existsSync5(configPath2)) continue;
|
|
6454
6145
|
const raw2 = readFileSync6(configPath2, "utf-8");
|
|
6455
6146
|
let config;
|
|
@@ -6466,7 +6157,7 @@ function resolveAccount() {
|
|
|
6466
6157
|
}
|
|
6467
6158
|
const result = {
|
|
6468
6159
|
accountId: config.accountId,
|
|
6469
|
-
accountDir:
|
|
6160
|
+
accountDir: resolve5(ACCOUNTS_DIR, entry.name),
|
|
6470
6161
|
config
|
|
6471
6162
|
};
|
|
6472
6163
|
if (usersJsonUserId && config.admins?.some((a) => a.userId === usersJsonUserId)) {
|
|
@@ -6484,7 +6175,7 @@ function resolveAccount() {
|
|
|
6484
6175
|
return fallback;
|
|
6485
6176
|
}
|
|
6486
6177
|
function readAgentFile(accountDir, agentName, filename) {
|
|
6487
|
-
const filePath =
|
|
6178
|
+
const filePath = resolve5(accountDir, "agents", agentName, filename);
|
|
6488
6179
|
if (!existsSync5(filePath)) return null;
|
|
6489
6180
|
return readFileSync6(filePath, "utf-8");
|
|
6490
6181
|
}
|
|
@@ -6521,7 +6212,7 @@ function validateAgentSlug(slug) {
|
|
|
6521
6212
|
return true;
|
|
6522
6213
|
}
|
|
6523
6214
|
function resolveDefaultAgentSlug(accountDir) {
|
|
6524
|
-
const configPath2 =
|
|
6215
|
+
const configPath2 = resolve5(accountDir, "account.json");
|
|
6525
6216
|
if (!existsSync5(configPath2)) {
|
|
6526
6217
|
console.error("[agent-resolve] account.json not found \u2014 cannot resolve defaultAgent");
|
|
6527
6218
|
return null;
|
|
@@ -6537,7 +6228,7 @@ function resolveDefaultAgentSlug(accountDir) {
|
|
|
6537
6228
|
console.error("[agent-resolve] defaultAgent not configured in account.json \u2014 set it via the connect-whatsapp skill");
|
|
6538
6229
|
return null;
|
|
6539
6230
|
}
|
|
6540
|
-
const agentConfigPath =
|
|
6231
|
+
const agentConfigPath = resolve5(accountDir, "agents", config.defaultAgent, "config.json");
|
|
6541
6232
|
if (!existsSync5(agentConfigPath)) {
|
|
6542
6233
|
console.error(`[agent-resolve] defaultAgent="${config.defaultAgent}" has no config.json at ${agentConfigPath}`);
|
|
6543
6234
|
return null;
|
|
@@ -6610,9 +6301,9 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
6610
6301
|
}
|
|
6611
6302
|
let knowledge = null;
|
|
6612
6303
|
let knowledgeBaked = false;
|
|
6613
|
-
const agentDir =
|
|
6614
|
-
const knowledgePath =
|
|
6615
|
-
const summaryPath =
|
|
6304
|
+
const agentDir = resolve5(accountDir, "agents", agentName);
|
|
6305
|
+
const knowledgePath = resolve5(agentDir, "KNOWLEDGE.md");
|
|
6306
|
+
const summaryPath = resolve5(agentDir, "KNOWLEDGE-SUMMARY.md");
|
|
6616
6307
|
const hasKnowledge = existsSync5(knowledgePath);
|
|
6617
6308
|
const hasSummary = existsSync5(summaryPath);
|
|
6618
6309
|
if (hasKnowledge && hasSummary) {
|
|
@@ -6648,7 +6339,7 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
6648
6339
|
return { model, plugins, status, displayName, image, imageShape, showAgentName, knowledge, knowledgeBaked, liveMemory, knowledgeKeywords, budget, accessMode };
|
|
6649
6340
|
}
|
|
6650
6341
|
function parsePluginFrontmatter(pluginDir) {
|
|
6651
|
-
const pluginPath =
|
|
6342
|
+
const pluginPath = resolve5(PLATFORM_ROOT3, "plugins", pluginDir, "PLUGIN.md");
|
|
6652
6343
|
if (!existsSync5(pluginPath)) return null;
|
|
6653
6344
|
let raw2;
|
|
6654
6345
|
try {
|
|
@@ -6711,14 +6402,14 @@ function parsePluginFrontmatter(pluginDir) {
|
|
|
6711
6402
|
function autoDeliverPremiumPlugins(purchasedPlugins) {
|
|
6712
6403
|
if (!purchasedPlugins || purchasedPlugins.length === 0) return;
|
|
6713
6404
|
const TAG19 = "[premium-auto-deliver]";
|
|
6714
|
-
const stagingRoot =
|
|
6715
|
-
const pluginsDir =
|
|
6405
|
+
const stagingRoot = resolve5(PLATFORM_ROOT3, "../premium-plugins");
|
|
6406
|
+
const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
|
|
6716
6407
|
if (!existsSync5(stagingRoot)) {
|
|
6717
6408
|
console.log(`${TAG19} no staging directory \u2014 skipping`);
|
|
6718
6409
|
return;
|
|
6719
6410
|
}
|
|
6720
6411
|
for (const pluginName of purchasedPlugins) {
|
|
6721
|
-
const stagingDir =
|
|
6412
|
+
const stagingDir = resolve5(stagingRoot, pluginName);
|
|
6722
6413
|
if (!existsSync5(stagingDir)) {
|
|
6723
6414
|
console.log(`${TAG19} ${pluginName}: not in staging \u2014 skipping`);
|
|
6724
6415
|
continue;
|
|
@@ -6759,12 +6450,12 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
|
|
|
6759
6450
|
let delivered = 0;
|
|
6760
6451
|
let skipped = 0;
|
|
6761
6452
|
for (const sub of subPlugins) {
|
|
6762
|
-
const target =
|
|
6763
|
-
if (existsSync5(
|
|
6453
|
+
const target = resolve5(pluginsDir, sub);
|
|
6454
|
+
if (existsSync5(resolve5(target, "PLUGIN.md"))) {
|
|
6764
6455
|
skipped++;
|
|
6765
6456
|
continue;
|
|
6766
6457
|
}
|
|
6767
|
-
const source =
|
|
6458
|
+
const source = resolve5(stagingDir, "plugins", sub);
|
|
6768
6459
|
if (!existsSync5(source)) {
|
|
6769
6460
|
console.log(`${TAG19} ${pluginName}/${sub}: source missing in staging \u2014 skipping`);
|
|
6770
6461
|
continue;
|
|
@@ -6778,8 +6469,8 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
|
|
|
6778
6469
|
}
|
|
6779
6470
|
console.log(`${TAG19} ${pluginName} (bundle): ${delivered} delivered, ${skipped} already present`);
|
|
6780
6471
|
} else {
|
|
6781
|
-
const target =
|
|
6782
|
-
if (existsSync5(
|
|
6472
|
+
const target = resolve5(pluginsDir, pluginName);
|
|
6473
|
+
if (existsSync5(resolve5(target, "PLUGIN.md"))) {
|
|
6783
6474
|
console.log(`${TAG19} ${pluginName}: already present \u2014 skipping`);
|
|
6784
6475
|
continue;
|
|
6785
6476
|
}
|
|
@@ -6819,7 +6510,7 @@ function migratePluginRenames(accountDir, config) {
|
|
|
6819
6510
|
return name;
|
|
6820
6511
|
});
|
|
6821
6512
|
if (!changed) return;
|
|
6822
|
-
const configPath2 =
|
|
6513
|
+
const configPath2 = resolve5(accountDir, "account.json");
|
|
6823
6514
|
try {
|
|
6824
6515
|
const raw2 = readFileSync6(configPath2, "utf-8");
|
|
6825
6516
|
const parsed = JSON.parse(raw2);
|
|
@@ -6830,9 +6521,9 @@ function migratePluginRenames(accountDir, config) {
|
|
|
6830
6521
|
} catch (err) {
|
|
6831
6522
|
console.error(`${TAG19} failed to update account.json \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
6832
6523
|
}
|
|
6833
|
-
const pluginsDir =
|
|
6524
|
+
const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
|
|
6834
6525
|
for (const oldName of Object.keys(PLUGIN_RENAMES)) {
|
|
6835
|
-
const orphan =
|
|
6526
|
+
const orphan = resolve5(pluginsDir, oldName);
|
|
6836
6527
|
if (existsSync5(orphan)) {
|
|
6837
6528
|
try {
|
|
6838
6529
|
rmSync2(orphan, { recursive: true });
|
|
@@ -6846,13 +6537,13 @@ function migratePluginRenames(accountDir, config) {
|
|
|
6846
6537
|
function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
|
|
6847
6538
|
if (!purchasedPlugins || purchasedPlugins.length === 0) return;
|
|
6848
6539
|
const TAG19 = "[bundle-agent-deliver]";
|
|
6849
|
-
const stagingRoot =
|
|
6850
|
-
const specialistsDir =
|
|
6540
|
+
const stagingRoot = resolve5(PLATFORM_ROOT3, "../premium-plugins");
|
|
6541
|
+
const specialistsDir = resolve5(accountDir, "specialists", "agents");
|
|
6851
6542
|
if (!existsSync5(stagingRoot)) return;
|
|
6852
6543
|
if (!existsSync5(specialistsDir)) {
|
|
6853
|
-
|
|
6544
|
+
mkdirSync4(specialistsDir, { recursive: true });
|
|
6854
6545
|
}
|
|
6855
|
-
const agentsmdPath =
|
|
6546
|
+
const agentsmdPath = resolve5(accountDir, "agents", "admin", "AGENTS.md");
|
|
6856
6547
|
let agentsmd = "";
|
|
6857
6548
|
try {
|
|
6858
6549
|
agentsmd = existsSync5(agentsmdPath) ? readFileSync6(agentsmdPath, "utf-8") : "";
|
|
@@ -6860,7 +6551,7 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
|
|
|
6860
6551
|
}
|
|
6861
6552
|
let delivered = 0;
|
|
6862
6553
|
for (const pluginName of purchasedPlugins) {
|
|
6863
|
-
const bundleAgentsDir =
|
|
6554
|
+
const bundleAgentsDir = resolve5(stagingRoot, pluginName, "agents");
|
|
6864
6555
|
if (!existsSync5(bundleAgentsDir)) continue;
|
|
6865
6556
|
let entries;
|
|
6866
6557
|
try {
|
|
@@ -6869,9 +6560,9 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
|
|
|
6869
6560
|
continue;
|
|
6870
6561
|
}
|
|
6871
6562
|
for (const filename of entries) {
|
|
6872
|
-
const target =
|
|
6563
|
+
const target = resolve5(specialistsDir, filename);
|
|
6873
6564
|
if (existsSync5(target)) continue;
|
|
6874
|
-
const source =
|
|
6565
|
+
const source = resolve5(bundleAgentsDir, filename);
|
|
6875
6566
|
try {
|
|
6876
6567
|
cpSync(source, target);
|
|
6877
6568
|
} catch (err) {
|
|
@@ -6911,8 +6602,8 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
|
|
|
6911
6602
|
}
|
|
6912
6603
|
}
|
|
6913
6604
|
function assemblePublicPluginContent(pluginDir) {
|
|
6914
|
-
const pluginRoot =
|
|
6915
|
-
const pluginPath =
|
|
6605
|
+
const pluginRoot = resolve5(PLATFORM_ROOT3, "plugins", pluginDir);
|
|
6606
|
+
const pluginPath = resolve5(pluginRoot, "PLUGIN.md");
|
|
6916
6607
|
let raw2;
|
|
6917
6608
|
try {
|
|
6918
6609
|
raw2 = readFileSync6(pluginPath, "utf-8");
|
|
@@ -6924,7 +6615,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
6924
6615
|
const parts = [pluginBody];
|
|
6925
6616
|
let skillCount = 0;
|
|
6926
6617
|
let refCount = 0;
|
|
6927
|
-
const skillsDir =
|
|
6618
|
+
const skillsDir = resolve5(pluginRoot, "skills");
|
|
6928
6619
|
let skillDirs;
|
|
6929
6620
|
try {
|
|
6930
6621
|
skillDirs = readdirSync2(skillsDir).sort();
|
|
@@ -6932,8 +6623,8 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
6932
6623
|
return { body: pluginBody, skillCount: 0, refCount: 0 };
|
|
6933
6624
|
}
|
|
6934
6625
|
for (const skillName of skillDirs) {
|
|
6935
|
-
const skillDir =
|
|
6936
|
-
const skillMdPath =
|
|
6626
|
+
const skillDir = resolve5(skillsDir, skillName);
|
|
6627
|
+
const skillMdPath = resolve5(skillDir, "SKILL.md");
|
|
6937
6628
|
let skillRaw;
|
|
6938
6629
|
try {
|
|
6939
6630
|
skillRaw = readFileSync6(skillMdPath, "utf-8");
|
|
@@ -6976,7 +6667,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
6976
6667
|
parts.push(`
|
|
6977
6668
|
<!-- skill: ${skillName} -->`);
|
|
6978
6669
|
parts.push(skillBody);
|
|
6979
|
-
const refsDir =
|
|
6670
|
+
const refsDir = resolve5(skillDir, "references");
|
|
6980
6671
|
let refFiles;
|
|
6981
6672
|
try {
|
|
6982
6673
|
refFiles = readdirSync2(refsDir).filter((f) => f.endsWith(".md")).filter((f) => !publicExcludeReferences.includes(f)).sort();
|
|
@@ -6987,7 +6678,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
6987
6678
|
}
|
|
6988
6679
|
for (const refFile of refFiles) {
|
|
6989
6680
|
try {
|
|
6990
|
-
const refContent = readFileSync6(
|
|
6681
|
+
const refContent = readFileSync6(resolve5(refsDir, refFile), "utf-8").trim();
|
|
6991
6682
|
if (refContent) {
|
|
6992
6683
|
parts.push(`
|
|
6993
6684
|
<!-- reference: ${refFile} -->`);
|
|
@@ -7005,7 +6696,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
7005
6696
|
return { body: parts.join("\n"), skillCount, refCount };
|
|
7006
6697
|
}
|
|
7007
6698
|
function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
|
|
7008
|
-
const pluginsDir =
|
|
6699
|
+
const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
|
|
7009
6700
|
let dirs;
|
|
7010
6701
|
try {
|
|
7011
6702
|
dirs = readdirSync2(pluginsDir);
|
|
@@ -7058,7 +6749,7 @@ function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
|
|
|
7058
6749
|
console.log(`[plugins] loaded ${dir} for public (${assembled.body.length} chars, ${assembled.skillCount} skills, ${assembled.refCount} refs)`);
|
|
7059
6750
|
}
|
|
7060
6751
|
} else {
|
|
7061
|
-
const pluginPath =
|
|
6752
|
+
const pluginPath = resolve5(pluginsDir, dir, "PLUGIN.md");
|
|
7062
6753
|
let raw2;
|
|
7063
6754
|
try {
|
|
7064
6755
|
raw2 = readFileSync6(pluginPath, "utf-8");
|
|
@@ -7085,7 +6776,7 @@ var mcpToolsCache = /* @__PURE__ */ new Map();
|
|
|
7085
6776
|
function fetchMcpToolsList(pluginDir) {
|
|
7086
6777
|
const cached = mcpToolsCache.get(pluginDir);
|
|
7087
6778
|
if (cached) return Promise.resolve(cached);
|
|
7088
|
-
const serverPath =
|
|
6779
|
+
const serverPath = resolve5(PLATFORM_ROOT3, "plugins", pluginDir, "mcp/dist/index.js");
|
|
7089
6780
|
if (!existsSync5(serverPath)) return Promise.resolve([]);
|
|
7090
6781
|
const startMs = Date.now();
|
|
7091
6782
|
return new Promise((resolvePromise) => {
|
|
@@ -7198,7 +6889,7 @@ var SPECIALIST_PLUGIN_DOMAINS = {
|
|
|
7198
6889
|
// agent, so it retains a full manifest entry for routing clarity.
|
|
7199
6890
|
};
|
|
7200
6891
|
async function buildPluginManifest(enabledPlugins) {
|
|
7201
|
-
const pluginsDir =
|
|
6892
|
+
const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
|
|
7202
6893
|
let dirs;
|
|
7203
6894
|
try {
|
|
7204
6895
|
dirs = readdirSync2(pluginsDir);
|
|
@@ -7285,16 +6976,16 @@ ${specialist}: ${plugins.join(", ")}`);
|
|
|
7285
6976
|
for (let j = 0; j < adminPlugins.length; j++) {
|
|
7286
6977
|
const { dir, parsed } = adminPlugins[j];
|
|
7287
6978
|
const mcpTools = adminMcpResults[j];
|
|
7288
|
-
const pluginRoot =
|
|
6979
|
+
const pluginRoot = resolve5(pluginsDir, dir);
|
|
7289
6980
|
const skills = [];
|
|
7290
6981
|
const references = [];
|
|
7291
6982
|
const scanDir = (base, prefix, target) => {
|
|
7292
|
-
const scanPath =
|
|
6983
|
+
const scanPath = resolve5(pluginRoot, base);
|
|
7293
6984
|
if (!existsSync5(scanPath)) return;
|
|
7294
6985
|
try {
|
|
7295
6986
|
const walk = (current, rel) => {
|
|
7296
6987
|
for (const entry of readdirSync2(current)) {
|
|
7297
|
-
const full =
|
|
6988
|
+
const full = resolve5(current, entry);
|
|
7298
6989
|
try {
|
|
7299
6990
|
const stat5 = statSync3(full);
|
|
7300
6991
|
if (stat5.isDirectory()) {
|
|
@@ -7322,7 +7013,7 @@ ${specialist}: ${plugins.join(", ")}`);
|
|
|
7322
7013
|
toolLines.push(desc ? ` ${tool.name} \u2014 ${desc}` : ` ${tool.name}`);
|
|
7323
7014
|
}
|
|
7324
7015
|
} else if (parsed.tools.length > 0) {
|
|
7325
|
-
const serverPath =
|
|
7016
|
+
const serverPath = resolve5(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
|
|
7326
7017
|
if (existsSync5(serverPath)) {
|
|
7327
7018
|
fallbackSourced++;
|
|
7328
7019
|
console.error(`[plugin-manifest] ${dir}: tools/list empty \u2014 fallback to frontmatter (${parsed.tools.length} tools)`);
|
|
@@ -7403,7 +7094,7 @@ function resolveUserAccounts(userId) {
|
|
|
7403
7094
|
const entries = readdirSync2(ACCOUNTS_DIR, { withFileTypes: true });
|
|
7404
7095
|
for (const entry of entries) {
|
|
7405
7096
|
if (!entry.isDirectory()) continue;
|
|
7406
|
-
const configPath2 =
|
|
7097
|
+
const configPath2 = resolve5(ACCOUNTS_DIR, entry.name, "account.json");
|
|
7407
7098
|
if (!existsSync5(configPath2)) continue;
|
|
7408
7099
|
let config;
|
|
7409
7100
|
try {
|
|
@@ -7416,7 +7107,7 @@ function resolveUserAccounts(userId) {
|
|
|
7416
7107
|
if (adminEntry) {
|
|
7417
7108
|
results.push({
|
|
7418
7109
|
accountId: config.accountId,
|
|
7419
|
-
accountDir:
|
|
7110
|
+
accountDir: resolve5(ACCOUNTS_DIR, entry.name),
|
|
7420
7111
|
config,
|
|
7421
7112
|
role: adminEntry.role
|
|
7422
7113
|
});
|
|
@@ -7633,8 +7324,8 @@ function consumeStalledSubagents(sessionKey) {
|
|
|
7633
7324
|
return stalls && stalls.length > 0 ? stalls : void 0;
|
|
7634
7325
|
}
|
|
7635
7326
|
function streamLogPathFor(accountId, conversationId) {
|
|
7636
|
-
const logDir =
|
|
7637
|
-
const streamLogPath =
|
|
7327
|
+
const logDir = resolve5(ACCOUNTS_DIR, accountId, "logs");
|
|
7328
|
+
const streamLogPath = resolve5(logDir, `claude-agent-stream-${conversationId}.log`);
|
|
7638
7329
|
return { logDir, streamLogPath };
|
|
7639
7330
|
}
|
|
7640
7331
|
function buildSpawnEnv(accountId, accountDir, conversationId) {
|
|
@@ -7655,7 +7346,7 @@ var cachedBrandHostname = null;
|
|
|
7655
7346
|
function readBrandHostname() {
|
|
7656
7347
|
if (cachedBrandHostname !== null) return cachedBrandHostname;
|
|
7657
7348
|
try {
|
|
7658
|
-
const brandPath =
|
|
7349
|
+
const brandPath = resolve5(PLATFORM_ROOT3, "config", "brand.json");
|
|
7659
7350
|
const parsed = JSON.parse(readFileSync6(brandPath, "utf-8"));
|
|
7660
7351
|
cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
|
|
7661
7352
|
} catch {
|
|
@@ -7693,37 +7384,37 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7693
7384
|
const servers = {
|
|
7694
7385
|
"memory": {
|
|
7695
7386
|
command: "node",
|
|
7696
|
-
args: [
|
|
7387
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js")],
|
|
7697
7388
|
env: { ...baseEnv, ...userId ? { USER_ID: userId } : {} }
|
|
7698
7389
|
},
|
|
7699
7390
|
"contacts": {
|
|
7700
7391
|
command: "node",
|
|
7701
|
-
args: [
|
|
7392
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/contacts/mcp/dist/index.js")],
|
|
7702
7393
|
env: { ...baseEnv }
|
|
7703
7394
|
},
|
|
7704
7395
|
"whatsapp": {
|
|
7705
7396
|
command: "node",
|
|
7706
|
-
args: [
|
|
7397
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/whatsapp/mcp/dist/index.js")],
|
|
7707
7398
|
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
|
|
7708
7399
|
},
|
|
7709
7400
|
"admin": {
|
|
7710
7401
|
command: "node",
|
|
7711
|
-
args: [
|
|
7402
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/admin/mcp/dist/index.js")],
|
|
7712
7403
|
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200", ...userId ? { USER_ID: userId } : {} }
|
|
7713
7404
|
},
|
|
7714
7405
|
"scheduling": {
|
|
7715
7406
|
command: "node",
|
|
7716
|
-
args: [
|
|
7407
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/scheduling/mcp/dist/index.js")],
|
|
7717
7408
|
env: { ...baseEnv }
|
|
7718
7409
|
},
|
|
7719
7410
|
"tasks": {
|
|
7720
7411
|
command: "node",
|
|
7721
|
-
args: [
|
|
7412
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/tasks/mcp/dist/index.js")],
|
|
7722
7413
|
env: { ...baseEnv }
|
|
7723
7414
|
},
|
|
7724
7415
|
"email": {
|
|
7725
7416
|
command: "node",
|
|
7726
|
-
args: [
|
|
7417
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/email/mcp/dist/index.js")],
|
|
7727
7418
|
env: { ...baseEnv }
|
|
7728
7419
|
},
|
|
7729
7420
|
// Workflows MCP — persistent admin-session server for list/get/update/delete/
|
|
@@ -7734,7 +7425,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7734
7425
|
// ToolSearches fruitlessly before degrading to a task-create stand-in (Task 571).
|
|
7735
7426
|
"workflows": {
|
|
7736
7427
|
command: "node",
|
|
7737
|
-
args: [
|
|
7428
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/workflows/mcp/dist/index.js")],
|
|
7738
7429
|
env: { ...baseEnv }
|
|
7739
7430
|
},
|
|
7740
7431
|
// Playwright MCP server — browser automation for browser-specialist.
|
|
@@ -7756,7 +7447,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7756
7447
|
// MAXY-PRD.md:627, not in any application-layer filter).
|
|
7757
7448
|
"graph": {
|
|
7758
7449
|
command: "node",
|
|
7759
|
-
args: [
|
|
7450
|
+
args: [resolve5(PLATFORM_ROOT3, "lib/graph-mcp/dist/index.js")],
|
|
7760
7451
|
env: {
|
|
7761
7452
|
...baseEnv,
|
|
7762
7453
|
BRAND: readBrandHostname(),
|
|
@@ -7772,7 +7463,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7772
7463
|
if (tgBotToken) {
|
|
7773
7464
|
servers["telegram"] = {
|
|
7774
7465
|
command: "node",
|
|
7775
|
-
args: [
|
|
7466
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/telegram/mcp/dist/index.js")],
|
|
7776
7467
|
env: { ...baseEnv, TELEGRAM_BOT_TOKEN: tgBotToken }
|
|
7777
7468
|
};
|
|
7778
7469
|
} else {
|
|
@@ -7780,11 +7471,11 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7780
7471
|
}
|
|
7781
7472
|
servers["cloudflare"] = {
|
|
7782
7473
|
command: "node",
|
|
7783
|
-
args: [
|
|
7474
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/cloudflare/mcp/dist/index.js")],
|
|
7784
7475
|
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
|
|
7785
7476
|
};
|
|
7786
7477
|
if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
|
|
7787
|
-
const pluginsDir =
|
|
7478
|
+
const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
|
|
7788
7479
|
let dirs;
|
|
7789
7480
|
try {
|
|
7790
7481
|
dirs = readdirSync2(pluginsDir);
|
|
@@ -7807,7 +7498,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7807
7498
|
continue;
|
|
7808
7499
|
}
|
|
7809
7500
|
}
|
|
7810
|
-
const mcpEntry =
|
|
7501
|
+
const mcpEntry = resolve5(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
|
|
7811
7502
|
if (!existsSync5(mcpEntry)) continue;
|
|
7812
7503
|
servers[dir] = {
|
|
7813
7504
|
command: "node",
|
|
@@ -7943,7 +7634,7 @@ var ADMIN_CORE_TOOLS = [
|
|
|
7943
7634
|
function getAdminAllowedTools(enabledPlugins) {
|
|
7944
7635
|
const tools = [...ADMIN_CORE_TOOLS];
|
|
7945
7636
|
if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
|
|
7946
|
-
const pluginsDir =
|
|
7637
|
+
const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
|
|
7947
7638
|
let dirs;
|
|
7948
7639
|
try {
|
|
7949
7640
|
dirs = readdirSync2(pluginsDir);
|
|
@@ -8051,13 +7742,13 @@ ${message.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`
|
|
|
8051
7742
|
}
|
|
8052
7743
|
}
|
|
8053
7744
|
async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
8054
|
-
const serverPath =
|
|
7745
|
+
const serverPath = resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
|
|
8055
7746
|
if (!existsSync5(serverPath)) {
|
|
8056
7747
|
console.error(`[fetchMemoryContext] MCP server not found: ${serverPath}`);
|
|
8057
7748
|
return null;
|
|
8058
7749
|
}
|
|
8059
7750
|
const startMs = Date.now();
|
|
8060
|
-
return new Promise((
|
|
7751
|
+
return new Promise((resolve28) => {
|
|
8061
7752
|
const proc = spawn2(process.execPath, [serverPath], {
|
|
8062
7753
|
env: {
|
|
8063
7754
|
...process.env,
|
|
@@ -8086,7 +7777,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
|
8086
7777
|
} else {
|
|
8087
7778
|
console.error(`[fetchMemoryContext] failed: ${reason} (${elapsed}ms)${stderrBuf ? ` stderr: ${stderrBuf.slice(0, 500)}` : ""}`);
|
|
8088
7779
|
}
|
|
8089
|
-
|
|
7780
|
+
resolve28(value);
|
|
8090
7781
|
};
|
|
8091
7782
|
proc.stdout.on("data", (chunk) => {
|
|
8092
7783
|
buffer += chunk.toString();
|
|
@@ -8149,7 +7840,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
|
8149
7840
|
}
|
|
8150
7841
|
async function compactTrimmedMessages(accountId, trimmedMessages) {
|
|
8151
7842
|
if (trimmedMessages.length === 0) return true;
|
|
8152
|
-
const serverPath =
|
|
7843
|
+
const serverPath = resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
|
|
8153
7844
|
if (!existsSync5(serverPath)) return false;
|
|
8154
7845
|
const briefing = trimmedMessages.map((m) => `[${m.role.toUpperCase()}] ${m.content}`).join("\n\n");
|
|
8155
7846
|
return new Promise((resolvePromise) => {
|
|
@@ -8467,7 +8158,7 @@ Then respond with only: [COMPACTED]`;
|
|
|
8467
8158
|
var COMPACTION_TIMEOUT_MS = 45e3;
|
|
8468
8159
|
async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, conversationId, enabledPlugins) {
|
|
8469
8160
|
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, conversationId, void 0, enabledPlugins) });
|
|
8470
|
-
const specialistsDir =
|
|
8161
|
+
const specialistsDir = resolve5(accountDir, "specialists");
|
|
8471
8162
|
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir, conversationId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
8472
8163
|
`);
|
|
8473
8164
|
const args = [
|
|
@@ -8741,7 +8432,7 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId, a
|
|
|
8741
8432
|
const { logDir } = streamLogPathFor(accountId, conversationId);
|
|
8742
8433
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
8743
8434
|
for (const s of failed) {
|
|
8744
|
-
const stderrPath =
|
|
8435
|
+
const stderrPath = resolve5(logDir, `mcp-${s.name}-stderr-${date}.log`);
|
|
8745
8436
|
let tail = "(no stderr file)";
|
|
8746
8437
|
try {
|
|
8747
8438
|
const stats = statSync3(stderrPath);
|
|
@@ -9383,7 +9074,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9383
9074
|
}
|
|
9384
9075
|
const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
|
|
9385
9076
|
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, spawnConvId, ccUserId, enabledPlugins) });
|
|
9386
|
-
const specialistsDir =
|
|
9077
|
+
const specialistsDir = resolve5(accountDir, "specialists");
|
|
9387
9078
|
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnConvId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9388
9079
|
`);
|
|
9389
9080
|
const args = [
|
|
@@ -9734,7 +9425,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9734
9425
|
`);
|
|
9735
9426
|
const managedUserId = getUserIdForSession(sessionKey);
|
|
9736
9427
|
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedConvId, managedUserId, enabledPlugins) });
|
|
9737
|
-
const specialistsDir =
|
|
9428
|
+
const specialistsDir = resolve5(accountDir, "specialists");
|
|
9738
9429
|
if (!existsSync5(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9739
9430
|
`);
|
|
9740
9431
|
const fullMessage = attachments.length > 0 ? message + buildAttachmentMetaText(attachments) : message;
|
|
@@ -10428,7 +10119,7 @@ ${sessionContext}`;
|
|
|
10428
10119
|
console.log(`[onboarding-inject] accountId=${accountId.slice(0, 8)}\u2026 error=neo4j-unreachable injected=false`);
|
|
10429
10120
|
} else if (onboardingStep < 8) {
|
|
10430
10121
|
const GENERIC_FALLBACK = "At every session start, call `onboarding-get`. If `currentStep` is less than 8, load the onboarding skill via `plugin-read` (find its path in the manifest under `admin`) and follow it \u2014 before any business setup. If `onboarding-get` fails (Neo4j unreachable), tell the user and skip onboarding for this session \u2014 it resumes automatically when the graph is available.";
|
|
10431
|
-
const skillPath =
|
|
10122
|
+
const skillPath = resolve5(PLATFORM_ROOT3, "plugins/admin/skills/onboarding/SKILL.md");
|
|
10432
10123
|
let skillContent = "";
|
|
10433
10124
|
try {
|
|
10434
10125
|
skillContent = readFileSync6(skillPath, "utf-8");
|
|
@@ -10481,7 +10172,7 @@ ${body}`;
|
|
|
10481
10172
|
|
|
10482
10173
|
${manifest}`;
|
|
10483
10174
|
}
|
|
10484
|
-
const graphRefPath =
|
|
10175
|
+
const graphRefPath = resolve5(PLATFORM_ROOT3, "plugins/memory/references/graph-primitives.md");
|
|
10485
10176
|
try {
|
|
10486
10177
|
const graphRef = readFileSync6(graphRefPath, "utf-8");
|
|
10487
10178
|
baseSystemPrompt += `
|
|
@@ -10699,7 +10390,7 @@ var clientIpMiddleware = async (c, next) => {
|
|
|
10699
10390
|
|
|
10700
10391
|
// server/routes/health.ts
|
|
10701
10392
|
import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
|
|
10702
|
-
import { createConnection as
|
|
10393
|
+
import { createConnection as createConnection2 } from "net";
|
|
10703
10394
|
|
|
10704
10395
|
// app/lib/network.ts
|
|
10705
10396
|
import { networkInterfaces } from "os";
|
|
@@ -10723,8 +10414,8 @@ function getLanIp() {
|
|
|
10723
10414
|
import { basename as basename2 } from "path";
|
|
10724
10415
|
|
|
10725
10416
|
// app/lib/review-detector/rules.ts
|
|
10726
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, statSync as statSync4, mkdirSync as
|
|
10727
|
-
import { resolve as
|
|
10417
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, statSync as statSync4, mkdirSync as mkdirSync5, renameSync as renameSync2 } from "fs";
|
|
10418
|
+
import { resolve as resolve6, dirname as dirname3 } from "path";
|
|
10728
10419
|
var DEFAULT_SCAN_INTERVAL_MS = 5e3;
|
|
10729
10420
|
var RATE_LIMIT_PATTERN = "rate[- ]?limit(?:ed| reached| hit)|(?:HTTP|status)[^a-z]{0,3}429|too many requests";
|
|
10730
10421
|
var RATE_LIMIT_PATTERN_V1 = "\\b429\\b|rate.?limit|too.?many.?requests";
|
|
@@ -11153,12 +10844,12 @@ function defaultRules() {
|
|
|
11153
10844
|
];
|
|
11154
10845
|
}
|
|
11155
10846
|
function rulesFilePath(configDir2) {
|
|
11156
|
-
return
|
|
10847
|
+
return resolve6(configDir2, "review-rules.json");
|
|
11157
10848
|
}
|
|
11158
10849
|
function ensureRulesFile(configDir2) {
|
|
11159
10850
|
const path2 = rulesFilePath(configDir2);
|
|
11160
10851
|
if (existsSync6(path2)) return { created: false, path: path2 };
|
|
11161
|
-
|
|
10852
|
+
mkdirSync5(dirname3(path2), { recursive: true });
|
|
11162
10853
|
const body = {
|
|
11163
10854
|
scanIntervalMs: DEFAULT_SCAN_INTERVAL_MS,
|
|
11164
10855
|
rules: defaultRules()
|
|
@@ -11336,10 +11027,10 @@ function validateRule(input, label, seenIds) {
|
|
|
11336
11027
|
}
|
|
11337
11028
|
|
|
11338
11029
|
// app/lib/review-detector/sources.ts
|
|
11339
|
-
import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync3, mkdirSync as
|
|
11340
|
-
import { resolve as
|
|
11030
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync3, mkdirSync as mkdirSync6, openSync as openSync3, readSync as readSync3, closeSync as closeSync3, readFileSync as readFileSync8 } from "fs";
|
|
11031
|
+
import { resolve as resolve7, join as join4, basename, dirname as dirname4 } from "path";
|
|
11341
11032
|
function tailStatePath(configDir2) {
|
|
11342
|
-
return
|
|
11033
|
+
return resolve7(configDir2, "review-state.json");
|
|
11343
11034
|
}
|
|
11344
11035
|
function loadTailState(configDir2) {
|
|
11345
11036
|
const path2 = tailStatePath(configDir2);
|
|
@@ -11363,25 +11054,25 @@ function loadTailState(configDir2) {
|
|
|
11363
11054
|
}
|
|
11364
11055
|
function saveTailState(configDir2, state) {
|
|
11365
11056
|
const path2 = tailStatePath(configDir2);
|
|
11366
|
-
|
|
11057
|
+
mkdirSync6(dirname4(path2), { recursive: true });
|
|
11367
11058
|
const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
|
|
11368
11059
|
writeFileSync7(tmp, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
11369
11060
|
renameSync3(tmp, path2);
|
|
11370
11061
|
}
|
|
11371
11062
|
function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
|
|
11372
11063
|
if (logicalSource === "server") {
|
|
11373
|
-
const p =
|
|
11064
|
+
const p = resolve7(configDir2, "logs", "server.log");
|
|
11374
11065
|
return existsSync7(p) ? [{ logicalSource: "server", filepath: p }] : [];
|
|
11375
11066
|
}
|
|
11376
11067
|
if (logicalSource === "vnc") {
|
|
11377
|
-
const p =
|
|
11068
|
+
const p = resolve7(configDir2, "logs", "vnc-boot.log");
|
|
11378
11069
|
return existsSync7(p) ? [{ logicalSource: "vnc", filepath: p }] : [];
|
|
11379
11070
|
}
|
|
11380
11071
|
if (logicalSource === "cloudflared") {
|
|
11381
11072
|
const files2 = [];
|
|
11382
|
-
const daemon =
|
|
11073
|
+
const daemon = resolve7(configDir2, "logs", "cloudflared.log");
|
|
11383
11074
|
if (existsSync7(daemon)) files2.push({ logicalSource: "cloudflared", filepath: daemon });
|
|
11384
|
-
const login =
|
|
11075
|
+
const login = resolve7(configDir2, "logs", "cloudflared-login.log");
|
|
11385
11076
|
if (existsSync7(login)) files2.push({ logicalSource: "cloudflared", filepath: login });
|
|
11386
11077
|
return files2;
|
|
11387
11078
|
}
|
|
@@ -11508,31 +11199,31 @@ function fileLastWriteMs(path2) {
|
|
|
11508
11199
|
}
|
|
11509
11200
|
}
|
|
11510
11201
|
function accountLogDir(accountDir) {
|
|
11511
|
-
return
|
|
11202
|
+
return resolve7(accountDir, "logs");
|
|
11512
11203
|
}
|
|
11513
11204
|
function sourceKey(file) {
|
|
11514
11205
|
return `${file.logicalSource}:${basename(file.filepath)}`;
|
|
11515
11206
|
}
|
|
11516
11207
|
|
|
11517
11208
|
// app/lib/review-detector/writer.ts
|
|
11518
|
-
import { appendFileSync as
|
|
11519
|
-
import { resolve as
|
|
11209
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync8, renameSync as renameSync4, statSync as statSync6 } from "fs";
|
|
11210
|
+
import { resolve as resolve8, dirname as dirname5 } from "path";
|
|
11520
11211
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
11521
11212
|
function reviewLogPath(configDir2) {
|
|
11522
|
-
return
|
|
11213
|
+
return resolve8(configDir2, "logs", "review.log");
|
|
11523
11214
|
}
|
|
11524
11215
|
function pendingAlertsPath(configDir2) {
|
|
11525
|
-
return
|
|
11216
|
+
return resolve8(configDir2, "review-pending-alerts.jsonl");
|
|
11526
11217
|
}
|
|
11527
11218
|
function reviewLog(configDir2, event) {
|
|
11528
11219
|
const path2 = reviewLogPath(configDir2);
|
|
11529
11220
|
try {
|
|
11530
|
-
|
|
11221
|
+
mkdirSync7(dirname5(path2), { recursive: true });
|
|
11531
11222
|
const line = `${new Date(
|
|
11532
11223
|
typeof event.ts === "number" ? event.ts : Date.now()
|
|
11533
11224
|
).toISOString()} [review] ${JSON.stringify(event)}
|
|
11534
11225
|
`;
|
|
11535
|
-
|
|
11226
|
+
appendFileSync2(path2, line, "utf-8");
|
|
11536
11227
|
} catch (err) {
|
|
11537
11228
|
console.error(`[review] failed to write review log at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11538
11229
|
}
|
|
@@ -11644,9 +11335,9 @@ async function upsertReviewAlert(accountId, match2) {
|
|
|
11644
11335
|
function queueAlert(configDir2, accountId, match2) {
|
|
11645
11336
|
const path2 = pendingAlertsPath(configDir2);
|
|
11646
11337
|
try {
|
|
11647
|
-
|
|
11338
|
+
mkdirSync7(dirname5(path2), { recursive: true });
|
|
11648
11339
|
const line = JSON.stringify({ accountId, match: match2 }) + "\n";
|
|
11649
|
-
|
|
11340
|
+
appendFileSync2(path2, line, "utf-8");
|
|
11650
11341
|
} catch (err) {
|
|
11651
11342
|
console.error(`[review] failed to queue alert at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11652
11343
|
}
|
|
@@ -11777,7 +11468,7 @@ async function bootDetector() {
|
|
|
11777
11468
|
}
|
|
11778
11469
|
|
|
11779
11470
|
// app/lib/review-detector/scan-loop.ts
|
|
11780
|
-
import { resolve as
|
|
11471
|
+
import { resolve as resolve9 } from "path";
|
|
11781
11472
|
|
|
11782
11473
|
// app/lib/review-detector/evaluator.ts
|
|
11783
11474
|
var SAMPLE_MAX_CHARS = 500;
|
|
@@ -12071,14 +11762,14 @@ async function runScanCycle(runtime) {
|
|
|
12071
11762
|
match2 = result.match;
|
|
12072
11763
|
}
|
|
12073
11764
|
} else if (rule.type === "file-write-storm") {
|
|
12074
|
-
const dir =
|
|
11765
|
+
const dir = resolve9(runtime.configDir, rule.watchPath ?? "");
|
|
12075
11766
|
const sinceMs = cycleStart - rule.thresholdWindowMinutes * 6e4;
|
|
12076
11767
|
const count = countRecentWrites(dir, sinceMs);
|
|
12077
11768
|
const result = evaluateFileWriteStormRule(rule, count, state, cycleStart);
|
|
12078
11769
|
state = result.state;
|
|
12079
11770
|
match2 = result.match;
|
|
12080
11771
|
} else if (rule.type === "stale-log") {
|
|
12081
|
-
const trackedPath =
|
|
11772
|
+
const trackedPath = resolve9(runtime.configDir, rule.watchPath ?? "");
|
|
12082
11773
|
const lastMs = fileLastWriteMs(trackedPath);
|
|
12083
11774
|
const result = evaluateStaleLogRule(rule, lastMs, state, cycleStart);
|
|
12084
11775
|
state = result.state;
|
|
@@ -12318,10 +12009,10 @@ var WhatsAppConfigSchema = z.object({
|
|
|
12318
12009
|
|
|
12319
12010
|
// app/lib/whatsapp/config-persist.ts
|
|
12320
12011
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "fs";
|
|
12321
|
-
import { resolve as
|
|
12012
|
+
import { resolve as resolve10, join as join5 } from "path";
|
|
12322
12013
|
var TAG3 = "[whatsapp:config]";
|
|
12323
12014
|
function configPath(accountDir) {
|
|
12324
|
-
return
|
|
12015
|
+
return resolve10(accountDir, "account.json");
|
|
12325
12016
|
}
|
|
12326
12017
|
function readConfig(accountDir) {
|
|
12327
12018
|
const path2 = configPath(accountDir);
|
|
@@ -12776,7 +12467,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
12776
12467
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
12777
12468
|
console.error(`${TAG5} draining credential save queue\u2026`);
|
|
12778
12469
|
const timer = new Promise(
|
|
12779
|
-
(
|
|
12470
|
+
(resolve28) => setTimeout(() => resolve28("timeout"), timeoutMs)
|
|
12780
12471
|
);
|
|
12781
12472
|
const result = await Promise.race([
|
|
12782
12473
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -12904,11 +12595,11 @@ async function createWaSocket(opts) {
|
|
|
12904
12595
|
return sock;
|
|
12905
12596
|
}
|
|
12906
12597
|
async function waitForConnection(sock) {
|
|
12907
|
-
return new Promise((
|
|
12598
|
+
return new Promise((resolve28, reject) => {
|
|
12908
12599
|
const handler = (update) => {
|
|
12909
12600
|
if (update.connection === "open") {
|
|
12910
12601
|
sock.ev.off("connection.update", handler);
|
|
12911
|
-
|
|
12602
|
+
resolve28();
|
|
12912
12603
|
}
|
|
12913
12604
|
if (update.connection === "close") {
|
|
12914
12605
|
sock.ev.off("connection.update", handler);
|
|
@@ -13022,14 +12713,14 @@ ${inspected}`;
|
|
|
13022
12713
|
return inspect2(err, INSPECT_OPTS2);
|
|
13023
12714
|
}
|
|
13024
12715
|
function withTimeout(label, promise, timeoutMs) {
|
|
13025
|
-
return new Promise((
|
|
12716
|
+
return new Promise((resolve28, reject) => {
|
|
13026
12717
|
const timer = setTimeout(() => {
|
|
13027
12718
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
13028
12719
|
}, timeoutMs);
|
|
13029
12720
|
promise.then(
|
|
13030
12721
|
(value) => {
|
|
13031
12722
|
clearTimeout(timer);
|
|
13032
|
-
|
|
12723
|
+
resolve28(value);
|
|
13033
12724
|
},
|
|
13034
12725
|
(err) => {
|
|
13035
12726
|
clearTimeout(timer);
|
|
@@ -13971,6 +13662,14 @@ function deriveSessionKey(input) {
|
|
|
13971
13662
|
if (input.isOwnerMirror || input.agentType === "admin") {
|
|
13972
13663
|
return `whatsapp:${input.accountId}`;
|
|
13973
13664
|
}
|
|
13665
|
+
if (input.isGroup) {
|
|
13666
|
+
if (!input.groupJid) {
|
|
13667
|
+
throw new Error(
|
|
13668
|
+
`deriveSessionKey: isGroup=true requires groupJid (accountId=${input.accountId}, senderPhone=${input.senderPhone})`
|
|
13669
|
+
);
|
|
13670
|
+
}
|
|
13671
|
+
return `whatsapp:${input.accountId}:group:${input.groupJid}`;
|
|
13672
|
+
}
|
|
13974
13673
|
return `whatsapp:${input.accountId}:${input.senderPhone}`;
|
|
13975
13674
|
}
|
|
13976
13675
|
async function init(opts) {
|
|
@@ -14235,11 +13934,11 @@ async function connectWithReconnect(conn) {
|
|
|
14235
13934
|
console.error(
|
|
14236
13935
|
`${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
14237
13936
|
);
|
|
14238
|
-
await new Promise((
|
|
14239
|
-
const timer = setTimeout(
|
|
13937
|
+
await new Promise((resolve28) => {
|
|
13938
|
+
const timer = setTimeout(resolve28, delay);
|
|
14240
13939
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
14241
13940
|
clearTimeout(timer);
|
|
14242
|
-
|
|
13941
|
+
resolve28();
|
|
14243
13942
|
}, { once: true });
|
|
14244
13943
|
});
|
|
14245
13944
|
}
|
|
@@ -14247,16 +13946,16 @@ async function connectWithReconnect(conn) {
|
|
|
14247
13946
|
}
|
|
14248
13947
|
}
|
|
14249
13948
|
function waitForDisconnectEvent(conn) {
|
|
14250
|
-
return new Promise((
|
|
13949
|
+
return new Promise((resolve28) => {
|
|
14251
13950
|
if (!conn.sock) {
|
|
14252
|
-
|
|
13951
|
+
resolve28();
|
|
14253
13952
|
return;
|
|
14254
13953
|
}
|
|
14255
13954
|
const sock = conn.sock;
|
|
14256
13955
|
const handler = (update) => {
|
|
14257
13956
|
if (update.connection === "close") {
|
|
14258
13957
|
sock.ev.off("connection.update", handler);
|
|
14259
|
-
|
|
13958
|
+
resolve28();
|
|
14260
13959
|
}
|
|
14261
13960
|
};
|
|
14262
13961
|
sock.ev.on("connection.update", handler);
|
|
@@ -14442,6 +14141,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
14442
14141
|
agentType: "admin",
|
|
14443
14142
|
accountId: conn.accountId,
|
|
14444
14143
|
senderPhone: senderPhone2,
|
|
14144
|
+
isGroup: isGroup2,
|
|
14145
|
+
groupJid: isGroup2 ? remoteJid : void 0,
|
|
14445
14146
|
isOwnerMirror: true
|
|
14446
14147
|
}),
|
|
14447
14148
|
isOwnerMirror: true
|
|
@@ -14471,8 +14172,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
14471
14172
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
14472
14173
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
14473
14174
|
let resolvePending;
|
|
14474
|
-
const sttPending = new Promise((
|
|
14475
|
-
resolvePending =
|
|
14175
|
+
const sttPending = new Promise((resolve28) => {
|
|
14176
|
+
resolvePending = resolve28;
|
|
14476
14177
|
});
|
|
14477
14178
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
14478
14179
|
try {
|
|
@@ -14525,7 +14226,9 @@ async function handleInboundMessage(conn, msg) {
|
|
|
14525
14226
|
const sessionKey = deriveSessionKey({
|
|
14526
14227
|
agentType: accessResult.agentType,
|
|
14527
14228
|
accountId: conn.accountId,
|
|
14528
|
-
senderPhone
|
|
14229
|
+
senderPhone,
|
|
14230
|
+
isGroup,
|
|
14231
|
+
groupJid: isGroup ? remoteJid : void 0
|
|
14529
14232
|
});
|
|
14530
14233
|
conn.lastMessageAt = Date.now();
|
|
14531
14234
|
const payload = {
|
|
@@ -14583,37 +14286,23 @@ async function probeApiKey() {
|
|
|
14583
14286
|
return result.status;
|
|
14584
14287
|
}
|
|
14585
14288
|
function checkPort(port2, timeoutMs = 500) {
|
|
14586
|
-
return new Promise((
|
|
14587
|
-
const socket =
|
|
14289
|
+
return new Promise((resolve28) => {
|
|
14290
|
+
const socket = createConnection2(port2, "127.0.0.1");
|
|
14588
14291
|
socket.setTimeout(timeoutMs);
|
|
14589
14292
|
socket.once("connect", () => {
|
|
14590
14293
|
socket.destroy();
|
|
14591
|
-
|
|
14294
|
+
resolve28(true);
|
|
14592
14295
|
});
|
|
14593
14296
|
socket.once("error", () => {
|
|
14594
14297
|
socket.destroy();
|
|
14595
|
-
|
|
14298
|
+
resolve28(false);
|
|
14596
14299
|
});
|
|
14597
14300
|
socket.once("timeout", () => {
|
|
14598
14301
|
socket.destroy();
|
|
14599
|
-
|
|
14302
|
+
resolve28(false);
|
|
14600
14303
|
});
|
|
14601
14304
|
});
|
|
14602
14305
|
}
|
|
14603
|
-
var TTYD_PROBE_CACHE_MS = 2e3;
|
|
14604
|
-
var TTYD_PROBE_TIMEOUT_MS = 200;
|
|
14605
|
-
var cachedTtydReady = null;
|
|
14606
|
-
async function probeTerminalReady() {
|
|
14607
|
-
if (cachedTtydReady && Date.now() - cachedTtydReady.checkedAt < TTYD_PROBE_CACHE_MS) {
|
|
14608
|
-
return cachedTtydReady.ok;
|
|
14609
|
-
}
|
|
14610
|
-
const ok = await checkPort(7681, TTYD_PROBE_TIMEOUT_MS);
|
|
14611
|
-
if (!cachedTtydReady || cachedTtydReady.ok !== ok) {
|
|
14612
|
-
console.log(`[health] terminal_ready: ${cachedTtydReady?.ok ?? "unknown"} \u2192 ${ok}`);
|
|
14613
|
-
}
|
|
14614
|
-
cachedTtydReady = { ok, checkedAt: Date.now() };
|
|
14615
|
-
return ok;
|
|
14616
|
-
}
|
|
14617
14306
|
var app = new Hono2();
|
|
14618
14307
|
app.get("/", async (c) => {
|
|
14619
14308
|
const browserTransport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
|
|
@@ -14637,7 +14326,6 @@ app.get("/", async (c) => {
|
|
|
14637
14326
|
}
|
|
14638
14327
|
const claudeAuthenticated = authHealth.status === "ok" || authHealth.status === "expiring";
|
|
14639
14328
|
const vncRunning = await checkPort(6080);
|
|
14640
|
-
const terminalReady = await probeTerminalReady();
|
|
14641
14329
|
let apiKeyConfigured = false;
|
|
14642
14330
|
try {
|
|
14643
14331
|
apiKeyConfigured = existsSync10(keyFilePath());
|
|
@@ -14681,7 +14369,6 @@ app.get("/", async (c) => {
|
|
|
14681
14369
|
claude_authenticated: claudeAuthenticated,
|
|
14682
14370
|
...onboardingComplete !== void 0 && { onboarding_complete: onboardingComplete },
|
|
14683
14371
|
vnc_running: vncRunning,
|
|
14684
|
-
terminal_ready: terminalReady,
|
|
14685
14372
|
browser_transport: browserTransport,
|
|
14686
14373
|
auth_status: authHealth.status,
|
|
14687
14374
|
auth_expires_at: authHealth.expiresAt ?? null,
|
|
@@ -14709,14 +14396,14 @@ app.get("/", async (c) => {
|
|
|
14709
14396
|
var health_default = app;
|
|
14710
14397
|
|
|
14711
14398
|
// server/routes/session.ts
|
|
14712
|
-
import { resolve as
|
|
14713
|
-
import { existsSync as existsSync11, writeFileSync as writeFileSync10, mkdirSync as
|
|
14399
|
+
import { resolve as resolve11 } from "path";
|
|
14400
|
+
import { existsSync as existsSync11, writeFileSync as writeFileSync10, mkdirSync as mkdirSync8 } from "fs";
|
|
14714
14401
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14715
14402
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
14716
14403
|
try {
|
|
14717
|
-
const cacheDir =
|
|
14718
|
-
|
|
14719
|
-
writeFileSync10(
|
|
14404
|
+
const cacheDir = resolve11(MAXY_DIR, "branding-cache", accountId);
|
|
14405
|
+
mkdirSync8(cacheDir, { recursive: true });
|
|
14406
|
+
writeFileSync10(resolve11(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
14720
14407
|
} catch (err) {
|
|
14721
14408
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
14722
14409
|
}
|
|
@@ -14786,8 +14473,8 @@ app2.post("/", async (c) => {
|
|
|
14786
14473
|
}
|
|
14787
14474
|
let agentConfig = null;
|
|
14788
14475
|
if (account) {
|
|
14789
|
-
const agentDir =
|
|
14790
|
-
const agentConfigPath =
|
|
14476
|
+
const agentDir = resolve11(account.accountDir, "agents", agentSlug);
|
|
14477
|
+
const agentConfigPath = resolve11(agentDir, "config.json");
|
|
14791
14478
|
if (!existsSync11(agentDir) || !existsSync11(agentConfigPath)) {
|
|
14792
14479
|
return c.json({ error: "Agent not found" }, 404);
|
|
14793
14480
|
}
|
|
@@ -15034,9 +14721,9 @@ ${raw2}`;
|
|
|
15034
14721
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
15035
14722
|
import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
15036
14723
|
import { realpathSync } from "fs";
|
|
15037
|
-
import { resolve as
|
|
15038
|
-
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ??
|
|
15039
|
-
var ATTACHMENTS_ROOT =
|
|
14724
|
+
import { resolve as resolve12, extname, basename as basename3 } from "path";
|
|
14725
|
+
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve12(process.cwd(), "../platform");
|
|
14726
|
+
var ATTACHMENTS_ROOT = resolve12(PLATFORM_ROOT4, "..", "data/uploads");
|
|
15040
14727
|
var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
15041
14728
|
"image/jpeg",
|
|
15042
14729
|
"image/png",
|
|
@@ -15060,11 +14747,11 @@ function assertSupportedMime(mimeType) {
|
|
|
15060
14747
|
}
|
|
15061
14748
|
async function writeAttachment(scope, filename, mimeType, sizeBytes, buffer) {
|
|
15062
14749
|
const attachmentId = randomUUID6();
|
|
15063
|
-
const dir =
|
|
14750
|
+
const dir = resolve12(ATTACHMENTS_ROOT, scope, attachmentId);
|
|
15064
14751
|
await mkdir2(dir, { recursive: true });
|
|
15065
14752
|
const ext = extname(filename) || "";
|
|
15066
|
-
const storagePath =
|
|
15067
|
-
const metaPath =
|
|
14753
|
+
const storagePath = resolve12(dir, `${attachmentId}${ext}`);
|
|
14754
|
+
const metaPath = resolve12(dir, `${attachmentId}.meta.json`);
|
|
15068
14755
|
const meta = {
|
|
15069
14756
|
attachmentId,
|
|
15070
14757
|
scope,
|
|
@@ -15742,13 +15429,13 @@ var group_default = app4;
|
|
|
15742
15429
|
// app/lib/access-gate.ts
|
|
15743
15430
|
import neo4j2 from "neo4j-driver";
|
|
15744
15431
|
import { readFileSync as readFileSync12 } from "fs";
|
|
15745
|
-
import { resolve as
|
|
15432
|
+
import { resolve as resolve13 } from "path";
|
|
15746
15433
|
import { randomUUID as randomUUID7, randomInt } from "crypto";
|
|
15747
|
-
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ??
|
|
15434
|
+
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
|
|
15748
15435
|
var driver2 = null;
|
|
15749
15436
|
function readPassword2() {
|
|
15750
15437
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
15751
|
-
const passwordFile =
|
|
15438
|
+
const passwordFile = resolve13(PLATFORM_ROOT5, "config/.neo4j-password");
|
|
15752
15439
|
try {
|
|
15753
15440
|
return readFileSync12(passwordFile, "utf-8").trim();
|
|
15754
15441
|
} catch {
|
|
@@ -16061,17 +15748,17 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
|
|
|
16061
15748
|
}
|
|
16062
15749
|
|
|
16063
15750
|
// app/lib/brevo-sms.ts
|
|
16064
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as
|
|
15751
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, existsSync as existsSync12, chmodSync } from "fs";
|
|
16065
15752
|
import { dirname as dirname6 } from "path";
|
|
16066
|
-
import { resolve as
|
|
16067
|
-
var BREVO_API_KEY_FILE =
|
|
15753
|
+
import { resolve as resolve14 } from "path";
|
|
15754
|
+
var BREVO_API_KEY_FILE = resolve14(MAXY_DIR, ".brevo-api-key");
|
|
16068
15755
|
var BREVO_API_URL = "https://api.brevo.com/v3/transactionalSMS/sms";
|
|
16069
15756
|
var BREVO_TIMEOUT_MS = 1e4;
|
|
16070
15757
|
var BREVO_SENDER = "Maxy";
|
|
16071
15758
|
var platformRoot = process.env.MAXY_PLATFORM_ROOT;
|
|
16072
15759
|
if (platformRoot) {
|
|
16073
15760
|
try {
|
|
16074
|
-
const brandPath =
|
|
15761
|
+
const brandPath = resolve14(platformRoot, "config", "brand.json");
|
|
16075
15762
|
if (existsSync12(brandPath)) {
|
|
16076
15763
|
const brand = JSON.parse(readFileSync13(brandPath, "utf-8"));
|
|
16077
15764
|
if (brand.productName) BREVO_SENDER = brand.productName;
|
|
@@ -16708,7 +16395,7 @@ app6.post("/webhook", async (c) => {
|
|
|
16708
16395
|
var telegram_default = app6;
|
|
16709
16396
|
|
|
16710
16397
|
// server/routes/whatsapp.ts
|
|
16711
|
-
import { join as join8, resolve as
|
|
16398
|
+
import { join as join8, resolve as resolve15, basename as basename4 } from "path";
|
|
16712
16399
|
import { readFile as readFile2, stat as stat3 } from "fs/promises";
|
|
16713
16400
|
import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
|
|
16714
16401
|
|
|
@@ -16816,8 +16503,8 @@ async function startLogin(opts) {
|
|
|
16816
16503
|
resetActiveLogin(accountId);
|
|
16817
16504
|
let resolveQr = null;
|
|
16818
16505
|
let rejectQr = null;
|
|
16819
|
-
const qrPromise = new Promise((
|
|
16820
|
-
resolveQr =
|
|
16506
|
+
const qrPromise = new Promise((resolve28, reject) => {
|
|
16507
|
+
resolveQr = resolve28;
|
|
16821
16508
|
rejectQr = reject;
|
|
16822
16509
|
});
|
|
16823
16510
|
const qrTimer = setTimeout(
|
|
@@ -17188,14 +16875,14 @@ app7.post("/config", async (c) => {
|
|
|
17188
16875
|
return c.json({ ok: true, slug: currentSlug });
|
|
17189
16876
|
}
|
|
17190
16877
|
case "list-public-agents": {
|
|
17191
|
-
const agentsDir =
|
|
16878
|
+
const agentsDir = resolve15(account.accountDir, "agents");
|
|
17192
16879
|
const agents = [];
|
|
17193
16880
|
if (existsSync14(agentsDir)) {
|
|
17194
16881
|
try {
|
|
17195
16882
|
const entries = readdirSync4(agentsDir, { withFileTypes: true });
|
|
17196
16883
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
17197
16884
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
17198
|
-
const configPath2 =
|
|
16885
|
+
const configPath2 = resolve15(agentsDir, entry.name, "config.json");
|
|
17199
16886
|
if (!existsSync14(configPath2)) continue;
|
|
17200
16887
|
try {
|
|
17201
16888
|
const config = JSON.parse(readFileSync15(configPath2, "utf-8"));
|
|
@@ -17273,7 +16960,7 @@ app7.post("/send-document", async (c) => {
|
|
|
17273
16960
|
if (!maxyAccountId || !PLATFORM_ROOT6) {
|
|
17274
16961
|
return c.json({ error: "Cannot validate file path: missing account or platform context" }, 400);
|
|
17275
16962
|
}
|
|
17276
|
-
const accountDir =
|
|
16963
|
+
const accountDir = resolve15(PLATFORM_ROOT6, "..", "data/accounts", maxyAccountId);
|
|
17277
16964
|
let resolvedPath;
|
|
17278
16965
|
try {
|
|
17279
16966
|
resolvedPath = realpathSync2(filePath);
|
|
@@ -17410,8 +17097,8 @@ var whatsapp_default = app7;
|
|
|
17410
17097
|
|
|
17411
17098
|
// server/routes/onboarding.ts
|
|
17412
17099
|
import { spawn as spawn3, execFileSync as execFileSync2 } from "child_process";
|
|
17413
|
-
import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync15, mkdirSync as
|
|
17414
|
-
import { resolve as
|
|
17100
|
+
import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync16, unlinkSync as unlinkSync4 } from "fs";
|
|
17101
|
+
import { resolve as resolve16, dirname as dirname7 } from "path";
|
|
17415
17102
|
import { createHash, randomUUID as randomUUID9 } from "crypto";
|
|
17416
17103
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
17417
17104
|
function hashPin(pin) {
|
|
@@ -17524,7 +17211,7 @@ app8.post("/set-pin", async (c) => {
|
|
|
17524
17211
|
}
|
|
17525
17212
|
const hash = hashPin(body.pin);
|
|
17526
17213
|
const userId = randomUUID9();
|
|
17527
|
-
|
|
17214
|
+
mkdirSync10(dirname7(USERS_FILE), { recursive: true });
|
|
17528
17215
|
writeFileSync12(USERS_FILE, JSON.stringify([{ userId, name: "Owner", pin: hash }]), { mode: 384 });
|
|
17529
17216
|
console.log(`[set-pin] created users.json: userId=${userId.slice(0, 8)}\u2026 hash=${hash.slice(0, 8)}\u2026`);
|
|
17530
17217
|
const account = resolveAccount();
|
|
@@ -17585,7 +17272,7 @@ app8.post("/skip", async (c) => {
|
|
|
17585
17272
|
}
|
|
17586
17273
|
const { accountId, accountDir } = account;
|
|
17587
17274
|
let agentName = "Maxy";
|
|
17588
|
-
const brandPath = PLATFORM_ROOT7 ?
|
|
17275
|
+
const brandPath = PLATFORM_ROOT7 ? resolve16(PLATFORM_ROOT7, "config", "brand.json") : "";
|
|
17589
17276
|
if (brandPath && existsSync15(brandPath)) {
|
|
17590
17277
|
try {
|
|
17591
17278
|
const brand = JSON.parse(readFileSync16(brandPath, "utf-8"));
|
|
@@ -17594,9 +17281,9 @@ app8.post("/skip", async (c) => {
|
|
|
17594
17281
|
console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
17595
17282
|
}
|
|
17596
17283
|
}
|
|
17597
|
-
const soulPath =
|
|
17284
|
+
const soulPath = resolve16(accountDir, "agents", "admin", "SOUL.md");
|
|
17598
17285
|
try {
|
|
17599
|
-
|
|
17286
|
+
mkdirSync10(dirname7(soulPath), { recursive: true });
|
|
17600
17287
|
writeFileSync12(soulPath, `You are ${agentName}, an AI operations manager.
|
|
17601
17288
|
`);
|
|
17602
17289
|
console.log(`[onboarding-skip] wrote SOUL.md: ${soulPath}`);
|
|
@@ -17635,7 +17322,7 @@ app8.post("/skip", async (c) => {
|
|
|
17635
17322
|
var onboarding_default = app8;
|
|
17636
17323
|
|
|
17637
17324
|
// server/routes/client-error.ts
|
|
17638
|
-
import { appendFileSync as
|
|
17325
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as renameSync5, statSync as statSync7 } from "fs";
|
|
17639
17326
|
import { join as join9 } from "path";
|
|
17640
17327
|
var CLIENT_ERRORS_LOG = join9(LOG_DIR, "client-errors.log");
|
|
17641
17328
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
@@ -17772,7 +17459,7 @@ app9.post("/", async (c) => {
|
|
|
17772
17459
|
tag: typeof body.tag === "string" ? truncate2(body.tag, 32) : void 0,
|
|
17773
17460
|
status: typeof body.status === "number" ? body.status : void 0
|
|
17774
17461
|
};
|
|
17775
|
-
|
|
17462
|
+
appendFileSync3(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
|
|
17776
17463
|
} catch (err) {
|
|
17777
17464
|
console.error(`[client-error] append failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
17778
17465
|
}
|
|
@@ -17919,11 +17606,11 @@ app10.post("/", async (c) => {
|
|
|
17919
17606
|
var session_default2 = app10;
|
|
17920
17607
|
|
|
17921
17608
|
// server/routes/admin/chat.ts
|
|
17922
|
-
import { resolve as
|
|
17609
|
+
import { resolve as resolve17 } from "path";
|
|
17923
17610
|
|
|
17924
17611
|
// app/lib/script-stream-tailer.ts
|
|
17925
17612
|
import * as childProcess from "child_process";
|
|
17926
|
-
import { appendFileSync as
|
|
17613
|
+
import { appendFileSync as appendFileSync4, createReadStream as createReadStream2, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
|
|
17927
17614
|
import { dirname as dirname8 } from "path";
|
|
17928
17615
|
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
17929
17616
|
var SCRIPT_STREAM_RE = /^\[([^\]]+)\] \[script:([a-z][a-z0-9-]*)((?::[a-z0-9:_-]+)?)\] (.*)$/;
|
|
@@ -18033,8 +17720,8 @@ function writeRouteMilestone(streamLogPath, scope, line) {
|
|
|
18033
17720
|
}
|
|
18034
17721
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
18035
17722
|
try {
|
|
18036
|
-
|
|
18037
|
-
|
|
17723
|
+
mkdirSync11(dirname8(streamLogPath), { recursive: true });
|
|
17724
|
+
appendFileSync4(streamLogPath, `[${ts}] [script:${scope}] ${line}
|
|
18038
17725
|
`);
|
|
18039
17726
|
} catch (err) {
|
|
18040
17727
|
console.error(
|
|
@@ -18364,7 +18051,7 @@ app11.post("/", requireAdminSession, async (c) => {
|
|
|
18364
18051
|
try {
|
|
18365
18052
|
registerAdminSSE(sseEntry);
|
|
18366
18053
|
if (sseConvId) {
|
|
18367
|
-
const streamLogPath =
|
|
18054
|
+
const streamLogPath = resolve17(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
|
|
18368
18055
|
tailer = startScriptStreamTailer({
|
|
18369
18056
|
path: streamLogPath,
|
|
18370
18057
|
onEvent: (event) => {
|
|
@@ -18490,7 +18177,7 @@ var compact_default = app12;
|
|
|
18490
18177
|
|
|
18491
18178
|
// server/routes/admin/logs.ts
|
|
18492
18179
|
import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync18, statSync as statSync9 } from "fs";
|
|
18493
|
-
import { resolve as
|
|
18180
|
+
import { resolve as resolve18, basename as basename5 } from "path";
|
|
18494
18181
|
var TAIL_BYTES = 8192;
|
|
18495
18182
|
var app13 = new Hono2();
|
|
18496
18183
|
app13.get("/", async (c) => {
|
|
@@ -18499,13 +18186,13 @@ app13.get("/", async (c) => {
|
|
|
18499
18186
|
const conversationIdParam = c.req.query("conversationId");
|
|
18500
18187
|
const download = c.req.query("download") === "1";
|
|
18501
18188
|
const account = resolveAccount();
|
|
18502
|
-
const accountLogDir2 = account ?
|
|
18189
|
+
const accountLogDir2 = account ? resolve18(account.accountDir, "logs") : null;
|
|
18503
18190
|
if (fileParam) {
|
|
18504
18191
|
const safe = basename5(fileParam);
|
|
18505
18192
|
const searched = [];
|
|
18506
18193
|
for (const dir of [accountLogDir2, LOG_DIR]) {
|
|
18507
18194
|
if (!dir) continue;
|
|
18508
|
-
const filePath =
|
|
18195
|
+
const filePath = resolve18(dir, safe);
|
|
18509
18196
|
searched.push(filePath);
|
|
18510
18197
|
try {
|
|
18511
18198
|
const content = readFileSync18(filePath, "utf-8");
|
|
@@ -18547,7 +18234,7 @@ app13.get("/", async (c) => {
|
|
|
18547
18234
|
const searched = [];
|
|
18548
18235
|
for (const dir of [accountLogDir2, LOG_DIR]) {
|
|
18549
18236
|
if (!dir) continue;
|
|
18550
|
-
const filePath =
|
|
18237
|
+
const filePath = resolve18(dir, fileName);
|
|
18551
18238
|
searched.push(filePath);
|
|
18552
18239
|
try {
|
|
18553
18240
|
const content = readFileSync18(filePath, "utf-8");
|
|
@@ -18574,10 +18261,10 @@ app13.get("/", async (c) => {
|
|
|
18574
18261
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
18575
18262
|
continue;
|
|
18576
18263
|
}
|
|
18577
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(
|
|
18264
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve18(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
18578
18265
|
seen.add(name);
|
|
18579
18266
|
try {
|
|
18580
|
-
const content = readFileSync18(
|
|
18267
|
+
const content = readFileSync18(resolve18(dir, name));
|
|
18581
18268
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
18582
18269
|
logs[name] = tail.trim() || "(empty)";
|
|
18583
18270
|
} catch (err) {
|
|
@@ -18618,7 +18305,7 @@ var claude_info_default = app14;
|
|
|
18618
18305
|
// server/routes/admin/attachment.ts
|
|
18619
18306
|
import { readFile as readFile3, readdir } from "fs/promises";
|
|
18620
18307
|
import { existsSync as existsSync19 } from "fs";
|
|
18621
|
-
import { resolve as
|
|
18308
|
+
import { resolve as resolve19 } from "path";
|
|
18622
18309
|
var app15 = new Hono2();
|
|
18623
18310
|
app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
18624
18311
|
const attachmentId = c.req.param("attachmentId");
|
|
@@ -18630,11 +18317,11 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
18630
18317
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
|
|
18631
18318
|
return new Response("Not found", { status: 404 });
|
|
18632
18319
|
}
|
|
18633
|
-
const dir =
|
|
18320
|
+
const dir = resolve19(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
18634
18321
|
if (!existsSync19(dir)) {
|
|
18635
18322
|
return new Response("Not found", { status: 404 });
|
|
18636
18323
|
}
|
|
18637
|
-
const metaPath =
|
|
18324
|
+
const metaPath = resolve19(dir, `${attachmentId}.meta.json`);
|
|
18638
18325
|
if (!existsSync19(metaPath)) {
|
|
18639
18326
|
return new Response("Not found", { status: 404 });
|
|
18640
18327
|
}
|
|
@@ -18649,7 +18336,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
18649
18336
|
if (!dataFile) {
|
|
18650
18337
|
return new Response("Not found", { status: 404 });
|
|
18651
18338
|
}
|
|
18652
|
-
const filePath =
|
|
18339
|
+
const filePath = resolve19(dir, dataFile);
|
|
18653
18340
|
const buffer = await readFile3(filePath);
|
|
18654
18341
|
return new Response(new Uint8Array(buffer), {
|
|
18655
18342
|
headers: {
|
|
@@ -18663,7 +18350,7 @@ var attachment_default = app15;
|
|
|
18663
18350
|
|
|
18664
18351
|
// server/routes/admin/account.ts
|
|
18665
18352
|
import { readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
|
|
18666
|
-
import { resolve as
|
|
18353
|
+
import { resolve as resolve20 } from "path";
|
|
18667
18354
|
var VALID_CONTEXT_MODES = ["managed", "claude-code"];
|
|
18668
18355
|
var app16 = new Hono2();
|
|
18669
18356
|
app16.patch("/", requireAdminSession, async (c) => {
|
|
@@ -18679,7 +18366,7 @@ app16.patch("/", requireAdminSession, async (c) => {
|
|
|
18679
18366
|
}
|
|
18680
18367
|
const account = resolveAccount();
|
|
18681
18368
|
if (!account) return c.json({ error: "No account configured" }, 500);
|
|
18682
|
-
const configPath2 =
|
|
18369
|
+
const configPath2 = resolve20(account.accountDir, "account.json");
|
|
18683
18370
|
try {
|
|
18684
18371
|
const raw2 = readFileSync19(configPath2, "utf-8");
|
|
18685
18372
|
const config = JSON.parse(raw2);
|
|
@@ -18695,13 +18382,13 @@ app16.patch("/", requireAdminSession, async (c) => {
|
|
|
18695
18382
|
var account_default = app16;
|
|
18696
18383
|
|
|
18697
18384
|
// server/routes/admin/agents.ts
|
|
18698
|
-
import { resolve as
|
|
18385
|
+
import { resolve as resolve21 } from "path";
|
|
18699
18386
|
import { readdirSync as readdirSync6, readFileSync as readFileSync20, existsSync as existsSync20, rmSync as rmSync3 } from "fs";
|
|
18700
18387
|
var app17 = new Hono2();
|
|
18701
18388
|
app17.get("/", (c) => {
|
|
18702
18389
|
const account = resolveAccount();
|
|
18703
18390
|
if (!account) return c.json({ agents: [] });
|
|
18704
|
-
const agentsDir =
|
|
18391
|
+
const agentsDir = resolve21(account.accountDir, "agents");
|
|
18705
18392
|
if (!existsSync20(agentsDir)) return c.json({ agents: [] });
|
|
18706
18393
|
const agents = [];
|
|
18707
18394
|
try {
|
|
@@ -18709,7 +18396,7 @@ app17.get("/", (c) => {
|
|
|
18709
18396
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
18710
18397
|
if (!entry.isDirectory()) continue;
|
|
18711
18398
|
if (entry.name === "admin") continue;
|
|
18712
|
-
const configPath2 =
|
|
18399
|
+
const configPath2 = resolve21(agentsDir, entry.name, "config.json");
|
|
18713
18400
|
if (!existsSync20(configPath2)) continue;
|
|
18714
18401
|
try {
|
|
18715
18402
|
const config = JSON.parse(readFileSync20(configPath2, "utf-8"));
|
|
@@ -18738,7 +18425,7 @@ app17.delete("/:slug", (c) => {
|
|
|
18738
18425
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
18739
18426
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
18740
18427
|
}
|
|
18741
|
-
const agentDir =
|
|
18428
|
+
const agentDir = resolve21(account.accountDir, "agents", slug);
|
|
18742
18429
|
if (!existsSync20(agentDir)) {
|
|
18743
18430
|
return c.json({ error: "Agent not found" }, 404);
|
|
18744
18431
|
}
|
|
@@ -18755,8 +18442,8 @@ var agents_default = app17;
|
|
|
18755
18442
|
|
|
18756
18443
|
// server/routes/admin/version.ts
|
|
18757
18444
|
import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
|
|
18758
|
-
import { resolve as
|
|
18759
|
-
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ??
|
|
18445
|
+
import { resolve as resolve22, join as join10 } from "path";
|
|
18446
|
+
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
|
|
18760
18447
|
var brandHostname = "maxy";
|
|
18761
18448
|
var brandNpmPackage = "@rubytech/create-maxy";
|
|
18762
18449
|
var brandJsonPath = join10(PLATFORM_ROOT8, "config", "brand.json");
|
|
@@ -18768,7 +18455,7 @@ if (existsSync21(brandJsonPath)) {
|
|
|
18768
18455
|
} catch {
|
|
18769
18456
|
}
|
|
18770
18457
|
}
|
|
18771
|
-
var VERSION_FILE =
|
|
18458
|
+
var VERSION_FILE = resolve22(PLATFORM_ROOT8, `config/.${brandHostname}-version`);
|
|
18772
18459
|
var NPM_PACKAGE = brandNpmPackage;
|
|
18773
18460
|
var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
|
|
18774
18461
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -19268,7 +18955,7 @@ var events_default = app23;
|
|
|
19268
18955
|
|
|
19269
18956
|
// server/routes/admin/cloudflare.ts
|
|
19270
18957
|
import { homedir as homedir3 } from "os";
|
|
19271
|
-
import { resolve as
|
|
18958
|
+
import { resolve as resolve24 } from "path";
|
|
19272
18959
|
import { readFileSync as readFileSync23 } from "fs";
|
|
19273
18960
|
|
|
19274
18961
|
// app/lib/dns-label.ts
|
|
@@ -19285,10 +18972,10 @@ function isValidDomain(value) {
|
|
|
19285
18972
|
}
|
|
19286
18973
|
|
|
19287
18974
|
// app/lib/alias-domains.ts
|
|
19288
|
-
import { existsSync as existsSync22, mkdirSync as
|
|
18975
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
|
|
19289
18976
|
import { dirname as dirname9 } from "path";
|
|
19290
|
-
import { resolve as
|
|
19291
|
-
var ALIAS_DOMAINS_PATH =
|
|
18977
|
+
import { resolve as resolve23 } from "path";
|
|
18978
|
+
var ALIAS_DOMAINS_PATH = resolve23(MAXY_DIR, "alias-domains.json");
|
|
19292
18979
|
function readExisting() {
|
|
19293
18980
|
if (!existsSync22(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
|
|
19294
18981
|
try {
|
|
@@ -19303,7 +18990,7 @@ function addAliasDomain(hostname2) {
|
|
|
19303
18990
|
const existing = readExisting();
|
|
19304
18991
|
if (existing.has(hostname2)) return;
|
|
19305
18992
|
existing.add(hostname2);
|
|
19306
|
-
|
|
18993
|
+
mkdirSync12(dirname9(ALIAS_DOMAINS_PATH), { recursive: true });
|
|
19307
18994
|
writeFileSync14(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
|
|
19308
18995
|
}
|
|
19309
18996
|
|
|
@@ -19311,8 +18998,8 @@ function addAliasDomain(hostname2) {
|
|
|
19311
18998
|
var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
19312
18999
|
var DOMAINS_TIMEOUT_MS = 40 * 1e3;
|
|
19313
19000
|
function loadBrandInfo() {
|
|
19314
|
-
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
19315
|
-
const brandPath =
|
|
19001
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
|
|
19002
|
+
const brandPath = resolve24(platformRoot2, "config", "brand.json");
|
|
19316
19003
|
try {
|
|
19317
19004
|
const parsed = JSON.parse(readFileSync23(brandPath, "utf-8"));
|
|
19318
19005
|
const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
|
|
@@ -19417,7 +19104,7 @@ app24.get("/domains", requireAdminSession, async (c) => {
|
|
|
19417
19104
|
streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
|
|
19418
19105
|
log2(`phase=stream-log-resolved path=${streamLogPath}`);
|
|
19419
19106
|
const brand = loadBrandInfo();
|
|
19420
|
-
const scriptPath =
|
|
19107
|
+
const scriptPath = resolve24(homedir3(), "list-cf-domains.sh");
|
|
19421
19108
|
const result = await runFormSpawn({
|
|
19422
19109
|
scriptPath,
|
|
19423
19110
|
args: [brand.hostname],
|
|
@@ -19542,7 +19229,7 @@ app24.post("/setup", requireAdminSession, async (c) => {
|
|
|
19542
19229
|
}
|
|
19543
19230
|
streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
|
|
19544
19231
|
log2(`phase=stream-log-resolved path=${streamLogPath}`);
|
|
19545
|
-
const scriptPath =
|
|
19232
|
+
const scriptPath = resolve24(homedir3(), "setup-tunnel.sh");
|
|
19546
19233
|
const args = [brand.hostname, String(port2), adminFqdn];
|
|
19547
19234
|
if (publicFqdn) args.push(publicFqdn);
|
|
19548
19235
|
if (apex) args.push(apex);
|
|
@@ -19611,17 +19298,17 @@ var cloudflare_default = app24;
|
|
|
19611
19298
|
import { createReadStream as createReadStream3 } from "fs";
|
|
19612
19299
|
import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
19613
19300
|
import { realpathSync as realpathSync4 } from "fs";
|
|
19614
|
-
import { basename as basename6, dirname as dirname10, join as join11, resolve as
|
|
19301
|
+
import { basename as basename6, dirname as dirname10, join as join11, resolve as resolve26, sep as sep2 } from "path";
|
|
19615
19302
|
import { Readable as Readable3 } from "stream";
|
|
19616
19303
|
|
|
19617
19304
|
// app/lib/data-path.ts
|
|
19618
19305
|
import { realpathSync as realpathSync3 } from "fs";
|
|
19619
|
-
import { resolve as
|
|
19620
|
-
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ??
|
|
19621
|
-
var DATA_ROOT =
|
|
19306
|
+
import { resolve as resolve25, normalize, sep, relative } from "path";
|
|
19307
|
+
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "../platform");
|
|
19308
|
+
var DATA_ROOT = resolve25(PLATFORM_ROOT9, "..", "data");
|
|
19622
19309
|
function resolveDataPath(raw2) {
|
|
19623
19310
|
const cleaned = normalize("/" + (raw2 ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
19624
|
-
const absolute =
|
|
19311
|
+
const absolute = resolve25(DATA_ROOT, cleaned);
|
|
19625
19312
|
let dataRootReal;
|
|
19626
19313
|
try {
|
|
19627
19314
|
dataRootReal = realpathSync3(DATA_ROOT);
|
|
@@ -19899,7 +19586,7 @@ async function readMeta(absDir, baseName) {
|
|
|
19899
19586
|
}
|
|
19900
19587
|
async function readAccountNames() {
|
|
19901
19588
|
const map = /* @__PURE__ */ new Map();
|
|
19902
|
-
const accountsDir =
|
|
19589
|
+
const accountsDir = resolve26(DATA_ROOT, "accounts");
|
|
19903
19590
|
let names;
|
|
19904
19591
|
try {
|
|
19905
19592
|
names = await readdir2(accountsDir);
|
|
@@ -19908,7 +19595,7 @@ async function readAccountNames() {
|
|
|
19908
19595
|
}
|
|
19909
19596
|
for (const name of names) {
|
|
19910
19597
|
if (!UUID_RE3.test(name)) continue;
|
|
19911
|
-
const configPath2 =
|
|
19598
|
+
const configPath2 = resolve26(accountsDir, name, "account.json");
|
|
19912
19599
|
try {
|
|
19913
19600
|
const raw2 = await readFile4(configPath2, "utf8");
|
|
19914
19601
|
const parsed = JSON.parse(raw2);
|
|
@@ -20099,8 +19786,8 @@ app25.post("/upload", requireAdminSession, async (c) => {
|
|
|
20099
19786
|
}
|
|
20100
19787
|
const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
|
|
20101
19788
|
const finalName = `${Date.now()}-${safeName}`;
|
|
20102
|
-
const destDir =
|
|
20103
|
-
const destPath =
|
|
19789
|
+
const destDir = resolve26(DATA_ROOT, "uploads", accountId);
|
|
19790
|
+
const destPath = resolve26(destDir, finalName);
|
|
20104
19791
|
try {
|
|
20105
19792
|
await mkdir3(destDir, { recursive: true });
|
|
20106
19793
|
const dataRootReal = realpathSync4(DATA_ROOT);
|
|
@@ -21405,8 +21092,8 @@ app35.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
21405
21092
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
21406
21093
|
return c.text("Not found", 404);
|
|
21407
21094
|
}
|
|
21408
|
-
const filePath =
|
|
21409
|
-
const expectedDir =
|
|
21095
|
+
const filePath = resolve27(account.accountDir, "agents", slug, "assets", filename);
|
|
21096
|
+
const expectedDir = resolve27(account.accountDir, "agents", slug, "assets");
|
|
21410
21097
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
21411
21098
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
21412
21099
|
return c.text("Forbidden", 403);
|
|
@@ -21435,8 +21122,8 @@ app35.get("/generated/:filename", (c) => {
|
|
|
21435
21122
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
21436
21123
|
return c.text("Not found", 404);
|
|
21437
21124
|
}
|
|
21438
|
-
const filePath =
|
|
21439
|
-
const expectedDir =
|
|
21125
|
+
const filePath = resolve27(account.accountDir, "generated", filename);
|
|
21126
|
+
const expectedDir = resolve27(account.accountDir, "generated");
|
|
21440
21127
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
21441
21128
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
21442
21129
|
return c.text("Forbidden", 403);
|
|
@@ -21519,7 +21206,7 @@ var clientErrorReporterScript = `<script>
|
|
|
21519
21206
|
function cachedHtml(file) {
|
|
21520
21207
|
let html = htmlCache.get(file);
|
|
21521
21208
|
if (!html) {
|
|
21522
|
-
html = readFileSync24(
|
|
21209
|
+
html = readFileSync24(resolve27(process.cwd(), "public", file), "utf-8");
|
|
21523
21210
|
html = html.replace("<title>Maxy</title>", `<title>${escapeHtml(BRAND.productName)}</title>`);
|
|
21524
21211
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
21525
21212
|
const headInjection = file === "index.html" ? `${brandScript}
|
|
@@ -21626,7 +21313,7 @@ app35.use("/vnc-popout.html", logViewerFetch);
|
|
|
21626
21313
|
app35.get("/vnc-popout.html", (c) => {
|
|
21627
21314
|
let html = htmlCache.get("vnc-popout.html");
|
|
21628
21315
|
if (!html) {
|
|
21629
|
-
html = readFileSync24(
|
|
21316
|
+
html = readFileSync24(resolve27(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
21630
21317
|
const name = escapeHtml(BRAND.productName);
|
|
21631
21318
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
21632
21319
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -21683,11 +21370,6 @@ app35.use("/*", serveStatic({ root: "./public" }));
|
|
|
21683
21370
|
var port = parseInt(process.env.PORT ?? "19199", 10);
|
|
21684
21371
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
21685
21372
|
var httpServer = serve({ fetch: app35.fetch, port, hostname });
|
|
21686
|
-
attachTerminalWsProxy(httpServer, {
|
|
21687
|
-
isPublicHost,
|
|
21688
|
-
upstreamHost: "127.0.0.1",
|
|
21689
|
-
upstreamPort: 7681
|
|
21690
|
-
});
|
|
21691
21373
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
21692
21374
|
var SUBAPP_MANIFEST = [
|
|
21693
21375
|
{ prefix: "/api/health", file: "server/routes/health.ts", subapp: health_default },
|
|
@@ -21748,7 +21430,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
21748
21430
|
}
|
|
21749
21431
|
init({
|
|
21750
21432
|
configDir: configDirForWhatsApp,
|
|
21751
|
-
platformRoot:
|
|
21433
|
+
platformRoot: resolve27(process.env.MAXY_PLATFORM_ROOT ?? join12(__dirname, "..")),
|
|
21752
21434
|
accountConfig: bootAccountConfig,
|
|
21753
21435
|
onMessage: async (msg) => {
|
|
21754
21436
|
try {
|