@rubytech/create-realagent 1.0.676 → 1.0.678
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/internals.md +4 -0
- 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/public/assets/{admin-DQmUdTBa.js → admin-BBL1no_g.js} +1 -1
- package/payload/server/public/assets/{data-DVlvxbTt.js → data-DUSyrydY.js} +1 -1
- package/payload/server/public/assets/{file-OY_hX2wu.js → file-CDJ6dUV3.js} +1 -1
- package/payload/server/public/assets/graph-CWcYp5bE.js +50 -0
- package/payload/server/public/assets/{house-CgENfOCP.js → house-CNP_bwvT.js} +1 -1
- package/payload/server/public/assets/{jsx-runtime-Bu4vXoe7.css → jsx-runtime-BFFQvkdQ.css} +1 -1
- package/payload/server/public/assets/{public-Clp4VPwo.js → public-sHoAccvb.js} +1 -1
- package/payload/server/public/assets/{share-2-RSIR3MmX.js → share-2-DBcb9j6E.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-B0FI_hts.js → useVoiceRecorder-CtSgpc95.js} +1 -1
- package/payload/server/public/assets/{x-DKZ5NR3n.js → x-CTVJaC_u.js} +1 -1
- package/payload/server/public/data.html +6 -6
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +7 -7
- package/payload/server/public/public.html +4 -4
- package/payload/server/server.js +427 -649
- package/payload/platform/templates/dotfiles/.tmux.conf +0 -1
- package/payload/platform/templates/systemd/maxy-ttyd.service +0 -25
- package/payload/server/public/assets/graph-BDaM4Qer.js +0 -49
- /package/payload/server/public/assets/{jsx-runtime-C_VUlXvu.js → jsx-runtime-BVKWELH6.js} +0 -0
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
|
});
|
|
@@ -7477,8 +7168,75 @@ function clearSessionHistory(sessionKey) {
|
|
|
7477
7168
|
session.stalledSubagents = void 0;
|
|
7478
7169
|
session.pendingTrimmedMessages = void 0;
|
|
7479
7170
|
session.pendingCommitmentOffers = void 0;
|
|
7171
|
+
session.pendingTurns = void 0;
|
|
7480
7172
|
return previousConversationId;
|
|
7481
7173
|
}
|
|
7174
|
+
function bufferPendingTurn(sessionKey, turn) {
|
|
7175
|
+
const session = sessionStore.get(sessionKey);
|
|
7176
|
+
if (!session) {
|
|
7177
|
+
console.error(`[conversation-gate] bufferPendingTurn: session not found sessionKey=${sessionKey.slice(0, 8)}\u2026`);
|
|
7178
|
+
return;
|
|
7179
|
+
}
|
|
7180
|
+
if (!session.pendingTurns) session.pendingTurns = [];
|
|
7181
|
+
session.pendingTurns.push(turn);
|
|
7182
|
+
console.log(`[conversation-gate] ${(/* @__PURE__ */ new Date()).toISOString()} buffered sessionKey=${sessionKey.slice(0, 8)} role=${turn.role} turnCount=${session.pendingTurns.filter((t) => t.role === "user").length}`);
|
|
7183
|
+
}
|
|
7184
|
+
function getPendingTurnCount(sessionKey) {
|
|
7185
|
+
const buf = sessionStore.get(sessionKey)?.pendingTurns;
|
|
7186
|
+
if (!buf) return 0;
|
|
7187
|
+
let n = 0;
|
|
7188
|
+
for (const t of buf) if (t.role === "user") n++;
|
|
7189
|
+
return n;
|
|
7190
|
+
}
|
|
7191
|
+
function drainPendingTurns(sessionKey) {
|
|
7192
|
+
const session = sessionStore.get(sessionKey);
|
|
7193
|
+
if (!session?.pendingTurns || session.pendingTurns.length === 0) return void 0;
|
|
7194
|
+
const drained = session.pendingTurns;
|
|
7195
|
+
session.pendingTurns = void 0;
|
|
7196
|
+
return drained;
|
|
7197
|
+
}
|
|
7198
|
+
async function maybeFlushConversationBuffer(sessionKey, agentType, accountId) {
|
|
7199
|
+
const session = sessionStore.get(sessionKey);
|
|
7200
|
+
if (!session) return null;
|
|
7201
|
+
if (session.conversationId) return session.conversationId;
|
|
7202
|
+
if (getPendingTurnCount(sessionKey) < 2) return null;
|
|
7203
|
+
if (session.flushInFlight) return session.flushInFlight;
|
|
7204
|
+
const attempt = (async () => {
|
|
7205
|
+
let conversationId = null;
|
|
7206
|
+
if (agentType === "admin") {
|
|
7207
|
+
const userId = session.userId;
|
|
7208
|
+
if (!userId) {
|
|
7209
|
+
console.error(`[conversation-gate] flush aborted: admin session missing userId sessionKey=${sessionKey.slice(0, 8)}\u2026`);
|
|
7210
|
+
return null;
|
|
7211
|
+
}
|
|
7212
|
+
conversationId = await createNewAdminConversation(userId, accountId, sessionKey);
|
|
7213
|
+
} else {
|
|
7214
|
+
conversationId = await ensureConversation(accountId, "public", sessionKey, void 0, void 0, void 0);
|
|
7215
|
+
}
|
|
7216
|
+
if (!conversationId) return null;
|
|
7217
|
+
session.conversationId = conversationId;
|
|
7218
|
+
const buffered = drainPendingTurns(sessionKey) ?? [];
|
|
7219
|
+
for (const turn of buffered) {
|
|
7220
|
+
persistMessage(conversationId, turn.role, turn.content, accountId, turn.tokens, turn.timestamp, turn.sender).catch((err) => {
|
|
7221
|
+
console.error(`[conversation-gate] replay persistMessage failed role=${turn.role}: ${err instanceof Error ? err.message : String(err)}`);
|
|
7222
|
+
});
|
|
7223
|
+
}
|
|
7224
|
+
console.log(`[conversation-gate] ${(/* @__PURE__ */ new Date()).toISOString()} flushed sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} bufferedMessages=${buffered.length} agentType=${agentType}`);
|
|
7225
|
+
return conversationId;
|
|
7226
|
+
})();
|
|
7227
|
+
session.flushInFlight = attempt;
|
|
7228
|
+
try {
|
|
7229
|
+
return await attempt;
|
|
7230
|
+
} finally {
|
|
7231
|
+
if (session.flushInFlight === attempt) session.flushInFlight = void 0;
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7234
|
+
function isDmChannelSessionKey(sessionKey) {
|
|
7235
|
+
return sessionKey.startsWith("whatsapp:") || sessionKey.startsWith("telegram:");
|
|
7236
|
+
}
|
|
7237
|
+
function preflushStreamLogKey(sessionKey) {
|
|
7238
|
+
return `preflush-${sessionKey.slice(0, 12)}`;
|
|
7239
|
+
}
|
|
7482
7240
|
function getAgentNameForSession(sessionKey) {
|
|
7483
7241
|
return sessionStore.get(sessionKey)?.agentName;
|
|
7484
7242
|
}
|
|
@@ -7633,8 +7391,8 @@ function consumeStalledSubagents(sessionKey) {
|
|
|
7633
7391
|
return stalls && stalls.length > 0 ? stalls : void 0;
|
|
7634
7392
|
}
|
|
7635
7393
|
function streamLogPathFor(accountId, conversationId) {
|
|
7636
|
-
const logDir =
|
|
7637
|
-
const streamLogPath =
|
|
7394
|
+
const logDir = resolve5(ACCOUNTS_DIR, accountId, "logs");
|
|
7395
|
+
const streamLogPath = resolve5(logDir, `claude-agent-stream-${conversationId}.log`);
|
|
7638
7396
|
return { logDir, streamLogPath };
|
|
7639
7397
|
}
|
|
7640
7398
|
function buildSpawnEnv(accountId, accountDir, conversationId) {
|
|
@@ -7655,7 +7413,7 @@ var cachedBrandHostname = null;
|
|
|
7655
7413
|
function readBrandHostname() {
|
|
7656
7414
|
if (cachedBrandHostname !== null) return cachedBrandHostname;
|
|
7657
7415
|
try {
|
|
7658
|
-
const brandPath =
|
|
7416
|
+
const brandPath = resolve5(PLATFORM_ROOT3, "config", "brand.json");
|
|
7659
7417
|
const parsed = JSON.parse(readFileSync6(brandPath, "utf-8"));
|
|
7660
7418
|
cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
|
|
7661
7419
|
} catch {
|
|
@@ -7693,37 +7451,37 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7693
7451
|
const servers = {
|
|
7694
7452
|
"memory": {
|
|
7695
7453
|
command: "node",
|
|
7696
|
-
args: [
|
|
7454
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js")],
|
|
7697
7455
|
env: { ...baseEnv, ...userId ? { USER_ID: userId } : {} }
|
|
7698
7456
|
},
|
|
7699
7457
|
"contacts": {
|
|
7700
7458
|
command: "node",
|
|
7701
|
-
args: [
|
|
7459
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/contacts/mcp/dist/index.js")],
|
|
7702
7460
|
env: { ...baseEnv }
|
|
7703
7461
|
},
|
|
7704
7462
|
"whatsapp": {
|
|
7705
7463
|
command: "node",
|
|
7706
|
-
args: [
|
|
7464
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/whatsapp/mcp/dist/index.js")],
|
|
7707
7465
|
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
|
|
7708
7466
|
},
|
|
7709
7467
|
"admin": {
|
|
7710
7468
|
command: "node",
|
|
7711
|
-
args: [
|
|
7469
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/admin/mcp/dist/index.js")],
|
|
7712
7470
|
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200", ...userId ? { USER_ID: userId } : {} }
|
|
7713
7471
|
},
|
|
7714
7472
|
"scheduling": {
|
|
7715
7473
|
command: "node",
|
|
7716
|
-
args: [
|
|
7474
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/scheduling/mcp/dist/index.js")],
|
|
7717
7475
|
env: { ...baseEnv }
|
|
7718
7476
|
},
|
|
7719
7477
|
"tasks": {
|
|
7720
7478
|
command: "node",
|
|
7721
|
-
args: [
|
|
7479
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/tasks/mcp/dist/index.js")],
|
|
7722
7480
|
env: { ...baseEnv }
|
|
7723
7481
|
},
|
|
7724
7482
|
"email": {
|
|
7725
7483
|
command: "node",
|
|
7726
|
-
args: [
|
|
7484
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/email/mcp/dist/index.js")],
|
|
7727
7485
|
env: { ...baseEnv }
|
|
7728
7486
|
},
|
|
7729
7487
|
// Workflows MCP — persistent admin-session server for list/get/update/delete/
|
|
@@ -7734,7 +7492,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7734
7492
|
// ToolSearches fruitlessly before degrading to a task-create stand-in (Task 571).
|
|
7735
7493
|
"workflows": {
|
|
7736
7494
|
command: "node",
|
|
7737
|
-
args: [
|
|
7495
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/workflows/mcp/dist/index.js")],
|
|
7738
7496
|
env: { ...baseEnv }
|
|
7739
7497
|
},
|
|
7740
7498
|
// Playwright MCP server — browser automation for browser-specialist.
|
|
@@ -7756,7 +7514,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7756
7514
|
// MAXY-PRD.md:627, not in any application-layer filter).
|
|
7757
7515
|
"graph": {
|
|
7758
7516
|
command: "node",
|
|
7759
|
-
args: [
|
|
7517
|
+
args: [resolve5(PLATFORM_ROOT3, "lib/graph-mcp/dist/index.js")],
|
|
7760
7518
|
env: {
|
|
7761
7519
|
...baseEnv,
|
|
7762
7520
|
BRAND: readBrandHostname(),
|
|
@@ -7772,7 +7530,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7772
7530
|
if (tgBotToken) {
|
|
7773
7531
|
servers["telegram"] = {
|
|
7774
7532
|
command: "node",
|
|
7775
|
-
args: [
|
|
7533
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/telegram/mcp/dist/index.js")],
|
|
7776
7534
|
env: { ...baseEnv, TELEGRAM_BOT_TOKEN: tgBotToken }
|
|
7777
7535
|
};
|
|
7778
7536
|
} else {
|
|
@@ -7780,11 +7538,11 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7780
7538
|
}
|
|
7781
7539
|
servers["cloudflare"] = {
|
|
7782
7540
|
command: "node",
|
|
7783
|
-
args: [
|
|
7541
|
+
args: [resolve5(PLATFORM_ROOT3, "plugins/cloudflare/mcp/dist/index.js")],
|
|
7784
7542
|
env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
|
|
7785
7543
|
};
|
|
7786
7544
|
if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
|
|
7787
|
-
const pluginsDir =
|
|
7545
|
+
const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
|
|
7788
7546
|
let dirs;
|
|
7789
7547
|
try {
|
|
7790
7548
|
dirs = readdirSync2(pluginsDir);
|
|
@@ -7807,7 +7565,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7807
7565
|
continue;
|
|
7808
7566
|
}
|
|
7809
7567
|
}
|
|
7810
|
-
const mcpEntry =
|
|
7568
|
+
const mcpEntry = resolve5(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
|
|
7811
7569
|
if (!existsSync5(mcpEntry)) continue;
|
|
7812
7570
|
servers[dir] = {
|
|
7813
7571
|
command: "node",
|
|
@@ -7943,7 +7701,7 @@ var ADMIN_CORE_TOOLS = [
|
|
|
7943
7701
|
function getAdminAllowedTools(enabledPlugins) {
|
|
7944
7702
|
const tools = [...ADMIN_CORE_TOOLS];
|
|
7945
7703
|
if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
|
|
7946
|
-
const pluginsDir =
|
|
7704
|
+
const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
|
|
7947
7705
|
let dirs;
|
|
7948
7706
|
try {
|
|
7949
7707
|
dirs = readdirSync2(pluginsDir);
|
|
@@ -8051,13 +7809,13 @@ ${message.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`
|
|
|
8051
7809
|
}
|
|
8052
7810
|
}
|
|
8053
7811
|
async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
8054
|
-
const serverPath =
|
|
7812
|
+
const serverPath = resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
|
|
8055
7813
|
if (!existsSync5(serverPath)) {
|
|
8056
7814
|
console.error(`[fetchMemoryContext] MCP server not found: ${serverPath}`);
|
|
8057
7815
|
return null;
|
|
8058
7816
|
}
|
|
8059
7817
|
const startMs = Date.now();
|
|
8060
|
-
return new Promise((
|
|
7818
|
+
return new Promise((resolve28) => {
|
|
8061
7819
|
const proc = spawn2(process.execPath, [serverPath], {
|
|
8062
7820
|
env: {
|
|
8063
7821
|
...process.env,
|
|
@@ -8086,7 +7844,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
|
8086
7844
|
} else {
|
|
8087
7845
|
console.error(`[fetchMemoryContext] failed: ${reason} (${elapsed}ms)${stderrBuf ? ` stderr: ${stderrBuf.slice(0, 500)}` : ""}`);
|
|
8088
7846
|
}
|
|
8089
|
-
|
|
7847
|
+
resolve28(value);
|
|
8090
7848
|
};
|
|
8091
7849
|
proc.stdout.on("data", (chunk) => {
|
|
8092
7850
|
buffer += chunk.toString();
|
|
@@ -8149,7 +7907,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
|
8149
7907
|
}
|
|
8150
7908
|
async function compactTrimmedMessages(accountId, trimmedMessages) {
|
|
8151
7909
|
if (trimmedMessages.length === 0) return true;
|
|
8152
|
-
const serverPath =
|
|
7910
|
+
const serverPath = resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
|
|
8153
7911
|
if (!existsSync5(serverPath)) return false;
|
|
8154
7912
|
const briefing = trimmedMessages.map((m) => `[${m.role.toUpperCase()}] ${m.content}`).join("\n\n");
|
|
8155
7913
|
return new Promise((resolvePromise) => {
|
|
@@ -8467,7 +8225,7 @@ Then respond with only: [COMPACTED]`;
|
|
|
8467
8225
|
var COMPACTION_TIMEOUT_MS = 45e3;
|
|
8468
8226
|
async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, conversationId, enabledPlugins) {
|
|
8469
8227
|
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, conversationId, void 0, enabledPlugins) });
|
|
8470
|
-
const specialistsDir =
|
|
8228
|
+
const specialistsDir = resolve5(accountDir, "specialists");
|
|
8471
8229
|
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir, conversationId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
8472
8230
|
`);
|
|
8473
8231
|
const args = [
|
|
@@ -8741,7 +8499,7 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId, a
|
|
|
8741
8499
|
const { logDir } = streamLogPathFor(accountId, conversationId);
|
|
8742
8500
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
8743
8501
|
for (const s of failed) {
|
|
8744
|
-
const stderrPath =
|
|
8502
|
+
const stderrPath = resolve5(logDir, `mcp-${s.name}-stderr-${date}.log`);
|
|
8745
8503
|
let tail = "(no stderr file)";
|
|
8746
8504
|
try {
|
|
8747
8505
|
const stats = statSync3(stderrPath);
|
|
@@ -9371,20 +9129,24 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9371
9129
|
const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9372
9130
|
const resumeSessionId = sessionKey ? getAgentSessionId(sessionKey) : void 0;
|
|
9373
9131
|
const spawnConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
|
|
9374
|
-
if (!spawnConvId) {
|
|
9375
|
-
throw new Error(`invokeAdminAgent: conversationId missing for sessionKey=${sessionKey
|
|
9132
|
+
if (!spawnConvId && sessionKey && getPendingTurnCount(sessionKey) >= 2) {
|
|
9133
|
+
throw new Error(`invokeAdminAgent: conversationId missing post-flush for sessionKey=${sessionKey.slice(0, 8)} \u2014 maybeFlushConversationBuffer must bind it before invoking the agent`);
|
|
9134
|
+
}
|
|
9135
|
+
const spawnLogKey = spawnConvId ?? (sessionKey ? preflushStreamLogKey(sessionKey) : void 0);
|
|
9136
|
+
if (!spawnLogKey) {
|
|
9137
|
+
throw new Error(`invokeAdminAgent: sessionKey required \u2014 cannot resolve log stream without one`);
|
|
9376
9138
|
}
|
|
9377
9139
|
const cdpOk = await ensureCdp();
|
|
9378
9140
|
if (!cdpOk) {
|
|
9379
|
-
const cdpLog = agentLogStream("claude-agent-stream", accountDir,
|
|
9141
|
+
const cdpLog = agentLogStream("claude-agent-stream", accountDir, spawnLogKey);
|
|
9380
9142
|
cdpLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
|
|
9381
9143
|
`);
|
|
9382
9144
|
cdpLog.end();
|
|
9383
9145
|
}
|
|
9384
9146
|
const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
|
|
9385
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId,
|
|
9386
|
-
const specialistsDir =
|
|
9387
|
-
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir,
|
|
9147
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, spawnLogKey, ccUserId, enabledPlugins) });
|
|
9148
|
+
const specialistsDir = resolve5(accountDir, "specialists");
|
|
9149
|
+
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnLogKey).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9388
9150
|
`);
|
|
9389
9151
|
const args = [
|
|
9390
9152
|
"--print",
|
|
@@ -9415,19 +9177,19 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9415
9177
|
cwd: accountDir,
|
|
9416
9178
|
stdio: ["ignore", "pipe", "pipe"],
|
|
9417
9179
|
// Task 556: STREAM_LOG_PATH inherited by Bash-tool subprocesses.
|
|
9418
|
-
env: buildSpawnEnv(accountId, accountDir,
|
|
9180
|
+
env: buildSpawnEnv(accountId, accountDir, spawnLogKey)
|
|
9419
9181
|
});
|
|
9420
|
-
const stderrLog = agentLogStream("claude-agent-stderr", accountDir,
|
|
9182
|
+
const stderrLog = agentLogStream("claude-agent-stderr", accountDir, spawnLogKey);
|
|
9421
9183
|
stderrLog.on("error", () => {
|
|
9422
9184
|
});
|
|
9423
9185
|
proc.stderr?.pipe(stderrLog);
|
|
9424
|
-
const streamLog = agentLogStream("claude-agent-stream", accountDir,
|
|
9186
|
+
const streamLog = agentLogStream("claude-agent-stream", accountDir, spawnLogKey);
|
|
9425
9187
|
streamLog.on("error", () => {
|
|
9426
9188
|
});
|
|
9427
9189
|
teeProcStderrToStreamLog(proc, streamLog);
|
|
9428
9190
|
streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
|
|
9429
9191
|
`);
|
|
9430
|
-
streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${spawnConvId} site=admin
|
|
9192
|
+
streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${spawnConvId ?? "preflush"} logKey=${spawnLogKey} site=admin
|
|
9431
9193
|
`);
|
|
9432
9194
|
if (sessionKey) {
|
|
9433
9195
|
const prev = activeProcesses.get(sessionKey);
|
|
@@ -9437,7 +9199,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9437
9199
|
}
|
|
9438
9200
|
activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
|
|
9439
9201
|
}
|
|
9440
|
-
streamLog.write(`[${isoTs()}] [spawn] pid=${proc.pid} resume=${resumeSessionId ?? "none"} sessionKey=${sessionKey ?? "none"} conversationId=${spawnConvId} pluginDir=${specialistsDir}
|
|
9202
|
+
streamLog.write(`[${isoTs()}] [spawn] pid=${proc.pid} resume=${resumeSessionId ?? "none"} sessionKey=${sessionKey ?? "none"} conversationId=${spawnConvId ?? "preflush"} logKey=${spawnLogKey} pluginDir=${specialistsDir}
|
|
9441
9203
|
`);
|
|
9442
9204
|
streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
|
|
9443
9205
|
`);
|
|
@@ -9509,7 +9271,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9509
9271
|
}
|
|
9510
9272
|
if (event.type === "usage" && sessionKey && currentAgentSessionId) {
|
|
9511
9273
|
const peakReqPct = event.peak_request_pct ?? 0;
|
|
9512
|
-
if (peakReqPct >= COMPACTION_THRESHOLD) {
|
|
9274
|
+
if (peakReqPct >= COMPACTION_THRESHOLD && spawnConvId) {
|
|
9513
9275
|
const compactionIter = runCompactionTurn(accountDir, accountId, systemPrompt, currentAgentSessionId, adminModel, spawnConvId, enabledPlugins);
|
|
9514
9276
|
let step = await compactionIter.next();
|
|
9515
9277
|
while (!step.done) {
|
|
@@ -9619,9 +9381,9 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9619
9381
|
} else {
|
|
9620
9382
|
gotDone = true;
|
|
9621
9383
|
if (!sessionWasReset) {
|
|
9384
|
+
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9622
9385
|
const convId = sessionKey ? sessionStore.get(sessionKey)?.conversationId : void 0;
|
|
9623
9386
|
if (convId) {
|
|
9624
|
-
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9625
9387
|
persistMessage(convId, "user", fullMessage, accountId, void 0, userTimestamp).catch(() => {
|
|
9626
9388
|
});
|
|
9627
9389
|
autoLabelSession(convId, fullMessage).catch(() => {
|
|
@@ -9629,7 +9391,14 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9629
9391
|
if (responseText) persistMessage(convId, "assistant", responseText, accountId, capturedTokens, assistantTimestamp).catch(() => {
|
|
9630
9392
|
});
|
|
9631
9393
|
} else if (sessionKey) {
|
|
9632
|
-
|
|
9394
|
+
bufferPendingTurn(sessionKey, { role: "user", content: fullMessage, timestamp: userTimestamp });
|
|
9395
|
+
if (responseText) bufferPendingTurn(sessionKey, { role: "assistant", content: responseText, timestamp: assistantTimestamp, tokens: capturedTokens });
|
|
9396
|
+
const flushedId = await maybeFlushConversationBuffer(sessionKey, "admin", accountId);
|
|
9397
|
+
if (flushedId) {
|
|
9398
|
+
autoLabelSession(flushedId, fullMessage).catch(() => {
|
|
9399
|
+
});
|
|
9400
|
+
yield { type: "conversation_attributed", conversationId: flushedId };
|
|
9401
|
+
}
|
|
9633
9402
|
}
|
|
9634
9403
|
if (sessionKey) {
|
|
9635
9404
|
const commitSession = sessionStore.get(sessionKey);
|
|
@@ -9692,9 +9461,10 @@ ${summary}`;
|
|
|
9692
9461
|
async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accountId, adminModel, sessionKey, maxTurns = 20, attachments = [], retryCount = 0, enabledPlugins, clientTimestamp, adherenceConstraints, agentName) {
|
|
9693
9462
|
const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9694
9463
|
const managedConvId = getConversationIdForSession(sessionKey);
|
|
9695
|
-
if (!managedConvId) {
|
|
9696
|
-
throw new Error(`invokeManagedAdminAgent: conversationId missing for sessionKey=${sessionKey.slice(0, 8)} \u2014
|
|
9464
|
+
if (!managedConvId && getPendingTurnCount(sessionKey) >= 2) {
|
|
9465
|
+
throw new Error(`invokeManagedAdminAgent: conversationId missing post-flush for sessionKey=${sessionKey.slice(0, 8)} \u2014 maybeFlushConversationBuffer must bind it first`);
|
|
9697
9466
|
}
|
|
9467
|
+
const managedLogKey = managedConvId ?? preflushStreamLogKey(sessionKey);
|
|
9698
9468
|
const pendingTrimmed = consumePendingTrimmedMessages(sessionKey);
|
|
9699
9469
|
if (pendingTrimmed && pendingTrimmed.length > 0) {
|
|
9700
9470
|
const ok = await compactTrimmedMessages(accountId, pendingTrimmed);
|
|
@@ -9702,7 +9472,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9702
9472
|
storePendingTrimmedMessages(sessionKey, pendingTrimmed);
|
|
9703
9473
|
}
|
|
9704
9474
|
}
|
|
9705
|
-
const streamLog = agentLogStream("claude-agent-stream", accountDir,
|
|
9475
|
+
const streamLog = agentLogStream("claude-agent-stream", accountDir, managedLogKey);
|
|
9706
9476
|
streamLog.on("error", () => {
|
|
9707
9477
|
});
|
|
9708
9478
|
const systemPromptTokens = estimateTokens(systemPrompt);
|
|
@@ -9733,8 +9503,8 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9733
9503
|
if (!cdpOk) streamLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
|
|
9734
9504
|
`);
|
|
9735
9505
|
const managedUserId = getUserIdForSession(sessionKey);
|
|
9736
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId,
|
|
9737
|
-
const specialistsDir =
|
|
9506
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedLogKey, managedUserId, enabledPlugins) });
|
|
9507
|
+
const specialistsDir = resolve5(accountDir, "specialists");
|
|
9738
9508
|
if (!existsSync5(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9739
9509
|
`);
|
|
9740
9510
|
const fullMessage = attachments.length > 0 ? message + buildAttachmentMetaText(attachments) : message;
|
|
@@ -9764,16 +9534,16 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9764
9534
|
cwd: accountDir,
|
|
9765
9535
|
stdio: ["ignore", "pipe", "pipe"],
|
|
9766
9536
|
// Task 556: STREAM_LOG_PATH inherited by Bash-tool subprocesses.
|
|
9767
|
-
env: buildSpawnEnv(accountId, accountDir,
|
|
9537
|
+
env: buildSpawnEnv(accountId, accountDir, managedLogKey)
|
|
9768
9538
|
});
|
|
9769
|
-
const stderrLog = agentLogStream("claude-agent-stderr", accountDir,
|
|
9539
|
+
const stderrLog = agentLogStream("claude-agent-stderr", accountDir, managedLogKey);
|
|
9770
9540
|
stderrLog.on("error", () => {
|
|
9771
9541
|
});
|
|
9772
9542
|
proc.stderr?.pipe(stderrLog);
|
|
9773
9543
|
teeProcStderrToStreamLog(proc, streamLog);
|
|
9774
9544
|
streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
|
|
9775
9545
|
`);
|
|
9776
|
-
streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${managedConvId} site=managed
|
|
9546
|
+
streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${managedConvId ?? "preflush"} logKey=${managedLogKey} site=managed
|
|
9777
9547
|
`);
|
|
9778
9548
|
if (sessionKey) {
|
|
9779
9549
|
const prev = activeProcesses.get(sessionKey);
|
|
@@ -9783,13 +9553,13 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9783
9553
|
}
|
|
9784
9554
|
activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
|
|
9785
9555
|
}
|
|
9786
|
-
streamLog.write(`[${isoTs()}] [managed-spawn] pid=${proc.pid} sessionKey=${sessionKey} conversationId=${managedConvId} historyMessages=${history.length} pluginDir=${specialistsDir}
|
|
9556
|
+
streamLog.write(`[${isoTs()}] [managed-spawn] pid=${proc.pid} sessionKey=${sessionKey} conversationId=${managedConvId ?? "preflush"} logKey=${managedLogKey} historyMessages=${history.length} pluginDir=${specialistsDir}
|
|
9787
9557
|
`);
|
|
9788
9558
|
streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
|
|
9789
9559
|
`);
|
|
9790
9560
|
proc.on("exit", (code, signal) => {
|
|
9791
|
-
console.log(`[process-exit] pid=${proc.pid} code=${code} signal=${signal} sessionKey=${sessionKey ?? "none"} conversationId=${managedConvId}`);
|
|
9792
|
-
if (!streamLog.destroyed && !streamLog.writableEnded) streamLog.write(`[${isoTs()}] [process-exit] pid=${proc.pid} code=${code} signal=${signal} conversationId=${managedConvId}
|
|
9561
|
+
console.log(`[process-exit] pid=${proc.pid} code=${code} signal=${signal} sessionKey=${sessionKey ?? "none"} conversationId=${managedConvId ?? "preflush"}`);
|
|
9562
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) streamLog.write(`[${isoTs()}] [process-exit] pid=${proc.pid} code=${code} signal=${signal} conversationId=${managedConvId ?? "preflush"}
|
|
9793
9563
|
`);
|
|
9794
9564
|
if (sessionKey) activeProcesses.delete(sessionKey);
|
|
9795
9565
|
});
|
|
@@ -9939,17 +9709,24 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9939
9709
|
`);
|
|
9940
9710
|
const successSession = sessionStore.get(sessionKey);
|
|
9941
9711
|
if (successSession) successSession.lastPeakContextPct = peakContextPct;
|
|
9712
|
+
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9942
9713
|
const convId = sessionStore.get(sessionKey)?.conversationId;
|
|
9943
9714
|
if (convId) {
|
|
9944
|
-
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9945
9715
|
persistMessage(convId, "user", fullMessage, accountId, void 0, userTimestamp).catch(() => {
|
|
9946
9716
|
});
|
|
9947
9717
|
autoLabelSession(convId, fullMessage).catch(() => {
|
|
9948
9718
|
});
|
|
9949
9719
|
if (responseText) persistMessage(convId, "assistant", responseText, accountId, capturedTokens, assistantTimestamp).catch(() => {
|
|
9950
9720
|
});
|
|
9951
|
-
} else
|
|
9952
|
-
|
|
9721
|
+
} else {
|
|
9722
|
+
bufferPendingTurn(sessionKey, { role: "user", content: fullMessage, timestamp: userTimestamp });
|
|
9723
|
+
if (responseText) bufferPendingTurn(sessionKey, { role: "assistant", content: responseText, timestamp: assistantTimestamp, tokens: capturedTokens });
|
|
9724
|
+
const flushedId = await maybeFlushConversationBuffer(sessionKey, "admin", accountId);
|
|
9725
|
+
if (flushedId) {
|
|
9726
|
+
autoLabelSession(flushedId, fullMessage).catch(() => {
|
|
9727
|
+
});
|
|
9728
|
+
yield { type: "conversation_attributed", conversationId: flushedId };
|
|
9729
|
+
}
|
|
9953
9730
|
}
|
|
9954
9731
|
const commitSession = sessionStore.get(sessionKey);
|
|
9955
9732
|
if (commitSession?.pendingCommitmentOffers && commitSession.pendingCommitmentOffers.length > 0) {
|
|
@@ -10021,10 +9798,14 @@ async function* invokePublicAgent(message, systemPrompt, accountId, accountDir,
|
|
|
10021
9798
|
return;
|
|
10022
9799
|
}
|
|
10023
9800
|
const publicConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
|
|
10024
|
-
if (!publicConvId) {
|
|
10025
|
-
throw new Error(`invokePublicAgent: conversationId missing for sessionKey=${sessionKey
|
|
9801
|
+
if (!publicConvId && sessionKey && getPendingTurnCount(sessionKey) >= 2) {
|
|
9802
|
+
throw new Error(`invokePublicAgent: conversationId missing post-flush for sessionKey=${sessionKey.slice(0, 8)} \u2014 maybeFlushConversationBuffer must bind it first`);
|
|
10026
9803
|
}
|
|
10027
|
-
const
|
|
9804
|
+
const publicLogKey = publicConvId ?? (sessionKey ? preflushStreamLogKey(sessionKey) : void 0);
|
|
9805
|
+
if (!publicLogKey) {
|
|
9806
|
+
throw new Error(`invokePublicAgent: sessionKey required \u2014 cannot resolve log stream without one`);
|
|
9807
|
+
}
|
|
9808
|
+
const streamLog = agentLogStream("public-agent-stream", accountDir, publicLogKey);
|
|
10028
9809
|
streamLog.write(`[${isoTs()}] [public-user-message] ${JSON.stringify(message)}
|
|
10029
9810
|
`);
|
|
10030
9811
|
if (sessionKey) {
|
|
@@ -10261,10 +10042,10 @@ User messages are prefixed with the sender's name in brackets. Address participa
|
|
|
10261
10042
|
`);
|
|
10262
10043
|
}
|
|
10263
10044
|
const conversationId = sessionKey ? sessionStore.get(sessionKey)?.conversationId : void 0;
|
|
10045
|
+
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
10046
|
+
const sess = sessionKey ? sessionStore.get(sessionKey) : void 0;
|
|
10047
|
+
const sender = sess?.groupSlug && sess.visitorId && sess.senderDisplayName ? { visitorId: sess.visitorId, displayName: sess.senderDisplayName } : void 0;
|
|
10264
10048
|
if (conversationId) {
|
|
10265
|
-
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
10266
|
-
const sess = sessionKey ? sessionStore.get(sessionKey) : void 0;
|
|
10267
|
-
const sender = sess?.groupSlug && sess.visitorId && sess.senderDisplayName ? { visitorId: sess.visitorId, displayName: sess.senderDisplayName } : void 0;
|
|
10268
10049
|
persistMessage(conversationId, "user", message, accountId, void 0, userTimestamp, sender).catch(() => {
|
|
10269
10050
|
});
|
|
10270
10051
|
autoLabelSession(conversationId, message).catch(() => {
|
|
@@ -10272,7 +10053,14 @@ User messages are prefixed with the sender's name in brackets. Address participa
|
|
|
10272
10053
|
if (fullText) persistMessage(conversationId, "assistant", fullText, accountId, void 0, assistantTimestamp).catch(() => {
|
|
10273
10054
|
});
|
|
10274
10055
|
} else if (sessionKey) {
|
|
10275
|
-
|
|
10056
|
+
bufferPendingTurn(sessionKey, { role: "user", content: message, timestamp: userTimestamp, sender });
|
|
10057
|
+
if (fullText) bufferPendingTurn(sessionKey, { role: "assistant", content: fullText, timestamp: assistantTimestamp });
|
|
10058
|
+
const flushedId = await maybeFlushConversationBuffer(sessionKey, "public", accountId);
|
|
10059
|
+
if (flushedId) {
|
|
10060
|
+
autoLabelSession(flushedId, message).catch(() => {
|
|
10061
|
+
});
|
|
10062
|
+
yield { type: "conversation_attributed", conversationId: flushedId };
|
|
10063
|
+
}
|
|
10276
10064
|
}
|
|
10277
10065
|
streamLog.end();
|
|
10278
10066
|
}
|
|
@@ -10428,7 +10216,7 @@ ${sessionContext}`;
|
|
|
10428
10216
|
console.log(`[onboarding-inject] accountId=${accountId.slice(0, 8)}\u2026 error=neo4j-unreachable injected=false`);
|
|
10429
10217
|
} else if (onboardingStep < 8) {
|
|
10430
10218
|
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 =
|
|
10219
|
+
const skillPath = resolve5(PLATFORM_ROOT3, "plugins/admin/skills/onboarding/SKILL.md");
|
|
10432
10220
|
let skillContent = "";
|
|
10433
10221
|
try {
|
|
10434
10222
|
skillContent = readFileSync6(skillPath, "utf-8");
|
|
@@ -10481,7 +10269,7 @@ ${body}`;
|
|
|
10481
10269
|
|
|
10482
10270
|
${manifest}`;
|
|
10483
10271
|
}
|
|
10484
|
-
const graphRefPath =
|
|
10272
|
+
const graphRefPath = resolve5(PLATFORM_ROOT3, "plugins/memory/references/graph-primitives.md");
|
|
10485
10273
|
try {
|
|
10486
10274
|
const graphRef = readFileSync6(graphRefPath, "utf-8");
|
|
10487
10275
|
baseSystemPrompt += `
|
|
@@ -10569,7 +10357,7 @@ Current session key: ${sessionKey}` : systemPromptBase;
|
|
|
10569
10357
|
|
|
10570
10358
|
${gwParts.join("\n")}`;
|
|
10571
10359
|
}
|
|
10572
|
-
if (sessionKey) {
|
|
10360
|
+
if (sessionKey && isDmChannelSessionKey(sessionKey)) {
|
|
10573
10361
|
try {
|
|
10574
10362
|
await ensureConversation(accountId, agentType, sessionKey, void 0, void 0, sessionUserId);
|
|
10575
10363
|
} catch (err) {
|
|
@@ -10699,7 +10487,7 @@ var clientIpMiddleware = async (c, next) => {
|
|
|
10699
10487
|
|
|
10700
10488
|
// server/routes/health.ts
|
|
10701
10489
|
import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
|
|
10702
|
-
import { createConnection as
|
|
10490
|
+
import { createConnection as createConnection2 } from "net";
|
|
10703
10491
|
|
|
10704
10492
|
// app/lib/network.ts
|
|
10705
10493
|
import { networkInterfaces } from "os";
|
|
@@ -10723,8 +10511,8 @@ function getLanIp() {
|
|
|
10723
10511
|
import { basename as basename2 } from "path";
|
|
10724
10512
|
|
|
10725
10513
|
// 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
|
|
10514
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, statSync as statSync4, mkdirSync as mkdirSync5, renameSync as renameSync2 } from "fs";
|
|
10515
|
+
import { resolve as resolve6, dirname as dirname3 } from "path";
|
|
10728
10516
|
var DEFAULT_SCAN_INTERVAL_MS = 5e3;
|
|
10729
10517
|
var RATE_LIMIT_PATTERN = "rate[- ]?limit(?:ed| reached| hit)|(?:HTTP|status)[^a-z]{0,3}429|too many requests";
|
|
10730
10518
|
var RATE_LIMIT_PATTERN_V1 = "\\b429\\b|rate.?limit|too.?many.?requests";
|
|
@@ -11153,12 +10941,12 @@ function defaultRules() {
|
|
|
11153
10941
|
];
|
|
11154
10942
|
}
|
|
11155
10943
|
function rulesFilePath(configDir2) {
|
|
11156
|
-
return
|
|
10944
|
+
return resolve6(configDir2, "review-rules.json");
|
|
11157
10945
|
}
|
|
11158
10946
|
function ensureRulesFile(configDir2) {
|
|
11159
10947
|
const path2 = rulesFilePath(configDir2);
|
|
11160
10948
|
if (existsSync6(path2)) return { created: false, path: path2 };
|
|
11161
|
-
|
|
10949
|
+
mkdirSync5(dirname3(path2), { recursive: true });
|
|
11162
10950
|
const body = {
|
|
11163
10951
|
scanIntervalMs: DEFAULT_SCAN_INTERVAL_MS,
|
|
11164
10952
|
rules: defaultRules()
|
|
@@ -11336,10 +11124,10 @@ function validateRule(input, label, seenIds) {
|
|
|
11336
11124
|
}
|
|
11337
11125
|
|
|
11338
11126
|
// 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
|
|
11127
|
+
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";
|
|
11128
|
+
import { resolve as resolve7, join as join4, basename, dirname as dirname4 } from "path";
|
|
11341
11129
|
function tailStatePath(configDir2) {
|
|
11342
|
-
return
|
|
11130
|
+
return resolve7(configDir2, "review-state.json");
|
|
11343
11131
|
}
|
|
11344
11132
|
function loadTailState(configDir2) {
|
|
11345
11133
|
const path2 = tailStatePath(configDir2);
|
|
@@ -11363,25 +11151,25 @@ function loadTailState(configDir2) {
|
|
|
11363
11151
|
}
|
|
11364
11152
|
function saveTailState(configDir2, state) {
|
|
11365
11153
|
const path2 = tailStatePath(configDir2);
|
|
11366
|
-
|
|
11154
|
+
mkdirSync6(dirname4(path2), { recursive: true });
|
|
11367
11155
|
const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
|
|
11368
11156
|
writeFileSync7(tmp, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
11369
11157
|
renameSync3(tmp, path2);
|
|
11370
11158
|
}
|
|
11371
11159
|
function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
|
|
11372
11160
|
if (logicalSource === "server") {
|
|
11373
|
-
const p =
|
|
11161
|
+
const p = resolve7(configDir2, "logs", "server.log");
|
|
11374
11162
|
return existsSync7(p) ? [{ logicalSource: "server", filepath: p }] : [];
|
|
11375
11163
|
}
|
|
11376
11164
|
if (logicalSource === "vnc") {
|
|
11377
|
-
const p =
|
|
11165
|
+
const p = resolve7(configDir2, "logs", "vnc-boot.log");
|
|
11378
11166
|
return existsSync7(p) ? [{ logicalSource: "vnc", filepath: p }] : [];
|
|
11379
11167
|
}
|
|
11380
11168
|
if (logicalSource === "cloudflared") {
|
|
11381
11169
|
const files2 = [];
|
|
11382
|
-
const daemon =
|
|
11170
|
+
const daemon = resolve7(configDir2, "logs", "cloudflared.log");
|
|
11383
11171
|
if (existsSync7(daemon)) files2.push({ logicalSource: "cloudflared", filepath: daemon });
|
|
11384
|
-
const login =
|
|
11172
|
+
const login = resolve7(configDir2, "logs", "cloudflared-login.log");
|
|
11385
11173
|
if (existsSync7(login)) files2.push({ logicalSource: "cloudflared", filepath: login });
|
|
11386
11174
|
return files2;
|
|
11387
11175
|
}
|
|
@@ -11508,31 +11296,31 @@ function fileLastWriteMs(path2) {
|
|
|
11508
11296
|
}
|
|
11509
11297
|
}
|
|
11510
11298
|
function accountLogDir(accountDir) {
|
|
11511
|
-
return
|
|
11299
|
+
return resolve7(accountDir, "logs");
|
|
11512
11300
|
}
|
|
11513
11301
|
function sourceKey(file) {
|
|
11514
11302
|
return `${file.logicalSource}:${basename(file.filepath)}`;
|
|
11515
11303
|
}
|
|
11516
11304
|
|
|
11517
11305
|
// app/lib/review-detector/writer.ts
|
|
11518
|
-
import { appendFileSync as
|
|
11519
|
-
import { resolve as
|
|
11306
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync8, renameSync as renameSync4, statSync as statSync6 } from "fs";
|
|
11307
|
+
import { resolve as resolve8, dirname as dirname5 } from "path";
|
|
11520
11308
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
11521
11309
|
function reviewLogPath(configDir2) {
|
|
11522
|
-
return
|
|
11310
|
+
return resolve8(configDir2, "logs", "review.log");
|
|
11523
11311
|
}
|
|
11524
11312
|
function pendingAlertsPath(configDir2) {
|
|
11525
|
-
return
|
|
11313
|
+
return resolve8(configDir2, "review-pending-alerts.jsonl");
|
|
11526
11314
|
}
|
|
11527
11315
|
function reviewLog(configDir2, event) {
|
|
11528
11316
|
const path2 = reviewLogPath(configDir2);
|
|
11529
11317
|
try {
|
|
11530
|
-
|
|
11318
|
+
mkdirSync7(dirname5(path2), { recursive: true });
|
|
11531
11319
|
const line = `${new Date(
|
|
11532
11320
|
typeof event.ts === "number" ? event.ts : Date.now()
|
|
11533
11321
|
).toISOString()} [review] ${JSON.stringify(event)}
|
|
11534
11322
|
`;
|
|
11535
|
-
|
|
11323
|
+
appendFileSync2(path2, line, "utf-8");
|
|
11536
11324
|
} catch (err) {
|
|
11537
11325
|
console.error(`[review] failed to write review log at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11538
11326
|
}
|
|
@@ -11644,9 +11432,9 @@ async function upsertReviewAlert(accountId, match2) {
|
|
|
11644
11432
|
function queueAlert(configDir2, accountId, match2) {
|
|
11645
11433
|
const path2 = pendingAlertsPath(configDir2);
|
|
11646
11434
|
try {
|
|
11647
|
-
|
|
11435
|
+
mkdirSync7(dirname5(path2), { recursive: true });
|
|
11648
11436
|
const line = JSON.stringify({ accountId, match: match2 }) + "\n";
|
|
11649
|
-
|
|
11437
|
+
appendFileSync2(path2, line, "utf-8");
|
|
11650
11438
|
} catch (err) {
|
|
11651
11439
|
console.error(`[review] failed to queue alert at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11652
11440
|
}
|
|
@@ -11777,7 +11565,7 @@ async function bootDetector() {
|
|
|
11777
11565
|
}
|
|
11778
11566
|
|
|
11779
11567
|
// app/lib/review-detector/scan-loop.ts
|
|
11780
|
-
import { resolve as
|
|
11568
|
+
import { resolve as resolve9 } from "path";
|
|
11781
11569
|
|
|
11782
11570
|
// app/lib/review-detector/evaluator.ts
|
|
11783
11571
|
var SAMPLE_MAX_CHARS = 500;
|
|
@@ -12071,14 +11859,14 @@ async function runScanCycle(runtime) {
|
|
|
12071
11859
|
match2 = result.match;
|
|
12072
11860
|
}
|
|
12073
11861
|
} else if (rule.type === "file-write-storm") {
|
|
12074
|
-
const dir =
|
|
11862
|
+
const dir = resolve9(runtime.configDir, rule.watchPath ?? "");
|
|
12075
11863
|
const sinceMs = cycleStart - rule.thresholdWindowMinutes * 6e4;
|
|
12076
11864
|
const count = countRecentWrites(dir, sinceMs);
|
|
12077
11865
|
const result = evaluateFileWriteStormRule(rule, count, state, cycleStart);
|
|
12078
11866
|
state = result.state;
|
|
12079
11867
|
match2 = result.match;
|
|
12080
11868
|
} else if (rule.type === "stale-log") {
|
|
12081
|
-
const trackedPath =
|
|
11869
|
+
const trackedPath = resolve9(runtime.configDir, rule.watchPath ?? "");
|
|
12082
11870
|
const lastMs = fileLastWriteMs(trackedPath);
|
|
12083
11871
|
const result = evaluateStaleLogRule(rule, lastMs, state, cycleStart);
|
|
12084
11872
|
state = result.state;
|
|
@@ -12318,10 +12106,10 @@ var WhatsAppConfigSchema = z.object({
|
|
|
12318
12106
|
|
|
12319
12107
|
// app/lib/whatsapp/config-persist.ts
|
|
12320
12108
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "fs";
|
|
12321
|
-
import { resolve as
|
|
12109
|
+
import { resolve as resolve10, join as join5 } from "path";
|
|
12322
12110
|
var TAG3 = "[whatsapp:config]";
|
|
12323
12111
|
function configPath(accountDir) {
|
|
12324
|
-
return
|
|
12112
|
+
return resolve10(accountDir, "account.json");
|
|
12325
12113
|
}
|
|
12326
12114
|
function readConfig(accountDir) {
|
|
12327
12115
|
const path2 = configPath(accountDir);
|
|
@@ -12776,7 +12564,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
12776
12564
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
12777
12565
|
console.error(`${TAG5} draining credential save queue\u2026`);
|
|
12778
12566
|
const timer = new Promise(
|
|
12779
|
-
(
|
|
12567
|
+
(resolve28) => setTimeout(() => resolve28("timeout"), timeoutMs)
|
|
12780
12568
|
);
|
|
12781
12569
|
const result = await Promise.race([
|
|
12782
12570
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -12904,11 +12692,11 @@ async function createWaSocket(opts) {
|
|
|
12904
12692
|
return sock;
|
|
12905
12693
|
}
|
|
12906
12694
|
async function waitForConnection(sock) {
|
|
12907
|
-
return new Promise((
|
|
12695
|
+
return new Promise((resolve28, reject) => {
|
|
12908
12696
|
const handler = (update) => {
|
|
12909
12697
|
if (update.connection === "open") {
|
|
12910
12698
|
sock.ev.off("connection.update", handler);
|
|
12911
|
-
|
|
12699
|
+
resolve28();
|
|
12912
12700
|
}
|
|
12913
12701
|
if (update.connection === "close") {
|
|
12914
12702
|
sock.ev.off("connection.update", handler);
|
|
@@ -13022,14 +12810,14 @@ ${inspected}`;
|
|
|
13022
12810
|
return inspect2(err, INSPECT_OPTS2);
|
|
13023
12811
|
}
|
|
13024
12812
|
function withTimeout(label, promise, timeoutMs) {
|
|
13025
|
-
return new Promise((
|
|
12813
|
+
return new Promise((resolve28, reject) => {
|
|
13026
12814
|
const timer = setTimeout(() => {
|
|
13027
12815
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
13028
12816
|
}, timeoutMs);
|
|
13029
12817
|
promise.then(
|
|
13030
12818
|
(value) => {
|
|
13031
12819
|
clearTimeout(timer);
|
|
13032
|
-
|
|
12820
|
+
resolve28(value);
|
|
13033
12821
|
},
|
|
13034
12822
|
(err) => {
|
|
13035
12823
|
clearTimeout(timer);
|
|
@@ -13971,6 +13759,14 @@ function deriveSessionKey(input) {
|
|
|
13971
13759
|
if (input.isOwnerMirror || input.agentType === "admin") {
|
|
13972
13760
|
return `whatsapp:${input.accountId}`;
|
|
13973
13761
|
}
|
|
13762
|
+
if (input.isGroup) {
|
|
13763
|
+
if (!input.groupJid) {
|
|
13764
|
+
throw new Error(
|
|
13765
|
+
`deriveSessionKey: isGroup=true requires groupJid (accountId=${input.accountId}, senderPhone=${input.senderPhone})`
|
|
13766
|
+
);
|
|
13767
|
+
}
|
|
13768
|
+
return `whatsapp:${input.accountId}:group:${input.groupJid}`;
|
|
13769
|
+
}
|
|
13974
13770
|
return `whatsapp:${input.accountId}:${input.senderPhone}`;
|
|
13975
13771
|
}
|
|
13976
13772
|
async function init(opts) {
|
|
@@ -14235,11 +14031,11 @@ async function connectWithReconnect(conn) {
|
|
|
14235
14031
|
console.error(
|
|
14236
14032
|
`${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
14237
14033
|
);
|
|
14238
|
-
await new Promise((
|
|
14239
|
-
const timer = setTimeout(
|
|
14034
|
+
await new Promise((resolve28) => {
|
|
14035
|
+
const timer = setTimeout(resolve28, delay);
|
|
14240
14036
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
14241
14037
|
clearTimeout(timer);
|
|
14242
|
-
|
|
14038
|
+
resolve28();
|
|
14243
14039
|
}, { once: true });
|
|
14244
14040
|
});
|
|
14245
14041
|
}
|
|
@@ -14247,16 +14043,16 @@ async function connectWithReconnect(conn) {
|
|
|
14247
14043
|
}
|
|
14248
14044
|
}
|
|
14249
14045
|
function waitForDisconnectEvent(conn) {
|
|
14250
|
-
return new Promise((
|
|
14046
|
+
return new Promise((resolve28) => {
|
|
14251
14047
|
if (!conn.sock) {
|
|
14252
|
-
|
|
14048
|
+
resolve28();
|
|
14253
14049
|
return;
|
|
14254
14050
|
}
|
|
14255
14051
|
const sock = conn.sock;
|
|
14256
14052
|
const handler = (update) => {
|
|
14257
14053
|
if (update.connection === "close") {
|
|
14258
14054
|
sock.ev.off("connection.update", handler);
|
|
14259
|
-
|
|
14055
|
+
resolve28();
|
|
14260
14056
|
}
|
|
14261
14057
|
};
|
|
14262
14058
|
sock.ev.on("connection.update", handler);
|
|
@@ -14442,6 +14238,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
14442
14238
|
agentType: "admin",
|
|
14443
14239
|
accountId: conn.accountId,
|
|
14444
14240
|
senderPhone: senderPhone2,
|
|
14241
|
+
isGroup: isGroup2,
|
|
14242
|
+
groupJid: isGroup2 ? remoteJid : void 0,
|
|
14445
14243
|
isOwnerMirror: true
|
|
14446
14244
|
}),
|
|
14447
14245
|
isOwnerMirror: true
|
|
@@ -14471,8 +14269,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
14471
14269
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
14472
14270
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
14473
14271
|
let resolvePending;
|
|
14474
|
-
const sttPending = new Promise((
|
|
14475
|
-
resolvePending =
|
|
14272
|
+
const sttPending = new Promise((resolve28) => {
|
|
14273
|
+
resolvePending = resolve28;
|
|
14476
14274
|
});
|
|
14477
14275
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
14478
14276
|
try {
|
|
@@ -14525,7 +14323,9 @@ async function handleInboundMessage(conn, msg) {
|
|
|
14525
14323
|
const sessionKey = deriveSessionKey({
|
|
14526
14324
|
agentType: accessResult.agentType,
|
|
14527
14325
|
accountId: conn.accountId,
|
|
14528
|
-
senderPhone
|
|
14326
|
+
senderPhone,
|
|
14327
|
+
isGroup,
|
|
14328
|
+
groupJid: isGroup ? remoteJid : void 0
|
|
14529
14329
|
});
|
|
14530
14330
|
conn.lastMessageAt = Date.now();
|
|
14531
14331
|
const payload = {
|
|
@@ -14583,37 +14383,23 @@ async function probeApiKey() {
|
|
|
14583
14383
|
return result.status;
|
|
14584
14384
|
}
|
|
14585
14385
|
function checkPort(port2, timeoutMs = 500) {
|
|
14586
|
-
return new Promise((
|
|
14587
|
-
const socket =
|
|
14386
|
+
return new Promise((resolve28) => {
|
|
14387
|
+
const socket = createConnection2(port2, "127.0.0.1");
|
|
14588
14388
|
socket.setTimeout(timeoutMs);
|
|
14589
14389
|
socket.once("connect", () => {
|
|
14590
14390
|
socket.destroy();
|
|
14591
|
-
|
|
14391
|
+
resolve28(true);
|
|
14592
14392
|
});
|
|
14593
14393
|
socket.once("error", () => {
|
|
14594
14394
|
socket.destroy();
|
|
14595
|
-
|
|
14395
|
+
resolve28(false);
|
|
14596
14396
|
});
|
|
14597
14397
|
socket.once("timeout", () => {
|
|
14598
14398
|
socket.destroy();
|
|
14599
|
-
|
|
14399
|
+
resolve28(false);
|
|
14600
14400
|
});
|
|
14601
14401
|
});
|
|
14602
14402
|
}
|
|
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
14403
|
var app = new Hono2();
|
|
14618
14404
|
app.get("/", async (c) => {
|
|
14619
14405
|
const browserTransport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
|
|
@@ -14637,7 +14423,6 @@ app.get("/", async (c) => {
|
|
|
14637
14423
|
}
|
|
14638
14424
|
const claudeAuthenticated = authHealth.status === "ok" || authHealth.status === "expiring";
|
|
14639
14425
|
const vncRunning = await checkPort(6080);
|
|
14640
|
-
const terminalReady = await probeTerminalReady();
|
|
14641
14426
|
let apiKeyConfigured = false;
|
|
14642
14427
|
try {
|
|
14643
14428
|
apiKeyConfigured = existsSync10(keyFilePath());
|
|
@@ -14681,7 +14466,6 @@ app.get("/", async (c) => {
|
|
|
14681
14466
|
claude_authenticated: claudeAuthenticated,
|
|
14682
14467
|
...onboardingComplete !== void 0 && { onboarding_complete: onboardingComplete },
|
|
14683
14468
|
vnc_running: vncRunning,
|
|
14684
|
-
terminal_ready: terminalReady,
|
|
14685
14469
|
browser_transport: browserTransport,
|
|
14686
14470
|
auth_status: authHealth.status,
|
|
14687
14471
|
auth_expires_at: authHealth.expiresAt ?? null,
|
|
@@ -14709,14 +14493,14 @@ app.get("/", async (c) => {
|
|
|
14709
14493
|
var health_default = app;
|
|
14710
14494
|
|
|
14711
14495
|
// server/routes/session.ts
|
|
14712
|
-
import { resolve as
|
|
14713
|
-
import { existsSync as existsSync11, writeFileSync as writeFileSync10, mkdirSync as
|
|
14496
|
+
import { resolve as resolve11 } from "path";
|
|
14497
|
+
import { existsSync as existsSync11, writeFileSync as writeFileSync10, mkdirSync as mkdirSync8 } from "fs";
|
|
14714
14498
|
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
14499
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
14716
14500
|
try {
|
|
14717
|
-
const cacheDir =
|
|
14718
|
-
|
|
14719
|
-
writeFileSync10(
|
|
14501
|
+
const cacheDir = resolve11(MAXY_DIR, "branding-cache", accountId);
|
|
14502
|
+
mkdirSync8(cacheDir, { recursive: true });
|
|
14503
|
+
writeFileSync10(resolve11(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
14720
14504
|
} catch (err) {
|
|
14721
14505
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
14722
14506
|
}
|
|
@@ -14786,8 +14570,8 @@ app2.post("/", async (c) => {
|
|
|
14786
14570
|
}
|
|
14787
14571
|
let agentConfig = null;
|
|
14788
14572
|
if (account) {
|
|
14789
|
-
const agentDir =
|
|
14790
|
-
const agentConfigPath =
|
|
14573
|
+
const agentDir = resolve11(account.accountDir, "agents", agentSlug);
|
|
14574
|
+
const agentConfigPath = resolve11(agentDir, "config.json");
|
|
14791
14575
|
if (!existsSync11(agentDir) || !existsSync11(agentConfigPath)) {
|
|
14792
14576
|
return c.json({ error: "Agent not found" }, 404);
|
|
14793
14577
|
}
|
|
@@ -14978,8 +14762,6 @@ app2.post("/", async (c) => {
|
|
|
14978
14762
|
const newVisitorId = visitorId ?? crypto.randomUUID();
|
|
14979
14763
|
const sessionKey = crypto.randomUUID();
|
|
14980
14764
|
registerSession(sessionKey, "public", accountId, agentSlug);
|
|
14981
|
-
ensureConversation(accountId, "public", sessionKey, newVisitorId, agentSlug).catch(() => {
|
|
14982
|
-
});
|
|
14983
14765
|
const hasImage = agentConfig?.image ? "yes" : "no";
|
|
14984
14766
|
console.log(`[session] new-session visitor=${newVisitorId.slice(0, 8)}\u2026 session=${sessionKey.slice(0, 8)}\u2026 agent=${agentSlug} image=${hasImage} showAgentName=${agentConfig?.showAgentName ?? false}`);
|
|
14985
14767
|
return withVisitorCookie(
|
|
@@ -15034,9 +14816,9 @@ ${raw2}`;
|
|
|
15034
14816
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
15035
14817
|
import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
15036
14818
|
import { realpathSync } from "fs";
|
|
15037
|
-
import { resolve as
|
|
15038
|
-
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ??
|
|
15039
|
-
var ATTACHMENTS_ROOT =
|
|
14819
|
+
import { resolve as resolve12, extname, basename as basename3 } from "path";
|
|
14820
|
+
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve12(process.cwd(), "../platform");
|
|
14821
|
+
var ATTACHMENTS_ROOT = resolve12(PLATFORM_ROOT4, "..", "data/uploads");
|
|
15040
14822
|
var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
15041
14823
|
"image/jpeg",
|
|
15042
14824
|
"image/png",
|
|
@@ -15060,11 +14842,11 @@ function assertSupportedMime(mimeType) {
|
|
|
15060
14842
|
}
|
|
15061
14843
|
async function writeAttachment(scope, filename, mimeType, sizeBytes, buffer) {
|
|
15062
14844
|
const attachmentId = randomUUID6();
|
|
15063
|
-
const dir =
|
|
14845
|
+
const dir = resolve12(ATTACHMENTS_ROOT, scope, attachmentId);
|
|
15064
14846
|
await mkdir2(dir, { recursive: true });
|
|
15065
14847
|
const ext = extname(filename) || "";
|
|
15066
|
-
const storagePath =
|
|
15067
|
-
const metaPath =
|
|
14848
|
+
const storagePath = resolve12(dir, `${attachmentId}${ext}`);
|
|
14849
|
+
const metaPath = resolve12(dir, `${attachmentId}.meta.json`);
|
|
15068
14850
|
const meta = {
|
|
15069
14851
|
attachmentId,
|
|
15070
14852
|
scope,
|
|
@@ -15742,13 +15524,13 @@ var group_default = app4;
|
|
|
15742
15524
|
// app/lib/access-gate.ts
|
|
15743
15525
|
import neo4j2 from "neo4j-driver";
|
|
15744
15526
|
import { readFileSync as readFileSync12 } from "fs";
|
|
15745
|
-
import { resolve as
|
|
15527
|
+
import { resolve as resolve13 } from "path";
|
|
15746
15528
|
import { randomUUID as randomUUID7, randomInt } from "crypto";
|
|
15747
|
-
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ??
|
|
15529
|
+
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
|
|
15748
15530
|
var driver2 = null;
|
|
15749
15531
|
function readPassword2() {
|
|
15750
15532
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
15751
|
-
const passwordFile =
|
|
15533
|
+
const passwordFile = resolve13(PLATFORM_ROOT5, "config/.neo4j-password");
|
|
15752
15534
|
try {
|
|
15753
15535
|
return readFileSync12(passwordFile, "utf-8").trim();
|
|
15754
15536
|
} catch {
|
|
@@ -16061,17 +15843,17 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
|
|
|
16061
15843
|
}
|
|
16062
15844
|
|
|
16063
15845
|
// app/lib/brevo-sms.ts
|
|
16064
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as
|
|
15846
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, existsSync as existsSync12, chmodSync } from "fs";
|
|
16065
15847
|
import { dirname as dirname6 } from "path";
|
|
16066
|
-
import { resolve as
|
|
16067
|
-
var BREVO_API_KEY_FILE =
|
|
15848
|
+
import { resolve as resolve14 } from "path";
|
|
15849
|
+
var BREVO_API_KEY_FILE = resolve14(MAXY_DIR, ".brevo-api-key");
|
|
16068
15850
|
var BREVO_API_URL = "https://api.brevo.com/v3/transactionalSMS/sms";
|
|
16069
15851
|
var BREVO_TIMEOUT_MS = 1e4;
|
|
16070
15852
|
var BREVO_SENDER = "Maxy";
|
|
16071
15853
|
var platformRoot = process.env.MAXY_PLATFORM_ROOT;
|
|
16072
15854
|
if (platformRoot) {
|
|
16073
15855
|
try {
|
|
16074
|
-
const brandPath =
|
|
15856
|
+
const brandPath = resolve14(platformRoot, "config", "brand.json");
|
|
16075
15857
|
if (existsSync12(brandPath)) {
|
|
16076
15858
|
const brand = JSON.parse(readFileSync13(brandPath, "utf-8"));
|
|
16077
15859
|
if (brand.productName) BREVO_SENDER = brand.productName;
|
|
@@ -16708,7 +16490,7 @@ app6.post("/webhook", async (c) => {
|
|
|
16708
16490
|
var telegram_default = app6;
|
|
16709
16491
|
|
|
16710
16492
|
// server/routes/whatsapp.ts
|
|
16711
|
-
import { join as join8, resolve as
|
|
16493
|
+
import { join as join8, resolve as resolve15, basename as basename4 } from "path";
|
|
16712
16494
|
import { readFile as readFile2, stat as stat3 } from "fs/promises";
|
|
16713
16495
|
import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
|
|
16714
16496
|
|
|
@@ -16816,8 +16598,8 @@ async function startLogin(opts) {
|
|
|
16816
16598
|
resetActiveLogin(accountId);
|
|
16817
16599
|
let resolveQr = null;
|
|
16818
16600
|
let rejectQr = null;
|
|
16819
|
-
const qrPromise = new Promise((
|
|
16820
|
-
resolveQr =
|
|
16601
|
+
const qrPromise = new Promise((resolve28, reject) => {
|
|
16602
|
+
resolveQr = resolve28;
|
|
16821
16603
|
rejectQr = reject;
|
|
16822
16604
|
});
|
|
16823
16605
|
const qrTimer = setTimeout(
|
|
@@ -17188,14 +16970,14 @@ app7.post("/config", async (c) => {
|
|
|
17188
16970
|
return c.json({ ok: true, slug: currentSlug });
|
|
17189
16971
|
}
|
|
17190
16972
|
case "list-public-agents": {
|
|
17191
|
-
const agentsDir =
|
|
16973
|
+
const agentsDir = resolve15(account.accountDir, "agents");
|
|
17192
16974
|
const agents = [];
|
|
17193
16975
|
if (existsSync14(agentsDir)) {
|
|
17194
16976
|
try {
|
|
17195
16977
|
const entries = readdirSync4(agentsDir, { withFileTypes: true });
|
|
17196
16978
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
17197
16979
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
17198
|
-
const configPath2 =
|
|
16980
|
+
const configPath2 = resolve15(agentsDir, entry.name, "config.json");
|
|
17199
16981
|
if (!existsSync14(configPath2)) continue;
|
|
17200
16982
|
try {
|
|
17201
16983
|
const config = JSON.parse(readFileSync15(configPath2, "utf-8"));
|
|
@@ -17273,7 +17055,7 @@ app7.post("/send-document", async (c) => {
|
|
|
17273
17055
|
if (!maxyAccountId || !PLATFORM_ROOT6) {
|
|
17274
17056
|
return c.json({ error: "Cannot validate file path: missing account or platform context" }, 400);
|
|
17275
17057
|
}
|
|
17276
|
-
const accountDir =
|
|
17058
|
+
const accountDir = resolve15(PLATFORM_ROOT6, "..", "data/accounts", maxyAccountId);
|
|
17277
17059
|
let resolvedPath;
|
|
17278
17060
|
try {
|
|
17279
17061
|
resolvedPath = realpathSync2(filePath);
|
|
@@ -17410,8 +17192,8 @@ var whatsapp_default = app7;
|
|
|
17410
17192
|
|
|
17411
17193
|
// server/routes/onboarding.ts
|
|
17412
17194
|
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
|
|
17195
|
+
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";
|
|
17196
|
+
import { resolve as resolve16, dirname as dirname7 } from "path";
|
|
17415
17197
|
import { createHash, randomUUID as randomUUID9 } from "crypto";
|
|
17416
17198
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
17417
17199
|
function hashPin(pin) {
|
|
@@ -17524,7 +17306,7 @@ app8.post("/set-pin", async (c) => {
|
|
|
17524
17306
|
}
|
|
17525
17307
|
const hash = hashPin(body.pin);
|
|
17526
17308
|
const userId = randomUUID9();
|
|
17527
|
-
|
|
17309
|
+
mkdirSync10(dirname7(USERS_FILE), { recursive: true });
|
|
17528
17310
|
writeFileSync12(USERS_FILE, JSON.stringify([{ userId, name: "Owner", pin: hash }]), { mode: 384 });
|
|
17529
17311
|
console.log(`[set-pin] created users.json: userId=${userId.slice(0, 8)}\u2026 hash=${hash.slice(0, 8)}\u2026`);
|
|
17530
17312
|
const account = resolveAccount();
|
|
@@ -17585,7 +17367,7 @@ app8.post("/skip", async (c) => {
|
|
|
17585
17367
|
}
|
|
17586
17368
|
const { accountId, accountDir } = account;
|
|
17587
17369
|
let agentName = "Maxy";
|
|
17588
|
-
const brandPath = PLATFORM_ROOT7 ?
|
|
17370
|
+
const brandPath = PLATFORM_ROOT7 ? resolve16(PLATFORM_ROOT7, "config", "brand.json") : "";
|
|
17589
17371
|
if (brandPath && existsSync15(brandPath)) {
|
|
17590
17372
|
try {
|
|
17591
17373
|
const brand = JSON.parse(readFileSync16(brandPath, "utf-8"));
|
|
@@ -17594,9 +17376,9 @@ app8.post("/skip", async (c) => {
|
|
|
17594
17376
|
console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
17595
17377
|
}
|
|
17596
17378
|
}
|
|
17597
|
-
const soulPath =
|
|
17379
|
+
const soulPath = resolve16(accountDir, "agents", "admin", "SOUL.md");
|
|
17598
17380
|
try {
|
|
17599
|
-
|
|
17381
|
+
mkdirSync10(dirname7(soulPath), { recursive: true });
|
|
17600
17382
|
writeFileSync12(soulPath, `You are ${agentName}, an AI operations manager.
|
|
17601
17383
|
`);
|
|
17602
17384
|
console.log(`[onboarding-skip] wrote SOUL.md: ${soulPath}`);
|
|
@@ -17635,7 +17417,7 @@ app8.post("/skip", async (c) => {
|
|
|
17635
17417
|
var onboarding_default = app8;
|
|
17636
17418
|
|
|
17637
17419
|
// server/routes/client-error.ts
|
|
17638
|
-
import { appendFileSync as
|
|
17420
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as renameSync5, statSync as statSync7 } from "fs";
|
|
17639
17421
|
import { join as join9 } from "path";
|
|
17640
17422
|
var CLIENT_ERRORS_LOG = join9(LOG_DIR, "client-errors.log");
|
|
17641
17423
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
@@ -17772,7 +17554,7 @@ app9.post("/", async (c) => {
|
|
|
17772
17554
|
tag: typeof body.tag === "string" ? truncate2(body.tag, 32) : void 0,
|
|
17773
17555
|
status: typeof body.status === "number" ? body.status : void 0
|
|
17774
17556
|
};
|
|
17775
|
-
|
|
17557
|
+
appendFileSync3(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
|
|
17776
17558
|
} catch (err) {
|
|
17777
17559
|
console.error(`[client-error] append failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
17778
17560
|
}
|
|
@@ -17811,11 +17593,7 @@ async function createAdminSession(accountId, thinkingView, userId, userName) {
|
|
|
17811
17593
|
businessName = branding?.name || void 0;
|
|
17812
17594
|
} catch {
|
|
17813
17595
|
}
|
|
17814
|
-
|
|
17815
|
-
if (userId) {
|
|
17816
|
-
conversationId = await createNewAdminConversation(userId, accountId, sessionKey);
|
|
17817
|
-
}
|
|
17818
|
-
console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId} conversationId=${conversationId?.slice(0, 8) ?? "\u2013"} sessionKey=${sessionKey.slice(0, 8)}`);
|
|
17596
|
+
console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId} conversationId=deferred sessionKey=${sessionKey.slice(0, 8)}`);
|
|
17819
17597
|
return {
|
|
17820
17598
|
session_key: sessionKey,
|
|
17821
17599
|
agent_id: "admin",
|
|
@@ -17824,7 +17602,7 @@ async function createAdminSession(accountId, thinkingView, userId, userName) {
|
|
|
17824
17602
|
thinkingView: effectiveThinkingView,
|
|
17825
17603
|
onboardingComplete,
|
|
17826
17604
|
businessName,
|
|
17827
|
-
conversationId
|
|
17605
|
+
conversationId: null
|
|
17828
17606
|
};
|
|
17829
17607
|
}
|
|
17830
17608
|
var app10 = new Hono2();
|
|
@@ -17919,11 +17697,11 @@ app10.post("/", async (c) => {
|
|
|
17919
17697
|
var session_default2 = app10;
|
|
17920
17698
|
|
|
17921
17699
|
// server/routes/admin/chat.ts
|
|
17922
|
-
import { resolve as
|
|
17700
|
+
import { resolve as resolve17 } from "path";
|
|
17923
17701
|
|
|
17924
17702
|
// app/lib/script-stream-tailer.ts
|
|
17925
17703
|
import * as childProcess from "child_process";
|
|
17926
|
-
import { appendFileSync as
|
|
17704
|
+
import { appendFileSync as appendFileSync4, createReadStream as createReadStream2, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
|
|
17927
17705
|
import { dirname as dirname8 } from "path";
|
|
17928
17706
|
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
17929
17707
|
var SCRIPT_STREAM_RE = /^\[([^\]]+)\] \[script:([a-z][a-z0-9-]*)((?::[a-z0-9:_-]+)?)\] (.*)$/;
|
|
@@ -18033,8 +17811,8 @@ function writeRouteMilestone(streamLogPath, scope, line) {
|
|
|
18033
17811
|
}
|
|
18034
17812
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
18035
17813
|
try {
|
|
18036
|
-
|
|
18037
|
-
|
|
17814
|
+
mkdirSync11(dirname8(streamLogPath), { recursive: true });
|
|
17815
|
+
appendFileSync4(streamLogPath, `[${ts}] [script:${scope}] ${line}
|
|
18038
17816
|
`);
|
|
18039
17817
|
} catch (err) {
|
|
18040
17818
|
console.error(
|
|
@@ -18364,7 +18142,7 @@ app11.post("/", requireAdminSession, async (c) => {
|
|
|
18364
18142
|
try {
|
|
18365
18143
|
registerAdminSSE(sseEntry);
|
|
18366
18144
|
if (sseConvId) {
|
|
18367
|
-
const streamLogPath =
|
|
18145
|
+
const streamLogPath = resolve17(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
|
|
18368
18146
|
tailer = startScriptStreamTailer({
|
|
18369
18147
|
path: streamLogPath,
|
|
18370
18148
|
onEvent: (event) => {
|
|
@@ -18490,7 +18268,7 @@ var compact_default = app12;
|
|
|
18490
18268
|
|
|
18491
18269
|
// server/routes/admin/logs.ts
|
|
18492
18270
|
import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync18, statSync as statSync9 } from "fs";
|
|
18493
|
-
import { resolve as
|
|
18271
|
+
import { resolve as resolve18, basename as basename5 } from "path";
|
|
18494
18272
|
var TAIL_BYTES = 8192;
|
|
18495
18273
|
var app13 = new Hono2();
|
|
18496
18274
|
app13.get("/", async (c) => {
|
|
@@ -18499,13 +18277,13 @@ app13.get("/", async (c) => {
|
|
|
18499
18277
|
const conversationIdParam = c.req.query("conversationId");
|
|
18500
18278
|
const download = c.req.query("download") === "1";
|
|
18501
18279
|
const account = resolveAccount();
|
|
18502
|
-
const accountLogDir2 = account ?
|
|
18280
|
+
const accountLogDir2 = account ? resolve18(account.accountDir, "logs") : null;
|
|
18503
18281
|
if (fileParam) {
|
|
18504
18282
|
const safe = basename5(fileParam);
|
|
18505
18283
|
const searched = [];
|
|
18506
18284
|
for (const dir of [accountLogDir2, LOG_DIR]) {
|
|
18507
18285
|
if (!dir) continue;
|
|
18508
|
-
const filePath =
|
|
18286
|
+
const filePath = resolve18(dir, safe);
|
|
18509
18287
|
searched.push(filePath);
|
|
18510
18288
|
try {
|
|
18511
18289
|
const content = readFileSync18(filePath, "utf-8");
|
|
@@ -18547,7 +18325,7 @@ app13.get("/", async (c) => {
|
|
|
18547
18325
|
const searched = [];
|
|
18548
18326
|
for (const dir of [accountLogDir2, LOG_DIR]) {
|
|
18549
18327
|
if (!dir) continue;
|
|
18550
|
-
const filePath =
|
|
18328
|
+
const filePath = resolve18(dir, fileName);
|
|
18551
18329
|
searched.push(filePath);
|
|
18552
18330
|
try {
|
|
18553
18331
|
const content = readFileSync18(filePath, "utf-8");
|
|
@@ -18574,10 +18352,10 @@ app13.get("/", async (c) => {
|
|
|
18574
18352
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
18575
18353
|
continue;
|
|
18576
18354
|
}
|
|
18577
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(
|
|
18355
|
+
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
18356
|
seen.add(name);
|
|
18579
18357
|
try {
|
|
18580
|
-
const content = readFileSync18(
|
|
18358
|
+
const content = readFileSync18(resolve18(dir, name));
|
|
18581
18359
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
18582
18360
|
logs[name] = tail.trim() || "(empty)";
|
|
18583
18361
|
} catch (err) {
|
|
@@ -18618,7 +18396,7 @@ var claude_info_default = app14;
|
|
|
18618
18396
|
// server/routes/admin/attachment.ts
|
|
18619
18397
|
import { readFile as readFile3, readdir } from "fs/promises";
|
|
18620
18398
|
import { existsSync as existsSync19 } from "fs";
|
|
18621
|
-
import { resolve as
|
|
18399
|
+
import { resolve as resolve19 } from "path";
|
|
18622
18400
|
var app15 = new Hono2();
|
|
18623
18401
|
app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
18624
18402
|
const attachmentId = c.req.param("attachmentId");
|
|
@@ -18630,11 +18408,11 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
18630
18408
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
|
|
18631
18409
|
return new Response("Not found", { status: 404 });
|
|
18632
18410
|
}
|
|
18633
|
-
const dir =
|
|
18411
|
+
const dir = resolve19(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
18634
18412
|
if (!existsSync19(dir)) {
|
|
18635
18413
|
return new Response("Not found", { status: 404 });
|
|
18636
18414
|
}
|
|
18637
|
-
const metaPath =
|
|
18415
|
+
const metaPath = resolve19(dir, `${attachmentId}.meta.json`);
|
|
18638
18416
|
if (!existsSync19(metaPath)) {
|
|
18639
18417
|
return new Response("Not found", { status: 404 });
|
|
18640
18418
|
}
|
|
@@ -18649,7 +18427,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
18649
18427
|
if (!dataFile) {
|
|
18650
18428
|
return new Response("Not found", { status: 404 });
|
|
18651
18429
|
}
|
|
18652
|
-
const filePath =
|
|
18430
|
+
const filePath = resolve19(dir, dataFile);
|
|
18653
18431
|
const buffer = await readFile3(filePath);
|
|
18654
18432
|
return new Response(new Uint8Array(buffer), {
|
|
18655
18433
|
headers: {
|
|
@@ -18663,7 +18441,7 @@ var attachment_default = app15;
|
|
|
18663
18441
|
|
|
18664
18442
|
// server/routes/admin/account.ts
|
|
18665
18443
|
import { readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
|
|
18666
|
-
import { resolve as
|
|
18444
|
+
import { resolve as resolve20 } from "path";
|
|
18667
18445
|
var VALID_CONTEXT_MODES = ["managed", "claude-code"];
|
|
18668
18446
|
var app16 = new Hono2();
|
|
18669
18447
|
app16.patch("/", requireAdminSession, async (c) => {
|
|
@@ -18679,7 +18457,7 @@ app16.patch("/", requireAdminSession, async (c) => {
|
|
|
18679
18457
|
}
|
|
18680
18458
|
const account = resolveAccount();
|
|
18681
18459
|
if (!account) return c.json({ error: "No account configured" }, 500);
|
|
18682
|
-
const configPath2 =
|
|
18460
|
+
const configPath2 = resolve20(account.accountDir, "account.json");
|
|
18683
18461
|
try {
|
|
18684
18462
|
const raw2 = readFileSync19(configPath2, "utf-8");
|
|
18685
18463
|
const config = JSON.parse(raw2);
|
|
@@ -18695,13 +18473,13 @@ app16.patch("/", requireAdminSession, async (c) => {
|
|
|
18695
18473
|
var account_default = app16;
|
|
18696
18474
|
|
|
18697
18475
|
// server/routes/admin/agents.ts
|
|
18698
|
-
import { resolve as
|
|
18476
|
+
import { resolve as resolve21 } from "path";
|
|
18699
18477
|
import { readdirSync as readdirSync6, readFileSync as readFileSync20, existsSync as existsSync20, rmSync as rmSync3 } from "fs";
|
|
18700
18478
|
var app17 = new Hono2();
|
|
18701
18479
|
app17.get("/", (c) => {
|
|
18702
18480
|
const account = resolveAccount();
|
|
18703
18481
|
if (!account) return c.json({ agents: [] });
|
|
18704
|
-
const agentsDir =
|
|
18482
|
+
const agentsDir = resolve21(account.accountDir, "agents");
|
|
18705
18483
|
if (!existsSync20(agentsDir)) return c.json({ agents: [] });
|
|
18706
18484
|
const agents = [];
|
|
18707
18485
|
try {
|
|
@@ -18709,7 +18487,7 @@ app17.get("/", (c) => {
|
|
|
18709
18487
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
18710
18488
|
if (!entry.isDirectory()) continue;
|
|
18711
18489
|
if (entry.name === "admin") continue;
|
|
18712
|
-
const configPath2 =
|
|
18490
|
+
const configPath2 = resolve21(agentsDir, entry.name, "config.json");
|
|
18713
18491
|
if (!existsSync20(configPath2)) continue;
|
|
18714
18492
|
try {
|
|
18715
18493
|
const config = JSON.parse(readFileSync20(configPath2, "utf-8"));
|
|
@@ -18738,7 +18516,7 @@ app17.delete("/:slug", (c) => {
|
|
|
18738
18516
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
18739
18517
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
18740
18518
|
}
|
|
18741
|
-
const agentDir =
|
|
18519
|
+
const agentDir = resolve21(account.accountDir, "agents", slug);
|
|
18742
18520
|
if (!existsSync20(agentDir)) {
|
|
18743
18521
|
return c.json({ error: "Agent not found" }, 404);
|
|
18744
18522
|
}
|
|
@@ -18755,8 +18533,8 @@ var agents_default = app17;
|
|
|
18755
18533
|
|
|
18756
18534
|
// server/routes/admin/version.ts
|
|
18757
18535
|
import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
|
|
18758
|
-
import { resolve as
|
|
18759
|
-
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ??
|
|
18536
|
+
import { resolve as resolve22, join as join10 } from "path";
|
|
18537
|
+
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
|
|
18760
18538
|
var brandHostname = "maxy";
|
|
18761
18539
|
var brandNpmPackage = "@rubytech/create-maxy";
|
|
18762
18540
|
var brandJsonPath = join10(PLATFORM_ROOT8, "config", "brand.json");
|
|
@@ -18768,7 +18546,7 @@ if (existsSync21(brandJsonPath)) {
|
|
|
18768
18546
|
} catch {
|
|
18769
18547
|
}
|
|
18770
18548
|
}
|
|
18771
|
-
var VERSION_FILE =
|
|
18549
|
+
var VERSION_FILE = resolve22(PLATFORM_ROOT8, `config/.${brandHostname}-version`);
|
|
18772
18550
|
var NPM_PACKAGE = brandNpmPackage;
|
|
18773
18551
|
var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
|
|
18774
18552
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -18884,16 +18662,10 @@ app19.post("/new", requireAdminSession, async (c) => {
|
|
|
18884
18662
|
const newSessionKey = crypto3.randomUUID();
|
|
18885
18663
|
const userName = getUserNameForSession(oldSessionKey);
|
|
18886
18664
|
registerSession(newSessionKey, "admin", accountId, void 0, userId, userName);
|
|
18887
|
-
const conversationId = await createNewAdminConversation(userId, accountId, newSessionKey);
|
|
18888
|
-
if (!conversationId) {
|
|
18889
|
-
unregisterSession(newSessionKey);
|
|
18890
|
-
console.error(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} new-conversation-failed: userId=${userId} accountId=${accountId.slice(0, 8)}\u2026 oldSessionKey=${oldSessionKey.slice(0, 8)}\u2026 newSessionKey=${newSessionKey.slice(0, 8)}\u2026`);
|
|
18891
|
-
return c.json({ error: "Failed to create conversation" }, 500);
|
|
18892
|
-
}
|
|
18893
18665
|
const previousConversationId = clearSessionHistory(oldSessionKey);
|
|
18894
18666
|
unregisterSession(oldSessionKey);
|
|
18895
|
-
console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} session reset for new conversation: oldSessionKey=${oldSessionKey.slice(0, 8)}\u2026 newSessionKey=${newSessionKey.slice(0, 8)}\u2026 previousConversationId=${previousConversationId?.slice(0, 8) ?? "none"}\u2026 newConversationId
|
|
18896
|
-
return c.json({ session_key: newSessionKey, conversationId });
|
|
18667
|
+
console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} session reset for new conversation: oldSessionKey=${oldSessionKey.slice(0, 8)}\u2026 newSessionKey=${newSessionKey.slice(0, 8)}\u2026 previousConversationId=${previousConversationId?.slice(0, 8) ?? "none"}\u2026 newConversationId=deferred`);
|
|
18668
|
+
return c.json({ session_key: newSessionKey, conversationId: null });
|
|
18897
18669
|
});
|
|
18898
18670
|
app19.delete("/:id", requireAdminSession, async (c) => {
|
|
18899
18671
|
const conversationId = c.req.param("id");
|
|
@@ -19268,7 +19040,7 @@ var events_default = app23;
|
|
|
19268
19040
|
|
|
19269
19041
|
// server/routes/admin/cloudflare.ts
|
|
19270
19042
|
import { homedir as homedir3 } from "os";
|
|
19271
|
-
import { resolve as
|
|
19043
|
+
import { resolve as resolve24 } from "path";
|
|
19272
19044
|
import { readFileSync as readFileSync23 } from "fs";
|
|
19273
19045
|
|
|
19274
19046
|
// app/lib/dns-label.ts
|
|
@@ -19285,10 +19057,10 @@ function isValidDomain(value) {
|
|
|
19285
19057
|
}
|
|
19286
19058
|
|
|
19287
19059
|
// app/lib/alias-domains.ts
|
|
19288
|
-
import { existsSync as existsSync22, mkdirSync as
|
|
19060
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
|
|
19289
19061
|
import { dirname as dirname9 } from "path";
|
|
19290
|
-
import { resolve as
|
|
19291
|
-
var ALIAS_DOMAINS_PATH =
|
|
19062
|
+
import { resolve as resolve23 } from "path";
|
|
19063
|
+
var ALIAS_DOMAINS_PATH = resolve23(MAXY_DIR, "alias-domains.json");
|
|
19292
19064
|
function readExisting() {
|
|
19293
19065
|
if (!existsSync22(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
|
|
19294
19066
|
try {
|
|
@@ -19303,7 +19075,7 @@ function addAliasDomain(hostname2) {
|
|
|
19303
19075
|
const existing = readExisting();
|
|
19304
19076
|
if (existing.has(hostname2)) return;
|
|
19305
19077
|
existing.add(hostname2);
|
|
19306
|
-
|
|
19078
|
+
mkdirSync12(dirname9(ALIAS_DOMAINS_PATH), { recursive: true });
|
|
19307
19079
|
writeFileSync14(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
|
|
19308
19080
|
}
|
|
19309
19081
|
|
|
@@ -19311,8 +19083,8 @@ function addAliasDomain(hostname2) {
|
|
|
19311
19083
|
var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
19312
19084
|
var DOMAINS_TIMEOUT_MS = 40 * 1e3;
|
|
19313
19085
|
function loadBrandInfo() {
|
|
19314
|
-
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
19315
|
-
const brandPath =
|
|
19086
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
|
|
19087
|
+
const brandPath = resolve24(platformRoot2, "config", "brand.json");
|
|
19316
19088
|
try {
|
|
19317
19089
|
const parsed = JSON.parse(readFileSync23(brandPath, "utf-8"));
|
|
19318
19090
|
const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
|
|
@@ -19417,7 +19189,7 @@ app24.get("/domains", requireAdminSession, async (c) => {
|
|
|
19417
19189
|
streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
|
|
19418
19190
|
log2(`phase=stream-log-resolved path=${streamLogPath}`);
|
|
19419
19191
|
const brand = loadBrandInfo();
|
|
19420
|
-
const scriptPath =
|
|
19192
|
+
const scriptPath = resolve24(homedir3(), "list-cf-domains.sh");
|
|
19421
19193
|
const result = await runFormSpawn({
|
|
19422
19194
|
scriptPath,
|
|
19423
19195
|
args: [brand.hostname],
|
|
@@ -19542,7 +19314,7 @@ app24.post("/setup", requireAdminSession, async (c) => {
|
|
|
19542
19314
|
}
|
|
19543
19315
|
streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
|
|
19544
19316
|
log2(`phase=stream-log-resolved path=${streamLogPath}`);
|
|
19545
|
-
const scriptPath =
|
|
19317
|
+
const scriptPath = resolve24(homedir3(), "setup-tunnel.sh");
|
|
19546
19318
|
const args = [brand.hostname, String(port2), adminFqdn];
|
|
19547
19319
|
if (publicFqdn) args.push(publicFqdn);
|
|
19548
19320
|
if (apex) args.push(apex);
|
|
@@ -19611,17 +19383,17 @@ var cloudflare_default = app24;
|
|
|
19611
19383
|
import { createReadStream as createReadStream3 } from "fs";
|
|
19612
19384
|
import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
19613
19385
|
import { realpathSync as realpathSync4 } from "fs";
|
|
19614
|
-
import { basename as basename6, dirname as dirname10, join as join11, resolve as
|
|
19386
|
+
import { basename as basename6, dirname as dirname10, join as join11, resolve as resolve26, sep as sep2 } from "path";
|
|
19615
19387
|
import { Readable as Readable3 } from "stream";
|
|
19616
19388
|
|
|
19617
19389
|
// app/lib/data-path.ts
|
|
19618
19390
|
import { realpathSync as realpathSync3 } from "fs";
|
|
19619
|
-
import { resolve as
|
|
19620
|
-
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ??
|
|
19621
|
-
var DATA_ROOT =
|
|
19391
|
+
import { resolve as resolve25, normalize, sep, relative } from "path";
|
|
19392
|
+
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "../platform");
|
|
19393
|
+
var DATA_ROOT = resolve25(PLATFORM_ROOT9, "..", "data");
|
|
19622
19394
|
function resolveDataPath(raw2) {
|
|
19623
19395
|
const cleaned = normalize("/" + (raw2 ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
19624
|
-
const absolute =
|
|
19396
|
+
const absolute = resolve25(DATA_ROOT, cleaned);
|
|
19625
19397
|
let dataRootReal;
|
|
19626
19398
|
try {
|
|
19627
19399
|
dataRootReal = realpathSync3(DATA_ROOT);
|
|
@@ -19899,7 +19671,7 @@ async function readMeta(absDir, baseName) {
|
|
|
19899
19671
|
}
|
|
19900
19672
|
async function readAccountNames() {
|
|
19901
19673
|
const map = /* @__PURE__ */ new Map();
|
|
19902
|
-
const accountsDir =
|
|
19674
|
+
const accountsDir = resolve26(DATA_ROOT, "accounts");
|
|
19903
19675
|
let names;
|
|
19904
19676
|
try {
|
|
19905
19677
|
names = await readdir2(accountsDir);
|
|
@@ -19908,7 +19680,7 @@ async function readAccountNames() {
|
|
|
19908
19680
|
}
|
|
19909
19681
|
for (const name of names) {
|
|
19910
19682
|
if (!UUID_RE3.test(name)) continue;
|
|
19911
|
-
const configPath2 =
|
|
19683
|
+
const configPath2 = resolve26(accountsDir, name, "account.json");
|
|
19912
19684
|
try {
|
|
19913
19685
|
const raw2 = await readFile4(configPath2, "utf8");
|
|
19914
19686
|
const parsed = JSON.parse(raw2);
|
|
@@ -20099,8 +19871,8 @@ app25.post("/upload", requireAdminSession, async (c) => {
|
|
|
20099
19871
|
}
|
|
20100
19872
|
const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
|
|
20101
19873
|
const finalName = `${Date.now()}-${safeName}`;
|
|
20102
|
-
const destDir =
|
|
20103
|
-
const destPath =
|
|
19874
|
+
const destDir = resolve26(DATA_ROOT, "uploads", accountId);
|
|
19875
|
+
const destPath = resolve26(destDir, finalName);
|
|
20104
19876
|
try {
|
|
20105
19877
|
await mkdir3(destDir, { recursive: true });
|
|
20106
19878
|
const dataRootReal = realpathSync4(DATA_ROOT);
|
|
@@ -20253,24 +20025,35 @@ var GRAPH_LABEL_COLOURS = {
|
|
|
20253
20025
|
Review: "#059669",
|
|
20254
20026
|
ImageObject: "#6EE7B7",
|
|
20255
20027
|
// Conversational
|
|
20028
|
+
//
|
|
20029
|
+
// Task 649 palette — the four sublabelled conversation/message labels
|
|
20030
|
+
// (AdminConversation, PublicConversation, UserMessage, AssistantMessage)
|
|
20031
|
+
// are assigned four pairwise-distinguishable hues: indigo / purple /
|
|
20032
|
+
// orange / yellow. The Task 633 violet-shade split (darker for admin,
|
|
20033
|
+
// lighter for public) was indistinguishable on canvas and in the legend;
|
|
20034
|
+
// hue-shift replaces lightness-shift.
|
|
20035
|
+
//
|
|
20036
|
+
// Constraints honoured:
|
|
20037
|
+
// - Pairwise distinct among the four in-family labels.
|
|
20038
|
+
// - UserMessage=#F97316 reserved (Person/Preference orange cue) —
|
|
20039
|
+
// the other three are non-orange.
|
|
20040
|
+
// - No collision with Task/Project/Event pinks (#DB2777/#BE185D/#EC4899),
|
|
20041
|
+
// Knowledge greens, Workflow cyans, LocalBusiness blues.
|
|
20042
|
+
// - Conversation base (#7C3AED) kept as pre-backfill fallback for
|
|
20043
|
+
// nodes that pre-date the AdminConversation/PublicConversation split.
|
|
20044
|
+
// - Message base reassigned away from old #A78BFA to avoid near-
|
|
20045
|
+
// collision with the new PublicConversation=#A855F7; slate signals
|
|
20046
|
+
// "legacy/system/tool" — nodes carrying only :Message without a
|
|
20047
|
+
// role-sublabel.
|
|
20048
|
+
// - Out of family (intentionally not touched): OnboardingState=#8B5CF6
|
|
20049
|
+
// sits in the violet neighbourhood; palette reassignment outside
|
|
20050
|
+
// the conversation/message family is out of scope for Task 649.
|
|
20256
20051
|
Conversation: "#7C3AED",
|
|
20257
|
-
|
|
20258
|
-
|
|
20259
|
-
|
|
20260
|
-
// alone. Both within the Conversation family — the hue shift is subtle
|
|
20261
|
-
// enough to stay legible on adjacent nodes yet distinct enough to be
|
|
20262
|
-
// unmistakable in the popover legend.
|
|
20263
|
-
AdminConversation: "#5B21B6",
|
|
20264
|
-
PublicConversation: "#A78BFA",
|
|
20265
|
-
Message: "#A78BFA",
|
|
20266
|
-
// Task 633 — user vs assistant disambiguation on /graph. UserMessage
|
|
20267
|
-
// picks up the Person/Preference orange family cue (the user is a
|
|
20268
|
-
// person); AssistantMessage stays violet-adjacent to reinforce the
|
|
20269
|
-
// AdminConversation cue (both are admin-side). system/tool Messages
|
|
20270
|
-
// (not written by the persist path today; reserved shape) stay on the
|
|
20271
|
-
// base `:Message` colour.
|
|
20052
|
+
AdminConversation: "#4338CA",
|
|
20053
|
+
PublicConversation: "#A855F7",
|
|
20054
|
+
Message: "#64748B",
|
|
20272
20055
|
UserMessage: "#F97316",
|
|
20273
|
-
AssistantMessage: "#
|
|
20056
|
+
AssistantMessage: "#FACC15",
|
|
20274
20057
|
ToolCall: "#C4B5FD",
|
|
20275
20058
|
// Tasks / projects / events
|
|
20276
20059
|
Task: "#DB2777",
|
|
@@ -21405,8 +21188,8 @@ app35.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
21405
21188
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
21406
21189
|
return c.text("Not found", 404);
|
|
21407
21190
|
}
|
|
21408
|
-
const filePath =
|
|
21409
|
-
const expectedDir =
|
|
21191
|
+
const filePath = resolve27(account.accountDir, "agents", slug, "assets", filename);
|
|
21192
|
+
const expectedDir = resolve27(account.accountDir, "agents", slug, "assets");
|
|
21410
21193
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
21411
21194
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
21412
21195
|
return c.text("Forbidden", 403);
|
|
@@ -21435,8 +21218,8 @@ app35.get("/generated/:filename", (c) => {
|
|
|
21435
21218
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
21436
21219
|
return c.text("Not found", 404);
|
|
21437
21220
|
}
|
|
21438
|
-
const filePath =
|
|
21439
|
-
const expectedDir =
|
|
21221
|
+
const filePath = resolve27(account.accountDir, "generated", filename);
|
|
21222
|
+
const expectedDir = resolve27(account.accountDir, "generated");
|
|
21440
21223
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
21441
21224
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
21442
21225
|
return c.text("Forbidden", 403);
|
|
@@ -21519,7 +21302,7 @@ var clientErrorReporterScript = `<script>
|
|
|
21519
21302
|
function cachedHtml(file) {
|
|
21520
21303
|
let html = htmlCache.get(file);
|
|
21521
21304
|
if (!html) {
|
|
21522
|
-
html = readFileSync24(
|
|
21305
|
+
html = readFileSync24(resolve27(process.cwd(), "public", file), "utf-8");
|
|
21523
21306
|
html = html.replace("<title>Maxy</title>", `<title>${escapeHtml(BRAND.productName)}</title>`);
|
|
21524
21307
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
21525
21308
|
const headInjection = file === "index.html" ? `${brandScript}
|
|
@@ -21626,7 +21409,7 @@ app35.use("/vnc-popout.html", logViewerFetch);
|
|
|
21626
21409
|
app35.get("/vnc-popout.html", (c) => {
|
|
21627
21410
|
let html = htmlCache.get("vnc-popout.html");
|
|
21628
21411
|
if (!html) {
|
|
21629
|
-
html = readFileSync24(
|
|
21412
|
+
html = readFileSync24(resolve27(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
21630
21413
|
const name = escapeHtml(BRAND.productName);
|
|
21631
21414
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
21632
21415
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -21683,11 +21466,6 @@ app35.use("/*", serveStatic({ root: "./public" }));
|
|
|
21683
21466
|
var port = parseInt(process.env.PORT ?? "19199", 10);
|
|
21684
21467
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
21685
21468
|
var httpServer = serve({ fetch: app35.fetch, port, hostname });
|
|
21686
|
-
attachTerminalWsProxy(httpServer, {
|
|
21687
|
-
isPublicHost,
|
|
21688
|
-
upstreamHost: "127.0.0.1",
|
|
21689
|
-
upstreamPort: 7681
|
|
21690
|
-
});
|
|
21691
21469
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
21692
21470
|
var SUBAPP_MANIFEST = [
|
|
21693
21471
|
{ prefix: "/api/health", file: "server/routes/health.ts", subapp: health_default },
|
|
@@ -21748,7 +21526,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
21748
21526
|
}
|
|
21749
21527
|
init({
|
|
21750
21528
|
configDir: configDirForWhatsApp,
|
|
21751
|
-
platformRoot:
|
|
21529
|
+
platformRoot: resolve27(process.env.MAXY_PLATFORM_ROOT ?? join12(__dirname, "..")),
|
|
21752
21530
|
accountConfig: bootAccountConfig,
|
|
21753
21531
|
onMessage: async (msg) => {
|
|
21754
21532
|
try {
|