@rubytech/create-maxy 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.
Files changed (30) hide show
  1. package/dist/index.js +77 -166
  2. package/dist/uninstall.js +24 -0
  3. package/package.json +1 -1
  4. package/payload/platform/plugins/docs/references/deployment.md +2 -3
  5. package/payload/platform/plugins/docs/references/internals.md +4 -0
  6. package/payload/platform/plugins/docs/references/platform.md +1 -1
  7. package/payload/platform/plugins/docs/references/troubleshooting.md +2 -2
  8. package/payload/platform/scripts/vnc.sh +34 -13
  9. package/payload/platform/templates/systemd/maxy-edge.service +33 -0
  10. package/payload/server/chunk-5YIXIF6C.js +726 -0
  11. package/payload/server/maxy-edge.js +422 -0
  12. package/payload/server/public/assets/{admin-DQmUdTBa.js → admin-BBL1no_g.js} +1 -1
  13. package/payload/server/public/assets/{data-DVlvxbTt.js → data-DUSyrydY.js} +1 -1
  14. package/payload/server/public/assets/{file-OY_hX2wu.js → file-CDJ6dUV3.js} +1 -1
  15. package/payload/server/public/assets/graph-CWcYp5bE.js +50 -0
  16. package/payload/server/public/assets/{house-CgENfOCP.js → house-CNP_bwvT.js} +1 -1
  17. package/payload/server/public/assets/{jsx-runtime-Bu4vXoe7.css → jsx-runtime-BFFQvkdQ.css} +1 -1
  18. package/payload/server/public/assets/{public-Clp4VPwo.js → public-sHoAccvb.js} +1 -1
  19. package/payload/server/public/assets/{share-2-RSIR3MmX.js → share-2-DBcb9j6E.js} +1 -1
  20. package/payload/server/public/assets/{useVoiceRecorder-B0FI_hts.js → useVoiceRecorder-CtSgpc95.js} +1 -1
  21. package/payload/server/public/assets/{x-DKZ5NR3n.js → x-CTVJaC_u.js} +1 -1
  22. package/payload/server/public/data.html +6 -6
  23. package/payload/server/public/graph.html +6 -6
  24. package/payload/server/public/index.html +7 -7
  25. package/payload/server/public/public.html +4 -4
  26. package/payload/server/server.js +704 -1874
  27. package/payload/platform/templates/dotfiles/.tmux.conf +0 -1
  28. package/payload/platform/templates/systemd/maxy-ttyd.service +0 -25
  29. package/payload/server/public/assets/graph-BDaM4Qer.js +0 -49
  30. /package/payload/server/public/assets/{jsx-runtime-C_VUlXvu.js → jsx-runtime-BVKWELH6.js} +0 -0
@@ -1,28 +1,31 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __commonJS = (cb, mod) => function __require() {
8
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
- // If the importer is in node compatibility mode or this is not an ESM
20
- // file that has been converted to a CommonJS file using a Babel-
21
- // compatible transform (i.e. "__esModule" has not been set), then set
22
- // "default" to the CommonJS "module.exports" for node compatibility.
23
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
- mod
25
- ));
1
+ import {
2
+ BIN_DIR,
3
+ CLAUDE_CREDENTIALS_FILE,
4
+ LOG_DIR,
5
+ MAXY_DIR,
6
+ TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE,
7
+ TELEGRAM_WEBHOOK_SECRET_FILE,
8
+ USERS_FILE,
9
+ __commonJS,
10
+ __toESM,
11
+ canAccessAdmin,
12
+ checkRateLimit,
13
+ clearRateLimit,
14
+ createRemoteSession,
15
+ hashPassword,
16
+ invalidateRemoteSession,
17
+ isPasswordValid,
18
+ isRemoteAuthConfigured,
19
+ recordFailedAttempt,
20
+ renderLoginPage,
21
+ resolveClientIp,
22
+ sanitizeClientCorrId,
23
+ setRemotePassword,
24
+ validatePasswordStrength,
25
+ verifyPassword,
26
+ verifyRemotePassword,
27
+ vncLog
28
+ } from "./chunk-5YIXIF6C.js";
26
29
 
27
30
  // ../lib/models/dist/index.js
28
31
  var require_dist = __commonJS({
@@ -2526,7 +2529,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
2526
2529
  });
2527
2530
  if (!chunk) {
2528
2531
  if (i === 1) {
2529
- await new Promise((resolve31) => setTimeout(resolve31));
2532
+ await new Promise((resolve28) => setTimeout(resolve28));
2530
2533
  maxReadCount = 3;
2531
2534
  continue;
2532
2535
  }
@@ -2892,1248 +2895,9 @@ var serveStatic = (options = { root: "" }) => {
2892
2895
  };
2893
2896
 
2894
2897
  // server/index.ts
2895
- import { readFileSync as readFileSync26, existsSync as existsSync25, watchFile } from "fs";
2896
- import { createConnection as createConnection5 } from "net";
2897
- import { resolve as resolve30, join as join13, basename as basename7 } from "path";
2898
- import { homedir as homedir5 } from "os";
2899
-
2900
- // app/lib/vnc-logger.ts
2901
- import { appendFileSync, mkdirSync } from "fs";
2902
- import { resolve as resolve2 } from "path";
2903
-
2904
- // app/lib/paths.ts
2905
- import { homedir } from "os";
2906
- import { resolve, join as join2 } from "path";
2907
- import { existsSync as existsSync2, readFileSync } from "fs";
2908
- var configDirName = ".maxy";
2909
- var platformRoot = process.env.MAXY_PLATFORM_ROOT;
2910
- if (platformRoot) {
2911
- const brandPath = join2(platformRoot, "config", "brand.json");
2912
- if (existsSync2(brandPath)) {
2913
- try {
2914
- const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
2915
- if (brand.configDir) configDirName = brand.configDir;
2916
- } catch {
2917
- }
2918
- }
2919
- }
2920
- var MAXY_DIR = resolve(homedir(), configDirName);
2921
- var PLATFORM_ROOT = process.env.MAXY_PLATFORM_ROOT ?? resolve(process.cwd(), "..");
2922
- var USERS_FILE = resolve(PLATFORM_ROOT, "config", "users.json");
2923
- var LOG_DIR = resolve(MAXY_DIR, "logs");
2924
- var BIN_DIR = resolve(MAXY_DIR, "bin");
2925
- var REMOTE_PASSWORD_FILE = resolve(MAXY_DIR, ".remote-password");
2926
- var TELEGRAM_WEBHOOK_SECRET_FILE = resolve(MAXY_DIR, ".telegram-webhook-secret");
2927
- var TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE = resolve(MAXY_DIR, ".telegram-admin-webhook-secret");
2928
- var CLAUDE_CREDENTIALS_FILE = resolve(homedir(), ".claude", ".credentials.json");
2929
-
2930
- // app/lib/vnc-logger.ts
2931
- var VNC_LOG_FILE = resolve2(LOG_DIR, "vnc-boot.log");
2932
- try {
2933
- mkdirSync(LOG_DIR, { recursive: true });
2934
- } catch (err) {
2935
- console.error(`[vnc-log-fail] mkdir ${LOG_DIR} failed: ${err.message}`);
2936
- }
2937
- function vncLog(phase, fields = {}) {
2938
- const ts = (/* @__PURE__ */ new Date()).toISOString();
2939
- const kv = Object.entries(fields).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
2940
- const line = kv.length > 0 ? `[${ts}] [${phase}] ${kv}
2941
- ` : `[${ts}] [${phase}]
2942
- `;
2943
- try {
2944
- appendFileSync(VNC_LOG_FILE, line);
2945
- } catch (err) {
2946
- console.error(`[vnc-log-fail] ${err.message} \u2014 dropped: ${line.slice(0, 300).trim()}`);
2947
- }
2948
- }
2949
- var corrCounter = 0;
2950
- function newCorrId() {
2951
- corrCounter = corrCounter + 1 & 4294967295;
2952
- const counterHex = corrCounter.toString(16).padStart(8, "0");
2953
- const randomHex = Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0");
2954
- return `${counterHex}-${randomHex}`;
2955
- }
2956
- function sanitizeClientCorrId(raw2) {
2957
- if (!raw2) return null;
2958
- if (raw2.length > 32) return null;
2959
- if (!/^[A-Za-z0-9_-]+$/.test(raw2)) return null;
2960
- return raw2;
2961
- }
2962
-
2963
- // app/lib/remote-auth.ts
2964
- import { scrypt, randomBytes, timingSafeEqual } from "crypto";
2965
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
2966
-
2967
- // app/lib/password-strength.ts
2968
- function validatePasswordStrength(password) {
2969
- return [
2970
- { key: "length", label: "At least 8 characters", met: password.length >= 8 },
2971
- { key: "number", label: "Contains a number", met: /\d/.test(password) },
2972
- { key: "special", label: "Contains a special character", met: /[^A-Za-z0-9]/.test(password) },
2973
- { key: "whitespace", label: "No spaces", met: password.length > 0 && !/\s/.test(password) }
2974
- ];
2975
- }
2976
- function isPasswordValid(password) {
2977
- return validatePasswordStrength(password).every((r) => r.met);
2978
- }
2979
-
2980
- // app/lib/remote-auth.ts
2981
- var SCRYPT_N = 16384;
2982
- var SCRYPT_R = 8;
2983
- var SCRYPT_P = 1;
2984
- var SCRYPT_KEYLEN = 64;
2985
- function scryptAsync(password, salt) {
2986
- return new Promise((resolve31, reject) => {
2987
- scrypt(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }, (err, key) => {
2988
- if (err) reject(err);
2989
- else resolve31(key);
2990
- });
2991
- });
2992
- }
2993
- async function hashPassword(password) {
2994
- const salt = randomBytes(32);
2995
- const hash = await scryptAsync(password, salt);
2996
- return `${salt.toString("hex")}:${hash.toString("hex")}`;
2997
- }
2998
- async function verifyPassword(password, stored) {
2999
- const colonIndex = stored.indexOf(":");
3000
- if (colonIndex === -1) return false;
3001
- const saltHex = stored.slice(0, colonIndex);
3002
- const hashHex = stored.slice(colonIndex + 1);
3003
- if (!saltHex || !hashHex) return false;
3004
- try {
3005
- const salt = Buffer.from(saltHex, "hex");
3006
- const storedHash = Buffer.from(hashHex, "hex");
3007
- if (storedHash.length !== SCRYPT_KEYLEN) return false;
3008
- const computed = await scryptAsync(password, salt);
3009
- return timingSafeEqual(computed, storedHash);
3010
- } catch {
3011
- return false;
3012
- }
3013
- }
3014
- function isRemoteAuthConfigured() {
3015
- if (!existsSync3(REMOTE_PASSWORD_FILE)) return false;
3016
- const content = readFileSync2(REMOTE_PASSWORD_FILE, "utf-8").trim();
3017
- return content.length > 0 && content.includes(":");
3018
- }
3019
- function readPasswordHash() {
3020
- try {
3021
- if (!existsSync3(REMOTE_PASSWORD_FILE)) return null;
3022
- const content = readFileSync2(REMOTE_PASSWORD_FILE, "utf-8").trim();
3023
- if (!content || !content.includes(":")) return null;
3024
- return content;
3025
- } catch {
3026
- return null;
3027
- }
3028
- }
3029
- async function setRemotePassword(password) {
3030
- const hash = await hashPassword(password);
3031
- writeFileSync(REMOTE_PASSWORD_FILE, hash, "utf-8");
3032
- }
3033
- async function verifyRemotePassword(password) {
3034
- const stored = readPasswordHash();
3035
- if (!stored) return false;
3036
- return verifyPassword(password, stored);
3037
- }
3038
- var SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
3039
- var remoteSessions = /* @__PURE__ */ new Map();
3040
- function pruneExpiredSessions() {
3041
- const now = Date.now();
3042
- for (const [token, session] of remoteSessions) {
3043
- if (now - session.createdAt > SESSION_TTL_MS) {
3044
- remoteSessions.delete(token);
3045
- }
3046
- }
3047
- }
3048
- function createRemoteSession() {
3049
- const token = randomBytes(32).toString("hex");
3050
- remoteSessions.set(token, { createdAt: Date.now() });
3051
- return token;
3052
- }
3053
- function validateRemoteSession(token) {
3054
- if (!token) return false;
3055
- pruneExpiredSessions();
3056
- const session = remoteSessions.get(token);
3057
- if (!session) return false;
3058
- if (Date.now() - session.createdAt > SESSION_TTL_MS) {
3059
- remoteSessions.delete(token);
3060
- return false;
3061
- }
3062
- return true;
3063
- }
3064
- function invalidateRemoteSession(token) {
3065
- remoteSessions.delete(token);
3066
- }
3067
- var MAX_ATTEMPTS = 5;
3068
- var LOCKOUT_MS = 15 * 60 * 1e3;
3069
- var rateLimitMap = /* @__PURE__ */ new Map();
3070
- function checkRateLimit(ip) {
3071
- const rec = rateLimitMap.get(ip);
3072
- if (!rec) return null;
3073
- if (rec.lockedUntil > Date.now()) {
3074
- const remaining = Math.ceil((rec.lockedUntil - Date.now()) / 1e3);
3075
- return `Too many attempts. Try again in ${remaining}s`;
3076
- }
3077
- if (rec.lockedUntil > 0) {
3078
- rateLimitMap.delete(ip);
3079
- }
3080
- return null;
3081
- }
3082
- function recordFailedAttempt(ip) {
3083
- const rec = rateLimitMap.get(ip) ?? { count: 0, lockedUntil: 0 };
3084
- rec.count++;
3085
- if (rec.count >= MAX_ATTEMPTS) {
3086
- rec.lockedUntil = Date.now() + LOCKOUT_MS;
3087
- rec.count = 0;
3088
- }
3089
- rateLimitMap.set(ip, rec);
3090
- }
3091
- function clearRateLimit(ip) {
3092
- rateLimitMap.delete(ip);
3093
- }
3094
- setInterval(() => {
3095
- const now = Date.now();
3096
- for (const [ip, rec] of rateLimitMap) {
3097
- if (rec.lockedUntil > 0 && rec.lockedUntil <= now) {
3098
- rateLimitMap.delete(ip);
3099
- }
3100
- }
3101
- }, 15 * 60 * 1e3).unref();
3102
- function normalizeIp(ip) {
3103
- if (!ip) return void 0;
3104
- const trimmed = ip.trim();
3105
- if (trimmed.startsWith("::ffff:")) return trimmed.slice(7);
3106
- return trimmed;
3107
- }
3108
- function isLoopback(ip) {
3109
- return ip === "127.0.0.1" || ip.startsWith("127.") || ip === "::1";
3110
- }
3111
- function isPrivateIp(ip) {
3112
- const parts = ip.split(".");
3113
- if (parts.length !== 4) return false;
3114
- const a = parseInt(parts[0], 10);
3115
- const b = parseInt(parts[1], 10);
3116
- if (Number.isNaN(a) || Number.isNaN(b)) return false;
3117
- if (a === 10) return true;
3118
- if (a === 172 && b >= 16 && b <= 31) return true;
3119
- if (a === 192 && b === 168) return true;
3120
- if (a === 169 && b === 254) return true;
3121
- return false;
3122
- }
3123
- function resolveClientIp(remoteAddress, xForwardedFor) {
3124
- const remote = normalizeIp(remoteAddress);
3125
- if (!remote) return void 0;
3126
- if (isLoopback(remote) && xForwardedFor) {
3127
- const firstIp = normalizeIp(xForwardedFor.split(",")[0]?.trim());
3128
- if (firstIp) {
3129
- if (isLoopback(firstIp) || isPrivateIp(firstIp)) return "loopback";
3130
- return firstIp;
3131
- }
3132
- }
3133
- return remote;
3134
- }
3135
- function isExternalIp(ip) {
3136
- if (!ip) return false;
3137
- const normalized = normalizeIp(ip);
3138
- if (!normalized) return false;
3139
- if (isLoopback(normalized)) return false;
3140
- if (isPrivateIp(normalized)) return false;
3141
- return true;
3142
- }
3143
- function renderLoginPage(opts) {
3144
- const error = opts?.error ?? "";
3145
- const lockout = opts?.lockoutSeconds ?? 0;
3146
- const redirect = opts?.redirect ?? "/";
3147
- const mode = opts?.mode ?? "login";
3148
- const changeError = opts?.changeError ?? "";
3149
- const success = opts?.success ?? "";
3150
- const setupError = opts?.setupError ?? "";
3151
- const primaryColor = opts?.primaryColor ?? "#7C8C72";
3152
- const primaryHoverColor = opts?.primaryHoverColor ?? "#6A7A62";
3153
- const primarySubtle = opts?.primarySubtle ?? "rgba(124,140,114,0.08)";
3154
- const productName = opts?.productName ?? "Maxy";
3155
- const logoPath = opts?.logoPath ?? "/brand/maxy-monochrome.png";
3156
- const faviconPath = opts?.faviconPath ?? "/favicon.ico";
3157
- const displayFont = opts?.displayFont ?? "'Cormorant', Georgia, serif";
3158
- const bodyFont = opts?.bodyFont ?? "'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
3159
- const logoContainsName = opts?.logoContainsName ?? false;
3160
- const errorHtml = error ? `<p class="msg msg--error">${escapeHtml(error)}</p>` : "";
3161
- const changeErrorHtml = changeError ? `<p class="msg msg--error">${escapeHtml(changeError)}</p>` : "";
3162
- const successHtml = success ? `<p class="msg msg--success">${escapeHtml(success)}</p>` : "";
3163
- const lockoutHtml = lockout > 0 ? `<p class="msg msg--error">Too many attempts. Try again in <span id="countdown">${lockout}</span>s</p>
3164
- <script>
3165
- (function() {
3166
- var s = ${lockout};
3167
- var el = document.getElementById('countdown');
3168
- var iv = setInterval(function() { s--; el.textContent = s; if (s <= 0) { clearInterval(iv); location.reload(); } }, 1000);
3169
- })();
3170
- </script>` : "";
3171
- const setupErrorHtml = setupError ? `<p class="msg msg--error">${escapeHtml(setupError)}</p>` : "";
3172
- const formDisabled = lockout > 0 ? "disabled" : "";
3173
- const loginDisplay = mode === "login" ? "block" : "none";
3174
- const changeDisplay = mode === "change" ? "block" : "none";
3175
- const setupDisplay = mode === "setup" ? "block" : "none";
3176
- const successDisplay = mode === "success" ? "block" : "none";
3177
- const subtitleText = mode === "setup" ? "Set your remote password" : "Remote access";
3178
- const displayFontName = displayFont.match(/^'([^']+)'/)?.[1] ?? "";
3179
- const bodyFontName = bodyFont.match(/^'([^']+)'/)?.[1] ?? "";
3180
- const googleFontsLink = displayFontName || bodyFontName ? [
3181
- '<link rel="preconnect" href="https://fonts.googleapis.com">',
3182
- '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>',
3183
- '<link href="https://fonts.googleapis.com/css2?' + [
3184
- displayFontName ? `family=${displayFontName.replace(/ /g, "+")}:wght@300;400` : "",
3185
- bodyFontName ? `family=${bodyFontName.replace(/ /g, "+")}:wght@400;500` : ""
3186
- ].filter(Boolean).join("&") + '&display=swap" rel="stylesheet">'
3187
- ].join("\n ") : "";
3188
- return `<!DOCTYPE html>
3189
- <html lang="en">
3190
- <head>
3191
- <meta charset="utf-8">
3192
- <meta name="viewport" content="width=device-width, initial-scale=1">
3193
- <title>Sign in \u2014 ${escapeHtml(productName)}</title>
3194
- <link rel="icon" href="${escapeHtml(faviconPath)}">
3195
- ${googleFontsLink}
3196
- <style>
3197
- * { margin: 0; padding: 0; box-sizing: border-box; }
3198
- body {
3199
- font-family: ${bodyFont};
3200
- background: #FAFAF8;
3201
- color: #1A1A1A;
3202
- min-height: 100vh;
3203
- display: flex;
3204
- align-items: center;
3205
- justify-content: center;
3206
- }
3207
- .page {
3208
- display: flex;
3209
- flex-direction: column;
3210
- align-items: center;
3211
- gap: 24px;
3212
- max-width: 420px;
3213
- width: 100%;
3214
- padding: 40px 20px;
3215
- }
3216
- .logo-img {
3217
- width: 110px;
3218
- height: 110px;
3219
- margin: -11px;
3220
- object-fit: contain;
3221
- }
3222
- .title {
3223
- font-family: ${displayFont};
3224
- font-weight: 300;
3225
- font-size: 28px;
3226
- color: #1A1A1A;
3227
- text-align: center;
3228
- letter-spacing: -0.01em;
3229
- line-height: 1.3;
3230
- }
3231
- .subtitle {
3232
- font-size: 15px;
3233
- color: #6B6B6B;
3234
- text-align: center;
3235
- margin-top: -8px;
3236
- }
3237
- .form-wrap {
3238
- width: 100%;
3239
- max-width: 300px;
3240
- }
3241
- .field { margin-bottom: 12px; }
3242
- .field-label {
3243
- display: block;
3244
- font-size: 13px;
3245
- font-weight: 500;
3246
- color: #6B6B6B;
3247
- margin-bottom: 6px;
3248
- }
3249
- .field-input {
3250
- width: 100%;
3251
- padding: 10px 12px;
3252
- border: 1px solid rgba(0,0,0,0.1);
3253
- border-radius: 8px;
3254
- font-size: 15px;
3255
- font-family: inherit;
3256
- outline: none;
3257
- background: #fff;
3258
- transition: border-color 0.15s, box-shadow 0.15s;
3259
- }
3260
- .field-input:focus {
3261
- border-color: ${primaryColor};
3262
- box-shadow: 0 0 0 3px ${primarySubtle};
3263
- }
3264
- .options {
3265
- display: flex;
3266
- align-items: center;
3267
- justify-content: space-between;
3268
- gap: 12px;
3269
- margin-top: 8px;
3270
- }
3271
-
3272
- /* Checkbox \u2014 matches Maxy shared checkbox (asterisk-in-square) */
3273
- .check {
3274
- position: relative;
3275
- display: flex;
3276
- align-items: center;
3277
- gap: 8px;
3278
- cursor: pointer;
3279
- user-select: none;
3280
- }
3281
- .check input { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
3282
- .check-box {
3283
- width: 14px;
3284
- height: 14px;
3285
- border: 1px solid rgba(0,0,0,0.1);
3286
- border-radius: 2px;
3287
- display: flex;
3288
- align-items: center;
3289
- justify-content: center;
3290
- font-size: 10px;
3291
- color: transparent;
3292
- transition: border-color 0.15s, color 0.15s;
3293
- flex-shrink: 0;
3294
- }
3295
- .check input:checked + .check-box {
3296
- border-color: ${primaryColor};
3297
- color: ${primaryColor};
3298
- }
3299
- .check-label {
3300
- font-size: 13px;
3301
- color: #6B6B6B;
3302
- }
3303
-
3304
- /* Ghost button \u2014 matches Maxy ghost variant */
3305
- .ghost {
3306
- background: none;
3307
- border: none;
3308
- font-family: inherit;
3309
- font-size: 13px;
3310
- color: #6B6B6B;
3311
- cursor: pointer;
3312
- padding: 4px 0;
3313
- transition: color 0.15s;
3314
- }
3315
- .ghost:hover { color: #1A1A1A; }
3316
-
3317
- /* Primary button */
3318
- .btn {
3319
- width: 100%;
3320
- padding: 12px;
3321
- margin-top: 16px;
3322
- background: ${primaryColor};
3323
- color: #fff;
3324
- border: none;
3325
- border-radius: 10px;
3326
- font-size: 15px;
3327
- font-weight: 500;
3328
- font-family: inherit;
3329
- cursor: pointer;
3330
- transition: background 0.15s;
3331
- }
3332
- .btn:hover:not(:disabled) { background: ${primaryHoverColor}; }
3333
- .btn:disabled { opacity: 0.5; cursor: not-allowed; }
3334
-
3335
- /* Messages */
3336
- .msg {
3337
- font-size: 13px;
3338
- text-align: center;
3339
- margin-top: 8px;
3340
- }
3341
- .msg--error { color: #c44; }
3342
- .msg--success { color: ${primaryColor}; }
3343
-
3344
- /* Strength checklist (setup mode) */
3345
- .strength-checklist { margin: 8px 0; }
3346
- .strength-item {
3347
- font-size: 13px;
3348
- color: #6B6B6B;
3349
- padding: 2px 0;
3350
- transition: color 0.15s;
3351
- }
3352
- .strength-icon {
3353
- display: inline-block;
3354
- width: 16px;
3355
- text-align: center;
3356
- }
3357
-
3358
- @media (max-width: 440px) {
3359
- .page { padding: 24px 16px; }
3360
- }
3361
- </style>
3362
- </head>
3363
- <body>
3364
- <div class="page">
3365
- <img src="${escapeHtml(logoPath)}" alt="${escapeHtml(productName)}" class="logo-img">
3366
- <h1 class="title"${logoContainsName ? ' style="display:none"' : ""}>${escapeHtml(productName)}</h1>
3367
- <p class="subtitle">${escapeHtml(subtitleText)}</p>
3368
-
3369
- ${successHtml}
3370
-
3371
- <!-- Login -->
3372
- <div id="login-section" class="form-wrap" style="display:${loginDisplay}">
3373
- <form method="POST" action="/__remote-auth/login">
3374
- <input type="hidden" name="redirect" value="${escapeHtml(redirect)}">
3375
- <div class="field">
3376
- <label class="field-label" for="password">Password</label>
3377
- <input class="field-input" type="password" id="password" name="password" required autofocus ${formDisabled}>
3378
- </div>
3379
- <div class="options">
3380
- <label class="check">
3381
- <input type="checkbox" id="show-login-pw">
3382
- <span class="check-box">\u2731</span>
3383
- <span class="check-label">Show password</span>
3384
- </label>
3385
- <button type="button" class="ghost" onclick="toggleMode('change')">Change password</button>
3386
- </div>
3387
- <button type="submit" class="btn" ${formDisabled}>Sign in</button>
3388
- </form>
3389
- ${errorHtml}
3390
- ${lockoutHtml}
3391
- </div>
3392
-
3393
- <!-- Change password -->
3394
- <div id="change-section" class="form-wrap" style="display:${changeDisplay}">
3395
- <form method="POST" action="/__remote-auth/change-password">
3396
- <input type="hidden" name="redirect" value="${escapeHtml(redirect)}">
3397
- <div class="field">
3398
- <label class="field-label" for="current_password">Current password</label>
3399
- <input class="field-input" type="password" id="current_password" name="current_password" required ${formDisabled}>
3400
- </div>
3401
- <div class="field">
3402
- <label class="field-label" for="new_password">New password</label>
3403
- <input class="field-input" type="password" id="new_password" name="new_password" required ${formDisabled}>
3404
- </div>
3405
- <div class="field">
3406
- <label class="field-label" for="confirm_password">Confirm new password</label>
3407
- <input class="field-input" type="password" id="confirm_password" name="confirm_password" required ${formDisabled}>
3408
- </div>
3409
- <div class="options">
3410
- <label class="check">
3411
- <input type="checkbox" id="show-change-pw">
3412
- <span class="check-box">\u2731</span>
3413
- <span class="check-label">Show passwords</span>
3414
- </label>
3415
- <button type="button" class="ghost" onclick="toggleMode('login')">Back to sign in</button>
3416
- </div>
3417
- <button type="submit" class="btn" ${formDisabled}>Change password</button>
3418
- </form>
3419
- ${changeErrorHtml}
3420
- </div>
3421
-
3422
- <!-- Setup (initial password) -->
3423
- <div id="setup-section" class="form-wrap" style="display:${setupDisplay}">
3424
- <form method="POST" action="/__remote-auth/set-initial-password">
3425
- <div class="field">
3426
- <label class="field-label" for="setup_password">Password</label>
3427
- <input class="field-input" type="password" id="setup_password" name="password" required autofocus>
3428
- </div>
3429
- <div class="field">
3430
- <label class="field-label" for="setup_confirm">Confirm password</label>
3431
- <input class="field-input" type="password" id="setup_confirm" name="confirm_password" required>
3432
- </div>
3433
- <div id="strength-checklist" class="strength-checklist">
3434
- <div class="strength-item" id="req-length"><span class="strength-icon">\u25CB</span> At least 8 characters</div>
3435
- <div class="strength-item" id="req-number"><span class="strength-icon">\u25CB</span> Contains a number</div>
3436
- <div class="strength-item" id="req-special"><span class="strength-icon">\u25CB</span> Contains a special character</div>
3437
- <div class="strength-item" id="req-spaces"><span class="strength-icon">\u25CB</span> No spaces</div>
3438
- </div>
3439
- <div class="options">
3440
- <label class="check">
3441
- <input type="checkbox" id="show-setup-pw">
3442
- <span class="check-box">\u2731</span>
3443
- <span class="check-label">Show passwords</span>
3444
- </label>
3445
- </div>
3446
- <button type="submit" class="btn" id="setup-submit" disabled>Set password</button>
3447
- </form>
3448
- ${setupErrorHtml}
3449
- </div>
3450
-
3451
- <!-- Success (after initial password set) -->
3452
- <div id="success-section" class="form-wrap" style="display:${successDisplay}">
3453
- <p class="msg msg--success" style="font-size:15px;margin-bottom:4px;">\u2713 Password set</p>
3454
- <p style="font-size:14px;color:#6B6B6B;text-align:center;">You can close this tab and return to the onboarding tab.</p>
3455
- <script>
3456
- (function() {
3457
- try {
3458
- var ch = new BroadcastChannel('platform-onboarding');
3459
- setTimeout(function() {
3460
- ch.postMessage({ type: 'remote-password-set' });
3461
- ch.close();
3462
- }, 2500);
3463
- } catch(e) {}
3464
- })();
3465
- </script>
3466
- </div>
3467
- </div>
3468
-
3469
- <script>
3470
- /* Toggle between login and change-password modes */
3471
- function toggleMode(mode) {
3472
- document.getElementById('login-section').style.display = mode === 'login' ? 'block' : 'none';
3473
- document.getElementById('change-section').style.display = mode === 'change' ? 'block' : 'none';
3474
- /* Focus first input in the active section */
3475
- var id = mode === 'login' ? 'password' : 'current_password';
3476
- var el = document.getElementById(id);
3477
- if (el) el.focus();
3478
- }
3479
-
3480
- /* Show/hide password toggles */
3481
- function bindShowHide(checkboxId, inputIds) {
3482
- var cb = document.getElementById(checkboxId);
3483
- if (!cb) return;
3484
- cb.addEventListener('change', function() {
3485
- var type = cb.checked ? 'text' : 'password';
3486
- inputIds.forEach(function(id) {
3487
- var el = document.getElementById(id);
3488
- if (el) el.type = type;
3489
- });
3490
- });
3491
- }
3492
- bindShowHide('show-login-pw', ['password']);
3493
- bindShowHide('show-change-pw', ['current_password', 'new_password', 'confirm_password']);
3494
- bindShowHide('show-setup-pw', ['setup_password', 'setup_confirm']);
3495
-
3496
- /* Explicit Enter-to-submit for the login password field */
3497
- var loginPw = document.getElementById('password');
3498
- if (loginPw) {
3499
- loginPw.addEventListener('keydown', function(e) {
3500
- if (e.key === 'Enter') {
3501
- e.preventDefault();
3502
- this.closest('form').requestSubmit();
3503
- }
3504
- });
3505
- }
3506
-
3507
- /* Live password strength validation (setup mode) */
3508
- (function() {
3509
- var pw = document.getElementById('setup_password');
3510
- var confirm = document.getElementById('setup_confirm');
3511
- var btn = document.getElementById('setup-submit');
3512
- if (!pw || !confirm || !btn) return;
3513
-
3514
- var rules = [
3515
- { id: 'req-length', test: function(p) { return p.length >= 8; } },
3516
- { id: 'req-number', test: function(p) { return /\\d/.test(p); } },
3517
- { id: 'req-special', test: function(p) { return /[^A-Za-z0-9]/.test(p); } },
3518
- { id: 'req-spaces', test: function(p) { return p.length > 0 && !/\\s/.test(p); } }
3519
- ];
3520
-
3521
- function check() {
3522
- var p = pw.value;
3523
- var allMet = true;
3524
- rules.forEach(function(r) {
3525
- var el = document.getElementById(r.id);
3526
- var met = r.test(p);
3527
- if (!met) allMet = false;
3528
- el.querySelector('.strength-icon').textContent = met ? '\\u2713' : '\\u25CB';
3529
- el.style.color = met ? '${primaryColor}' : '#6B6B6B';
3530
- });
3531
- var match = p.length > 0 && p === confirm.value;
3532
- btn.disabled = !(allMet && match);
3533
- }
3534
-
3535
- pw.addEventListener('input', check);
3536
- confirm.addEventListener('input', check);
3537
- })();
3538
- </script>
3539
- </body>
3540
- </html>`;
3541
- }
3542
- function escapeHtml(str) {
3543
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
3544
- }
3545
-
3546
- // app/lib/auth-gate.ts
3547
- function canAccessAdmin(input) {
3548
- if (input.isPublicHost(input.host)) {
3549
- return { allow: false, reason: "public-host" };
3550
- }
3551
- const clientIp = resolveClientIp(input.remoteAddress, input.xForwardedFor);
3552
- if (!isExternalIp(clientIp)) {
3553
- return { allow: true };
3554
- }
3555
- if (!isRemoteAuthConfigured()) {
3556
- return { allow: false, reason: "remote-unconfigured" };
3557
- }
3558
- const token = parseCookieValue(input.cookieHeader, "__remote_session");
3559
- if (token && validateRemoteSession(token)) {
3560
- return { allow: true };
3561
- }
3562
- return { allow: false, reason: "unauthorized" };
3563
- }
3564
- function parseCookieValue(cookieHeader, name) {
3565
- if (!cookieHeader) return null;
3566
- const match2 = cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
3567
- if (!match2) return null;
3568
- try {
3569
- return decodeURIComponent(match2[1]);
3570
- } catch {
3571
- return null;
3572
- }
3573
- }
3574
-
3575
- // server/ws-proxy.ts
3576
- import { createConnection } from "net";
3577
- var WS_PATH = "/websockify";
3578
- var UPSTREAM_TIMEOUT_MS = 5e3;
3579
- var HOP_BY_HOP = /* @__PURE__ */ new Set([
3580
- "connection",
3581
- "keep-alive",
3582
- "proxy-authenticate",
3583
- "proxy-authorization",
3584
- "te",
3585
- "trailer",
3586
- "transfer-encoding",
3587
- "upgrade"
3588
- ]);
3589
- function attachVncWsProxy(server, opts) {
3590
- const upstreamHost = opts.upstreamHost ?? "127.0.0.1";
3591
- const upstreamPort = opts.upstreamPort ?? 6080;
3592
- server.on("upgrade", (req, clientSocket, head) => {
3593
- try {
3594
- handleUpgrade(req, clientSocket, head, {
3595
- isPublicHost: opts.isPublicHost,
3596
- upstreamHost,
3597
- upstreamPort
3598
- });
3599
- } catch (err) {
3600
- vncLog("ws-upgrade", {
3601
- decision: "rejected",
3602
- reason: "handler-exception",
3603
- err: err.message
3604
- });
3605
- clientSocket.destroy();
3606
- }
3607
- });
3608
- }
3609
- function handleUpgrade(req, clientSocket, head, opts) {
3610
- const url = req.url ?? "";
3611
- const qsIndex = url.indexOf("?");
3612
- const pathname = qsIndex === -1 ? url : url.slice(0, qsIndex);
3613
- if (pathname !== WS_PATH) {
3614
- return;
3615
- }
3616
- const corrId = newCorrId();
3617
- const query = qsIndex === -1 ? "" : url.slice(qsIndex + 1);
3618
- const rawClientCorrId = parseQueryParam(query, "corrId");
3619
- const clientCorrId = sanitizeClientCorrId(rawClientCorrId);
3620
- const hostHeader = (req.headers.host ?? "").split(":")[0];
3621
- const originHeader = headerString(req.headers.origin);
3622
- const remote = req.socket.remoteAddress;
3623
- const xff = headerString(req.headers["x-forwarded-for"]);
3624
- const decision = canAccessAdmin({
3625
- host: hostHeader,
3626
- remoteAddress: remote,
3627
- xForwardedFor: xff,
3628
- cookieHeader: headerString(req.headers.cookie),
3629
- isPublicHost: opts.isPublicHost
3630
- });
3631
- if (!decision.allow) {
3632
- const status = decision.reason === "public-host" ? 404 : 401;
3633
- vncLog("ws-upgrade", {
3634
- corrId,
3635
- clientCorrId: clientCorrId ?? null,
3636
- decision: "rejected",
3637
- reason: decision.reason,
3638
- ip: remote,
3639
- xff: xff ?? null,
3640
- origin: originHeader ?? null,
3641
- host: hostHeader
3642
- });
3643
- writeStatusAndDestroy(clientSocket, status, decision.reason === "public-host" ? "Not Found" : "Unauthorized");
3644
- return;
3645
- }
3646
- const originHost = parseOriginHost(originHeader);
3647
- if (!originHost) {
3648
- vncLog("ws-upgrade", {
3649
- corrId,
3650
- clientCorrId: clientCorrId ?? null,
3651
- decision: "rejected",
3652
- reason: "origin-missing-or-invalid",
3653
- origin: originHeader ?? null,
3654
- host: hostHeader,
3655
- ip: remote
3656
- });
3657
- writeStatusAndDestroy(clientSocket, 403, "Forbidden");
3658
- return;
3659
- }
3660
- if (originHost !== hostHeader) {
3661
- vncLog("ws-upgrade", {
3662
- corrId,
3663
- clientCorrId: clientCorrId ?? null,
3664
- decision: "rejected",
3665
- reason: "origin-mismatch",
3666
- origin_host: originHost,
3667
- host: hostHeader,
3668
- ip: remote
3669
- });
3670
- writeStatusAndDestroy(clientSocket, 403, "Forbidden");
3671
- return;
3672
- }
3673
- if (opts.isPublicHost(originHost)) {
3674
- vncLog("ws-upgrade", {
3675
- corrId,
3676
- clientCorrId: clientCorrId ?? null,
3677
- decision: "rejected",
3678
- reason: "origin-public-host",
3679
- origin_host: originHost,
3680
- host: hostHeader,
3681
- ip: remote
3682
- });
3683
- writeStatusAndDestroy(clientSocket, 403, "Forbidden");
3684
- return;
3685
- }
3686
- vncLog("ws-upgrade", {
3687
- corrId,
3688
- clientCorrId: clientCorrId ?? null,
3689
- decision: "accepted",
3690
- ip: remote,
3691
- xff: xff ?? null,
3692
- origin: originHeader ?? null,
3693
- host: hostHeader,
3694
- sec_ws_version: headerString(req.headers["sec-websocket-version"]) ?? null,
3695
- sec_ws_protocol: headerString(req.headers["sec-websocket-protocol"]) ?? null
3696
- });
3697
- const upstream = createConnection({ host: opts.upstreamHost, port: opts.upstreamPort });
3698
- upstream.setTimeout(UPSTREAM_TIMEOUT_MS);
3699
- let bytesClientToUpstream = 0;
3700
- let bytesUpstreamToClient = 0;
3701
- let closedBy = null;
3702
- let proxyOpened = false;
3703
- const finish = (side, reason) => {
3704
- if (closedBy) return;
3705
- closedBy = side;
3706
- if (proxyOpened) {
3707
- vncLog("proxy-close", {
3708
- corrId,
3709
- closedBy: side,
3710
- reason,
3711
- clientBytes: bytesClientToUpstream,
3712
- upstreamBytes: bytesUpstreamToClient
3713
- });
3714
- }
3715
- clientSocket.destroy();
3716
- upstream.destroy();
3717
- };
3718
- upstream.once("connect", () => {
3719
- upstream.setTimeout(0);
3720
- proxyOpened = true;
3721
- vncLog("proxy-open", {
3722
- corrId,
3723
- upstream: `${opts.upstreamHost}:${opts.upstreamPort}`
3724
- });
3725
- const lines = [];
3726
- lines.push(`${req.method ?? "GET"} ${WS_PATH} HTTP/${req.httpVersion}`);
3727
- lines.push(`host: ${opts.upstreamHost}:${opts.upstreamPort}`);
3728
- for (const [name, value] of Object.entries(req.headers)) {
3729
- if (name === "host") continue;
3730
- if (HOP_BY_HOP.has(name)) continue;
3731
- if (value == null) continue;
3732
- if (Array.isArray(value)) {
3733
- for (const v of value) lines.push(`${name}: ${v}`);
3734
- } else {
3735
- lines.push(`${name}: ${value}`);
3736
- }
3737
- }
3738
- const upgradeHeader = headerString(req.headers.upgrade);
3739
- const connectionHeader = headerString(req.headers.connection);
3740
- if (upgradeHeader) lines.push(`upgrade: ${upgradeHeader}`);
3741
- if (connectionHeader) lines.push(`connection: ${connectionHeader}`);
3742
- upstream.write(lines.join("\r\n") + "\r\n\r\n");
3743
- if (head && head.length > 0) upstream.write(head);
3744
- clientSocket.on("data", (chunk) => {
3745
- bytesClientToUpstream += chunk.length;
3746
- });
3747
- upstream.on("data", (chunk) => {
3748
- bytesUpstreamToClient += chunk.length;
3749
- });
3750
- clientSocket.pipe(upstream);
3751
- upstream.pipe(clientSocket);
3752
- clientSocket.once("close", (hadError) => {
3753
- finish("client", hadError ? "error" : "normal");
3754
- });
3755
- upstream.once("close", (hadError) => {
3756
- finish("upstream", hadError ? "error" : "normal");
3757
- });
3758
- clientSocket.once("error", (err) => {
3759
- vncLog("proxy-error", { corrId, side: "client", err: err.message });
3760
- finish("client", "error");
3761
- });
3762
- upstream.once("error", (err) => {
3763
- vncLog("proxy-error", { corrId, side: "upstream", err: err.message });
3764
- finish("upstream", "error");
3765
- });
3766
- });
3767
- upstream.once("timeout", () => {
3768
- if (proxyOpened) return;
3769
- vncLog("proxy-error", {
3770
- corrId,
3771
- side: "upstream-connect",
3772
- err: "timeout",
3773
- timeout_ms: UPSTREAM_TIMEOUT_MS
3774
- });
3775
- writeStatusAndDestroy(clientSocket, 504, "Gateway Timeout");
3776
- upstream.destroy();
3777
- });
3778
- upstream.once("error", (err) => {
3779
- if (proxyOpened) return;
3780
- vncLog("proxy-error", {
3781
- corrId,
3782
- side: "upstream-connect",
3783
- err: err.message
3784
- });
3785
- writeStatusAndDestroy(clientSocket, 502, "Bad Gateway");
3786
- upstream.destroy();
3787
- });
3788
- }
3789
- function parseQueryParam(query, key) {
3790
- if (!query) return null;
3791
- for (const pair of query.split("&")) {
3792
- const eq = pair.indexOf("=");
3793
- const k = eq === -1 ? pair : pair.slice(0, eq);
3794
- if (k !== key) continue;
3795
- const v = eq === -1 ? "" : pair.slice(eq + 1);
3796
- try {
3797
- return decodeURIComponent(v);
3798
- } catch {
3799
- return null;
3800
- }
3801
- }
3802
- return null;
3803
- }
3804
- function headerString(value) {
3805
- if (value == null) return void 0;
3806
- return Array.isArray(value) ? value[0] : value;
3807
- }
3808
- function parseOriginHost(origin) {
3809
- if (!origin) return null;
3810
- try {
3811
- return new URL(origin).hostname;
3812
- } catch {
3813
- return null;
3814
- }
3815
- }
3816
- function writeStatusAndDestroy(socket, status, statusText) {
3817
- try {
3818
- socket.write(
3819
- `HTTP/1.1 ${status} ${statusText}\r
3820
- Connection: close\r
3821
- Content-Length: 0\r
3822
- \r
3823
- `
3824
- );
3825
- } catch {
3826
- }
3827
- socket.destroy();
3828
- }
3829
-
3830
- // server/ws-proxy-terminal.ts
3831
- import { createConnection as createConnection2 } from "net";
3832
-
3833
- // app/lib/terminal-logger.ts
3834
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync2 } from "fs";
3835
- import { resolve as resolve3 } from "path";
3836
- var TERMINAL_LOG_FILE = resolve3(LOG_DIR, "terminal.log");
3837
- try {
3838
- mkdirSync2(LOG_DIR, { recursive: true });
3839
- } catch (err) {
3840
- console.error(`[terminal-log-fail] mkdir ${LOG_DIR} failed: ${err.message}`);
3841
- }
3842
- function terminalLog(phase, fields = {}) {
3843
- const ts = (/* @__PURE__ */ new Date()).toISOString();
3844
- const kv = Object.entries(fields).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
3845
- const line = kv.length > 0 ? `[${ts}] [terminal-${phase}] ${kv}
3846
- ` : `[${ts}] [terminal-${phase}]
3847
- `;
3848
- try {
3849
- appendFileSync2(TERMINAL_LOG_FILE, line);
3850
- } catch (err) {
3851
- console.error(`[terminal-log-fail] ${err.message} \u2014 dropped: ${line.slice(0, 300).trim()}`);
3852
- }
3853
- }
3854
-
3855
- // server/ws-proxy-terminal.ts
3856
- var WS_PATH2 = "/admin/terminal/ws";
3857
- var UPSTREAM_TIMEOUT_MS2 = 5e3;
3858
- var FLOW_TICK_MS = 5e3;
3859
- var FLOW_IDLE_EMIT_MS = 3e4;
3860
- var HOP_BY_HOP2 = /* @__PURE__ */ new Set([
3861
- "connection",
3862
- "keep-alive",
3863
- "proxy-authenticate",
3864
- "proxy-authorization",
3865
- "te",
3866
- "trailer",
3867
- "transfer-encoding",
3868
- "upgrade"
3869
- ]);
3870
- function attachTerminalWsProxy(server, opts) {
3871
- const upstreamHost = opts.upstreamHost ?? "127.0.0.1";
3872
- const upstreamPort = opts.upstreamPort ?? 7681;
3873
- server.on("upgrade", (req, clientSocket, head) => {
3874
- try {
3875
- handleUpgrade2(req, clientSocket, head, {
3876
- isPublicHost: opts.isPublicHost,
3877
- upstreamHost,
3878
- upstreamPort
3879
- });
3880
- } catch (err) {
3881
- terminalLog("ws-upgrade", {
3882
- decision: "rejected",
3883
- reason: "handler-exception",
3884
- err: err.message
3885
- });
3886
- clientSocket.destroy();
3887
- }
3888
- });
3889
- }
3890
- function handleUpgrade2(req, clientSocket, head, opts) {
3891
- const url = req.url ?? "";
3892
- const qsIndex = url.indexOf("?");
3893
- const pathname = qsIndex === -1 ? url : url.slice(0, qsIndex);
3894
- if (pathname !== WS_PATH2) {
3895
- return;
3896
- }
3897
- const corrId = newCorrId();
3898
- const query = qsIndex === -1 ? "" : url.slice(qsIndex + 1);
3899
- const rawClientCorrId = parseQueryParam2(query, "corrId");
3900
- const clientCorrId = sanitizeClientCorrId(rawClientCorrId);
3901
- const hostHeader = (req.headers.host ?? "").split(":")[0];
3902
- const originHeader = headerString2(req.headers.origin);
3903
- const remote = req.socket.remoteAddress;
3904
- const xff = headerString2(req.headers["x-forwarded-for"]);
3905
- const decision = canAccessAdmin({
3906
- host: hostHeader,
3907
- remoteAddress: remote,
3908
- xForwardedFor: xff,
3909
- cookieHeader: headerString2(req.headers.cookie),
3910
- isPublicHost: opts.isPublicHost
3911
- });
3912
- if (!decision.allow) {
3913
- const status = decision.reason === "public-host" ? 404 : 401;
3914
- terminalLog("ws-upgrade", {
3915
- corrId,
3916
- clientCorrId: clientCorrId ?? null,
3917
- decision: "rejected",
3918
- reason: decision.reason,
3919
- ip: remote,
3920
- xff: xff ?? null,
3921
- origin: originHeader ?? null,
3922
- host: hostHeader
3923
- });
3924
- writeStatusAndDestroy2(clientSocket, status, decision.reason === "public-host" ? "Not Found" : "Unauthorized");
3925
- return;
3926
- }
3927
- const originHost = parseOriginHost2(originHeader);
3928
- if (!originHost) {
3929
- terminalLog("ws-upgrade", {
3930
- corrId,
3931
- clientCorrId: clientCorrId ?? null,
3932
- decision: "rejected",
3933
- reason: "origin-missing-or-invalid",
3934
- origin: originHeader ?? null,
3935
- host: hostHeader,
3936
- ip: remote
3937
- });
3938
- writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
3939
- return;
3940
- }
3941
- if (originHost !== hostHeader) {
3942
- terminalLog("ws-upgrade", {
3943
- corrId,
3944
- clientCorrId: clientCorrId ?? null,
3945
- decision: "rejected",
3946
- reason: "origin-mismatch",
3947
- origin_host: originHost,
3948
- host: hostHeader,
3949
- ip: remote
3950
- });
3951
- writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
3952
- return;
3953
- }
3954
- if (opts.isPublicHost(originHost)) {
3955
- terminalLog("ws-upgrade", {
3956
- corrId,
3957
- clientCorrId: clientCorrId ?? null,
3958
- decision: "rejected",
3959
- reason: "origin-public-host",
3960
- origin_host: originHost,
3961
- host: hostHeader,
3962
- ip: remote
3963
- });
3964
- writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
3965
- return;
3966
- }
3967
- terminalLog("ws-upgrade", {
3968
- corrId,
3969
- clientCorrId: clientCorrId ?? null,
3970
- decision: "accepted",
3971
- ip: remote,
3972
- xff: xff ?? null,
3973
- origin: originHeader ?? null,
3974
- host: hostHeader,
3975
- sec_ws_version: headerString2(req.headers["sec-websocket-version"]) ?? null,
3976
- sec_ws_protocol: headerString2(req.headers["sec-websocket-protocol"]) ?? null
3977
- });
3978
- const upstream = createConnection2({ host: opts.upstreamHost, port: opts.upstreamPort });
3979
- upstream.setTimeout(UPSTREAM_TIMEOUT_MS2);
3980
- let bytesClientToUpstream = 0;
3981
- let bytesUpstreamToClient = 0;
3982
- let closedBy = null;
3983
- let proxyOpened = false;
3984
- let lastUpstreamByteTs = 0;
3985
- let flowInterval = null;
3986
- let lastEmittedAt = 0;
3987
- let lastEmittedUpstreamBytes = 0;
3988
- const finish = (side, reason) => {
3989
- if (closedBy) return;
3990
- closedBy = side;
3991
- if (flowInterval) {
3992
- clearInterval(flowInterval);
3993
- flowInterval = null;
3994
- }
3995
- if (proxyOpened) {
3996
- terminalLog("proxy-close", {
3997
- corrId,
3998
- closedBy: side,
3999
- reason,
4000
- clientBytes: bytesClientToUpstream,
4001
- upstreamBytes: bytesUpstreamToClient
4002
- });
4003
- }
4004
- clientSocket.destroy();
4005
- upstream.destroy();
4006
- };
4007
- upstream.once("connect", () => {
4008
- upstream.setTimeout(0);
4009
- proxyOpened = true;
4010
- terminalLog("proxy-open", {
4011
- corrId,
4012
- upstream: `${opts.upstreamHost}:${opts.upstreamPort}`
4013
- });
4014
- const lines = [];
4015
- lines.push(`${req.method ?? "GET"} ${WS_PATH2} HTTP/${req.httpVersion}`);
4016
- lines.push(`host: ${opts.upstreamHost}:${opts.upstreamPort}`);
4017
- for (const [name, value] of Object.entries(req.headers)) {
4018
- if (name === "host") continue;
4019
- if (HOP_BY_HOP2.has(name)) continue;
4020
- if (value == null) continue;
4021
- if (Array.isArray(value)) {
4022
- for (const v of value) lines.push(`${name}: ${v}`);
4023
- } else {
4024
- lines.push(`${name}: ${value}`);
4025
- }
4026
- }
4027
- const upgradeHeader = headerString2(req.headers.upgrade);
4028
- const connectionHeader = headerString2(req.headers.connection);
4029
- if (upgradeHeader) lines.push(`upgrade: ${upgradeHeader}`);
4030
- if (connectionHeader) lines.push(`connection: ${connectionHeader}`);
4031
- upstream.write(lines.join("\r\n") + "\r\n\r\n");
4032
- if (head && head.length > 0) upstream.write(head);
4033
- clientSocket.on("data", (chunk) => {
4034
- bytesClientToUpstream += chunk.length;
4035
- });
4036
- upstream.on("data", (chunk) => {
4037
- bytesUpstreamToClient += chunk.length;
4038
- lastUpstreamByteTs = Date.now();
4039
- });
4040
- clientSocket.pipe(upstream);
4041
- upstream.pipe(clientSocket);
4042
- lastEmittedAt = Date.now();
4043
- lastEmittedUpstreamBytes = 0;
4044
- flowInterval = setInterval(() => {
4045
- const now = Date.now();
4046
- const idleMs = lastUpstreamByteTs === 0 ? now - lastEmittedAt : now - lastUpstreamByteTs;
4047
- const upstreamMoved = bytesUpstreamToClient !== lastEmittedUpstreamBytes;
4048
- const sinceLastEmit = now - lastEmittedAt;
4049
- if (upstreamMoved || sinceLastEmit >= FLOW_IDLE_EMIT_MS) {
4050
- terminalLog("proxy-flow", {
4051
- corrId,
4052
- clientBytes: bytesClientToUpstream,
4053
- upstreamBytes: bytesUpstreamToClient,
4054
- idleMs
4055
- });
4056
- lastEmittedAt = now;
4057
- lastEmittedUpstreamBytes = bytesUpstreamToClient;
4058
- }
4059
- }, FLOW_TICK_MS);
4060
- clientSocket.once("close", (hadError) => {
4061
- finish("client", hadError ? "error" : "normal");
4062
- });
4063
- upstream.once("close", (hadError) => {
4064
- finish("upstream", hadError ? "error" : "normal");
4065
- });
4066
- clientSocket.once("error", (err) => {
4067
- terminalLog("proxy-error", { corrId, side: "client", err: err.message });
4068
- finish("client", "error");
4069
- });
4070
- upstream.once("error", (err) => {
4071
- terminalLog("proxy-error", { corrId, side: "upstream", err: err.message });
4072
- finish("upstream", "error");
4073
- });
4074
- });
4075
- upstream.once("timeout", () => {
4076
- if (proxyOpened) return;
4077
- terminalLog("proxy-error", {
4078
- corrId,
4079
- side: "upstream-connect",
4080
- err: "timeout",
4081
- timeout_ms: UPSTREAM_TIMEOUT_MS2
4082
- });
4083
- writeStatusAndDestroy2(clientSocket, 504, "Gateway Timeout");
4084
- upstream.destroy();
4085
- });
4086
- upstream.once("error", (err) => {
4087
- if (proxyOpened) return;
4088
- terminalLog("proxy-error", {
4089
- corrId,
4090
- side: "upstream-connect",
4091
- err: err.message
4092
- });
4093
- writeStatusAndDestroy2(clientSocket, 502, "Bad Gateway");
4094
- upstream.destroy();
4095
- });
4096
- }
4097
- function parseQueryParam2(query, key) {
4098
- if (!query) return null;
4099
- for (const pair of query.split("&")) {
4100
- const eq = pair.indexOf("=");
4101
- const k = eq === -1 ? pair : pair.slice(0, eq);
4102
- if (k !== key) continue;
4103
- const v = eq === -1 ? "" : pair.slice(eq + 1);
4104
- try {
4105
- return decodeURIComponent(v);
4106
- } catch {
4107
- return null;
4108
- }
4109
- }
4110
- return null;
4111
- }
4112
- function headerString2(value) {
4113
- if (value == null) return void 0;
4114
- return Array.isArray(value) ? value[0] : value;
4115
- }
4116
- function parseOriginHost2(origin) {
4117
- if (!origin) return null;
4118
- try {
4119
- return new URL(origin).hostname;
4120
- } catch {
4121
- return null;
4122
- }
4123
- }
4124
- function writeStatusAndDestroy2(socket, status, statusText) {
4125
- try {
4126
- socket.write(
4127
- `HTTP/1.1 ${status} ${statusText}\r
4128
- Connection: close\r
4129
- Content-Length: 0\r
4130
- \r
4131
- `
4132
- );
4133
- } catch {
4134
- }
4135
- socket.destroy();
4136
- }
2898
+ import { readFileSync as readFileSync24, existsSync as existsSync23, watchFile } from "fs";
2899
+ import { resolve as resolve27, join as join12, basename as basename7 } from "path";
2900
+ import { homedir as homedir4 } from "os";
4137
2901
 
4138
2902
  // app/lib/agent-slug-pattern.ts
4139
2903
  var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
@@ -4142,9 +2906,9 @@ var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
4142
2906
  import Anthropic3 from "@anthropic-ai/sdk";
4143
2907
  import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
4144
2908
  import { randomUUID as randomUUID2 } from "crypto";
4145
- import { resolve as resolve8, join as join4 } from "path";
2909
+ import { resolve as resolve5, join as join3 } from "path";
4146
2910
  import { platform as osPlatform } from "os";
4147
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, readdirSync as readdirSync2, existsSync as existsSync7, mkdirSync as mkdirSync6, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync3, cpSync, rmSync as rmSync2, appendFileSync as appendFileSync3, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
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";
4148
2912
  import { lookup as dnsLookup } from "dns/promises";
4149
2913
  import { createConnection as netConnect } from "net";
4150
2914
  import { StringDecoder } from "string_decoder";
@@ -4152,34 +2916,34 @@ import { StringDecoder } from "string_decoder";
4152
2916
  // ../lib/anthropic-key/src/index.ts
4153
2917
  var import_dist = __toESM(require_dist());
4154
2918
  import {
4155
- existsSync as existsSync4,
4156
- mkdirSync as mkdirSync3,
4157
- readFileSync as readFileSync3,
2919
+ existsSync as existsSync2,
2920
+ mkdirSync,
2921
+ readFileSync,
4158
2922
  unlinkSync,
4159
- writeFileSync as writeFileSync2
2923
+ writeFileSync
4160
2924
  } from "fs";
4161
- import { resolve as resolve4, join as join3, dirname } from "path";
4162
- import { homedir as homedir2 } from "os";
2925
+ import { resolve, join as join2, dirname } from "path";
2926
+ import { homedir } from "os";
4163
2927
  var cachedKeyFilePath = null;
4164
2928
  function resolveKeyFilePath() {
4165
2929
  if (cachedKeyFilePath) return cachedKeyFilePath;
4166
- let configDirName2 = ".maxy";
4167
- const platformRoot3 = process.env.PLATFORM_ROOT ?? process.env.MAXY_PLATFORM_ROOT;
4168
- if (platformRoot3) {
4169
- const brandPath = join3(platformRoot3, "config", "brand.json");
4170
- if (!existsSync4(brandPath)) {
2930
+ let configDirName = ".maxy";
2931
+ const platformRoot2 = process.env.PLATFORM_ROOT ?? process.env.MAXY_PLATFORM_ROOT;
2932
+ if (platformRoot2) {
2933
+ const brandPath = join2(platformRoot2, "config", "brand.json");
2934
+ if (!existsSync2(brandPath)) {
4171
2935
  throw new Error(
4172
2936
  `brand.json not found at ${brandPath} \u2014 platform not properly installed`
4173
2937
  );
4174
2938
  }
4175
2939
  try {
4176
- const brand = JSON.parse(readFileSync3(brandPath, "utf-8"));
2940
+ const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
4177
2941
  if (!brand.configDir) {
4178
2942
  throw new Error(
4179
2943
  `brand.json at ${brandPath} is missing the configDir field`
4180
2944
  );
4181
2945
  }
4182
- configDirName2 = brand.configDir;
2946
+ configDirName = brand.configDir;
4183
2947
  } catch (err) {
4184
2948
  if (err instanceof SyntaxError) {
4185
2949
  throw new Error(
@@ -4189,7 +2953,7 @@ function resolveKeyFilePath() {
4189
2953
  throw err;
4190
2954
  }
4191
2955
  }
4192
- cachedKeyFilePath = resolve4(homedir2(), configDirName2, ".anthropic-api-key");
2956
+ cachedKeyFilePath = resolve(homedir(), configDirName, ".anthropic-api-key");
4193
2957
  return cachedKeyFilePath;
4194
2958
  }
4195
2959
  function readKey() {
@@ -4211,7 +2975,7 @@ function readKey() {
4211
2975
  );
4212
2976
  }
4213
2977
  try {
4214
- const raw2 = readFileSync3(keyFilePath2, "utf-8").trim();
2978
+ const raw2 = readFileSync(keyFilePath2, "utf-8").trim();
4215
2979
  if (!raw2) {
4216
2980
  console.error(
4217
2981
  `[anthropic-key] key file exists but is empty: ${keyFilePath2}`
@@ -4325,14 +3089,14 @@ function contextWindow(model) {
4325
3089
  }
4326
3090
 
4327
3091
  // app/lib/claude-auth.ts
4328
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
3092
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
4329
3093
  var TOKEN_ENDPOINT = "https://platform.claude.com/v1/oauth/token";
4330
3094
  var CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
4331
3095
  var EXPIRING_THRESHOLD_MS = 5 * 60 * 1e3;
4332
3096
  function readCredentials() {
4333
3097
  let raw2;
4334
3098
  try {
4335
- raw2 = readFileSync4(CLAUDE_CREDENTIALS_FILE, "utf-8");
3099
+ raw2 = readFileSync2(CLAUDE_CREDENTIALS_FILE, "utf-8");
4336
3100
  } catch {
4337
3101
  return null;
4338
3102
  }
@@ -4360,7 +3124,7 @@ function writeOAuthCredentials(tokens) {
4360
3124
  try {
4361
3125
  let fileData = {};
4362
3126
  try {
4363
- const rawFile = readFileSync4(CLAUDE_CREDENTIALS_FILE, "utf-8");
3127
+ const rawFile = readFileSync2(CLAUDE_CREDENTIALS_FILE, "utf-8");
4364
3128
  fileData = JSON.parse(rawFile);
4365
3129
  } catch {
4366
3130
  }
@@ -4371,7 +3135,7 @@ function writeOAuthCredentials(tokens) {
4371
3135
  ...tokens.refreshToken != null ? { refreshToken: tokens.refreshToken } : {},
4372
3136
  expiresAt: newExpiresAt
4373
3137
  };
4374
- writeFileSync3(CLAUDE_CREDENTIALS_FILE, JSON.stringify(fileData, null, 2), "utf-8");
3138
+ writeFileSync2(CLAUDE_CREDENTIALS_FILE, JSON.stringify(fileData, null, 2), "utf-8");
4375
3139
  return newExpiresAt;
4376
3140
  } catch (err) {
4377
3141
  console.error(`[claude-auth] credential write failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -4475,11 +3239,11 @@ async function ensureAuth() {
4475
3239
 
4476
3240
  // app/lib/vnc.ts
4477
3241
  import { spawnSync, execFileSync } from "child_process";
4478
- import { createConnection as createConnection3 } from "net";
4479
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
4480
- import { resolve as resolve5 } from "path";
4481
- var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
4482
- var VNC_SCRIPT = resolve5(PLATFORM_ROOT2, "scripts/vnc.sh");
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");
4483
3247
  var displayMode = process.env.DISPLAY_MODE ?? "virtual";
4484
3248
  if (displayMode === "native") {
4485
3249
  console.log(`[vnc] DISPLAY_MODE=native \u2014 local requests use desktop display, remote requests use VNC`);
@@ -4488,8 +3252,8 @@ function resolveBrowserTransport(req, remoteAddress) {
4488
3252
  if (displayMode !== "native") return "vnc";
4489
3253
  const xff = req.headers.get("x-forwarded-for") ?? void 0;
4490
3254
  const clientIp = resolveClientIp(remoteAddress ?? "127.0.0.1", xff);
4491
- const isLoopback2 = clientIp === "127.0.0.1" || clientIp === "::1" || clientIp === "loopback";
4492
- const transport = isLoopback2 && !xff ? "native" : "vnc";
3255
+ const isLoopback = clientIp === "127.0.0.1" || clientIp === "::1" || clientIp === "loopback";
3256
+ const transport = isLoopback && !xff ? "native" : "vnc";
4493
3257
  if (remoteAddress && remoteAddress !== "127.0.0.1" && remoteAddress !== "::1") {
4494
3258
  const oldClientIp = resolveClientIp("127.0.0.1", xff);
4495
3259
  const oldIsLoopback = oldClientIp === "127.0.0.1" || oldClientIp === "::1" || oldClientIp === "loopback";
@@ -4537,7 +3301,7 @@ function discoverNativeDisplay() {
4537
3301
  const leaderPid = leaderResult.stdout?.trim();
4538
3302
  if (leaderPid) {
4539
3303
  try {
4540
- const environ = readFileSync5(`/proc/${leaderPid}/environ`, "utf8");
3304
+ const environ = readFileSync3(`/proc/${leaderPid}/environ`, "utf8");
4541
3305
  const match2 = environ.split("\0").find((e) => e.startsWith("WAYLAND_DISPLAY="));
4542
3306
  if (match2) waylandDisplay = match2.split("=")[1];
4543
3307
  } catch {
@@ -4571,7 +3335,7 @@ async function waitForPort(port2, timeoutMs = 12e3) {
4571
3335
  const deadline = Date.now() + timeoutMs;
4572
3336
  while (Date.now() < deadline) {
4573
3337
  const ready = await new Promise((res) => {
4574
- const socket = createConnection3(port2, "127.0.0.1");
3338
+ const socket = createConnection(port2, "127.0.0.1");
4575
3339
  socket.setTimeout(500);
4576
3340
  socket.once("connect", () => {
4577
3341
  socket.destroy();
@@ -4592,10 +3356,10 @@ async function waitForPort(port2, timeoutMs = 12e3) {
4592
3356
  return false;
4593
3357
  }
4594
3358
  function ensureLogDir() {
4595
- mkdirSync4(LOG_DIR, { recursive: true });
3359
+ mkdirSync2(LOG_DIR, { recursive: true });
4596
3360
  }
4597
3361
  function logPath(name) {
4598
- return resolve5(LOG_DIR, `${name}.log`);
3362
+ return resolve2(LOG_DIR, `${name}.log`);
4599
3363
  }
4600
3364
  async function ensureVnc() {
4601
3365
  const up = await waitForPort(5900, 1e3);
@@ -4804,9 +3568,9 @@ async function ensureTerminalUpgrade(transport = "vnc") {
4804
3568
  return { ok: true };
4805
3569
  }
4806
3570
  function writeChromiumWrapper() {
4807
- mkdirSync4(BIN_DIR, { recursive: true });
4808
- const wrapperPath = resolve5(BIN_DIR, "chromium");
4809
- writeFileSync4(wrapperPath, `#!/bin/bash
3571
+ mkdirSync2(BIN_DIR, { recursive: true });
3572
+ const wrapperPath = resolve2(BIN_DIR, "chromium");
3573
+ writeFileSync3(wrapperPath, `#!/bin/bash
4810
3574
  LOG="${LOG_DIR}/chromium.log"
4811
3575
  echo "==== [$(date)] chromium wrapper ====" >> "$LOG"
4812
3576
  echo " DISPLAY=$DISPLAY WAYLAND=$WAYLAND_DISPLAY XDG_SESSION_TYPE=$XDG_SESSION_TYPE" >> "$LOG"
@@ -4890,15 +3654,15 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
4890
3654
  import neo4j from "neo4j-driver";
4891
3655
  import { randomUUID } from "crypto";
4892
3656
  import { spawn } from "child_process";
4893
- import { readFileSync as readFileSync6, readdirSync, existsSync as existsSync5, openSync, readSync, closeSync, statSync as statSync2, rmSync } from "fs";
4894
- import { resolve as resolve6 } from "path";
4895
- var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
3657
+ import { readFileSync as readFileSync4, readdirSync, existsSync as existsSync3, openSync, readSync, closeSync, statSync as statSync2, rmSync } from "fs";
3658
+ import { resolve as resolve3 } from "path";
3659
+ var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
4896
3660
  var driver = null;
4897
3661
  function readPassword() {
4898
3662
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
4899
- const passwordFile = resolve6(PLATFORM_ROOT3, "config/.neo4j-password");
3663
+ const passwordFile = resolve3(PLATFORM_ROOT2, "config/.neo4j-password");
4900
3664
  try {
4901
- return readFileSync6(passwordFile, "utf-8").trim();
3665
+ return readFileSync4(passwordFile, "utf-8").trim();
4902
3666
  } catch {
4903
3667
  throw new Error(
4904
3668
  `Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
@@ -5361,8 +4125,8 @@ async function persistMessage(conversationId, role, content, accountId, tokens,
5361
4125
  const prev = persistMessageLocks.get(conversationId);
5362
4126
  const waited = prev !== void 0;
5363
4127
  let release;
5364
- const mine = new Promise((resolve31) => {
5365
- release = resolve31;
4128
+ const mine = new Promise((resolve28) => {
4129
+ release = resolve28;
5366
4130
  });
5367
4131
  const chained = (prev ?? Promise.resolve()).then(() => mine);
5368
4132
  persistMessageLocks.set(conversationId, chained);
@@ -5621,7 +4385,7 @@ ${userContent}`;
5621
4385
  "dontAsk",
5622
4386
  prompt
5623
4387
  ];
5624
- return new Promise((resolve31) => {
4388
+ return new Promise((resolve28) => {
5625
4389
  let stdout = "";
5626
4390
  let stderr = "";
5627
4391
  const spawnFn = _spawnOverride ?? spawn;
@@ -5639,35 +4403,35 @@ ${userContent}`;
5639
4403
  const timer = setTimeout(() => {
5640
4404
  proc.kill("SIGTERM");
5641
4405
  console.error("[persist] autoLabel: haiku subprocess timed out");
5642
- resolve31(null);
4406
+ resolve28(null);
5643
4407
  }, SESSION_LABEL_TIMEOUT_MS);
5644
4408
  proc.on("error", (err) => {
5645
4409
  clearTimeout(timer);
5646
4410
  console.error(`[persist] autoLabel: subprocess error \u2014 ${err.message}`);
5647
- resolve31(null);
4411
+ resolve28(null);
5648
4412
  });
5649
4413
  proc.on("close", (code) => {
5650
4414
  clearTimeout(timer);
5651
4415
  if (code !== 0) {
5652
4416
  console.error(`[persist] autoLabel: subprocess exited code=${code}${stderr ? ` stderr=${stderr.trim().slice(0, 200)}` : ""}`);
5653
- resolve31(null);
4417
+ resolve28(null);
5654
4418
  return;
5655
4419
  }
5656
4420
  const text = stdout.trim();
5657
4421
  if (!text) {
5658
4422
  console.error("[persist] autoLabel: haiku returned empty response");
5659
- resolve31(null);
4423
+ resolve28(null);
5660
4424
  return;
5661
4425
  }
5662
4426
  if (text === "SKIP") {
5663
4427
  console.error("[persist] autoLabel: haiku returned SKIP \u2014 messages too vague");
5664
- resolve31(null);
4428
+ resolve28(null);
5665
4429
  return;
5666
4430
  }
5667
4431
  const words = text.split(/\s+/).slice(0, SESSION_LABEL_MAX_WORDS);
5668
4432
  const label = words.join(" ");
5669
4433
  console.error(`[persist] autoLabel: haiku response="${label}"`);
5670
- resolve31(label);
4434
+ resolve28(label);
5671
4435
  });
5672
4436
  });
5673
4437
  }
@@ -5939,10 +4703,10 @@ var MAX_RECENT_TOOL_FAILURES = 3;
5939
4703
  var RECENT_FAILURES_TAIL_BYTES = 10 * 1024;
5940
4704
  function readRecentToolFailures(accountId, conversationId) {
5941
4705
  try {
5942
- const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
5943
- const logDir = resolve6(platformRoot3, "..", "data/accounts", accountId, "logs");
5944
- const logPath2 = resolve6(logDir, `claude-agent-stream-${conversationId}.log`);
5945
- if (!existsSync5(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`);
4709
+ if (!existsSync3(logPath2)) {
5946
4710
  console.error(`[review-tail-skip] path=${logPath2} reason=file-missing \u2014 first turn of conversation, subprocess not yet spawned, or log rotated`);
5947
4711
  return [];
5948
4712
  }
@@ -6098,13 +4862,13 @@ ${taskLines.join("\n")}`);
6098
4862
  let pendingCount = 0;
6099
4863
  let pendingLines = [];
6100
4864
  try {
6101
- const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
6102
- const pendingDir = resolve6(platformRoot3, "..", "data/accounts", accountId, "pending-actions");
6103
- if (existsSync5(pendingDir)) {
4865
+ const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
4866
+ const pendingDir = resolve3(platformRoot2, "..", "data/accounts", accountId, "pending-actions");
4867
+ if (existsSync3(pendingDir)) {
6104
4868
  const files = readdirSync(pendingDir).filter((f) => f.endsWith(".json") && !f.startsWith("."));
6105
4869
  for (const file of files) {
6106
4870
  try {
6107
- const raw2 = readFileSync6(resolve6(pendingDir, file), "utf-8");
4871
+ const raw2 = readFileSync4(resolve3(pendingDir, file), "utf-8");
6108
4872
  const action = JSON.parse(raw2);
6109
4873
  if (action.state === "pending") {
6110
4874
  const inputSummary = JSON.stringify(action.hookPayload?.tool_input ?? {}).slice(0, 150);
@@ -6171,12 +4935,12 @@ ${sections.join("\n\n")}
6171
4935
  }
6172
4936
  }
6173
4937
  async function consumeStep7FlagUI(session, accountId) {
6174
- const accountDir = resolve6(PLATFORM_ROOT3, "..", "data/accounts", accountId);
6175
- const flagPath = resolve6(accountDir, "onboarding", "step7-complete");
6176
- if (!existsSync5(flagPath)) return false;
4938
+ const accountDir = resolve3(PLATFORM_ROOT2, "..", "data/accounts", accountId);
4939
+ const flagPath = resolve3(accountDir, "onboarding", "step7-complete");
4940
+ if (!existsSync3(flagPath)) return false;
6177
4941
  let completedAt = (/* @__PURE__ */ new Date()).toISOString();
6178
4942
  try {
6179
- const raw2 = readFileSync6(flagPath, "utf-8").trim();
4943
+ const raw2 = readFileSync4(flagPath, "utf-8").trim();
6180
4944
  if (raw2) {
6181
4945
  const parsed = JSON.parse(raw2);
6182
4946
  if (typeof parsed.completedAt === "string") {
@@ -6614,8 +5378,8 @@ ${items}
6614
5378
  }
6615
5379
 
6616
5380
  // app/lib/adherence-ledger.ts
6617
- import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync7, renameSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
6618
- import { resolve as resolve7, dirname as dirname2 } from "path";
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";
6619
5383
  import lockfile from "proper-lockfile";
6620
5384
  var LOG_TAG = "[adherence-ledger]";
6621
5385
  var ADHERENCE_BLOCK_THRESHOLD = 5;
@@ -6630,7 +5394,7 @@ function nowIso() {
6630
5394
  return (/* @__PURE__ */ new Date()).toISOString();
6631
5395
  }
6632
5396
  function ledgerPath(accountDir, agentName) {
6633
- return resolve7(accountDir, "agents", agentName, "adherence-ledger.json");
5397
+ return resolve4(accountDir, "agents", agentName, "adherence-ledger.json");
6634
5398
  }
6635
5399
  function lockPath(accountDir, agentName) {
6636
5400
  return `${ledgerPath(accountDir, agentName)}.lock`;
@@ -6655,8 +5419,8 @@ function pruneViolations(violations) {
6655
5419
  });
6656
5420
  }
6657
5421
  function readUnlocked(path2, accountId, agentName) {
6658
- if (!existsSync6(path2)) return emptyLedger(accountId, agentName);
6659
- const content = readFileSync7(path2, "utf-8");
5422
+ if (!existsSync4(path2)) return emptyLedger(accountId, agentName);
5423
+ const content = readFileSync5(path2, "utf-8");
6660
5424
  if (!content.trim()) return emptyLedger(accountId, agentName);
6661
5425
  const data = JSON.parse(content);
6662
5426
  if (typeof data !== "object" || data === null) {
@@ -6675,10 +5439,10 @@ function writeAtomic(path2, data) {
6675
5439
  }
6676
5440
  data.updated_at = nowIso();
6677
5441
  const dir = dirname2(path2);
6678
- mkdirSync5(dir, { recursive: true });
5442
+ mkdirSync3(dir, { recursive: true });
6679
5443
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
6680
5444
  try {
6681
- writeFileSync5(tmp, JSON.stringify(data, null, 2), "utf-8");
5445
+ writeFileSync4(tmp, JSON.stringify(data, null, 2), "utf-8");
6682
5446
  renameSync(tmp, path2);
6683
5447
  } catch (err) {
6684
5448
  try {
@@ -6691,9 +5455,9 @@ function writeAtomic(path2, data) {
6691
5455
  async function withLock(accountDir, agentName, fn) {
6692
5456
  const path2 = ledgerPath(accountDir, agentName);
6693
5457
  const lock = lockPath(accountDir, agentName);
6694
- mkdirSync5(dirname2(lock), { recursive: true });
6695
- if (!existsSync6(lock)) {
6696
- writeFileSync5(lock, "", "utf-8");
5458
+ mkdirSync3(dirname2(lock), { recursive: true });
5459
+ if (!existsSync4(lock)) {
5460
+ writeFileSync4(lock, "", "utf-8");
6697
5461
  }
6698
5462
  const release = await lockfile.lock(lock, {
6699
5463
  realpath: false,
@@ -7179,20 +5943,20 @@ function agentLogStream(name, accountDir, conversationId) {
7179
5943
  if (!conversationId) {
7180
5944
  throw new Error(`agentLogStream: conversationId is required (name=${name}) \u2014 use preConversationLogStream for pre-session events`);
7181
5945
  }
7182
- const logDir = resolve8(accountDir, "logs");
7183
- mkdirSync6(logDir, { recursive: true });
5946
+ const logDir = resolve5(accountDir, "logs");
5947
+ mkdirSync4(logDir, { recursive: true });
7184
5948
  purgeOldLogs(logDir, `${name}-`);
7185
- const logPath2 = resolve8(logDir, `${name}-${conversationId}.log`);
5949
+ const logPath2 = resolve5(logDir, `${name}-${conversationId}.log`);
7186
5950
  const stream = createWriteStream(logPath2, { flags: "a" });
7187
5951
  registerStreamLog(stream, { path: logPath2, conversationId, name });
7188
5952
  return stream;
7189
5953
  }
7190
5954
  function preConversationLogStream(name, accountDir) {
7191
- const logDir = resolve8(accountDir, "logs");
7192
- mkdirSync6(logDir, { recursive: true });
5955
+ const logDir = resolve5(accountDir, "logs");
5956
+ mkdirSync4(logDir, { recursive: true });
7193
5957
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
7194
5958
  purgeOldLogs(logDir, `preconversation-${name}-`);
7195
- const logPath2 = resolve8(logDir, `preconversation-${name}-${date}.log`);
5959
+ const logPath2 = resolve5(logDir, `preconversation-${name}-${date}.log`);
7196
5960
  const stream = createWriteStream(logPath2, { flags: "a" });
7197
5961
  registerStreamLog(stream, { path: logPath2, conversationId: null, name: `preconversation-${name}` });
7198
5962
  return stream;
@@ -7211,7 +5975,7 @@ function sigtermFlushStreamLogs(reason, source) {
7211
5975
  const line = `[${ts}] [server-sigterm] reason=${reason}${convPart} name=${entry.name} source=${source}
7212
5976
  `;
7213
5977
  try {
7214
- appendFileSync3(entry.path, line);
5978
+ appendFileSync(entry.path, line);
7215
5979
  } catch (err) {
7216
5980
  const msg = err instanceof Error ? err.message : String(err);
7217
5981
  console.error(`[server-sigterm-flush-err] path=${entry.path} reason=${msg}`);
@@ -7230,7 +5994,7 @@ function purgeOldLogs(logDir, prefix) {
7230
5994
  }
7231
5995
  for (const file of entries) {
7232
5996
  if (!file.startsWith(prefix)) continue;
7233
- const filePath = resolve8(logDir, file);
5997
+ const filePath = resolve5(logDir, file);
7234
5998
  try {
7235
5999
  if (statSync3(filePath).mtimeMs < cutoff) unlinkSync3(filePath);
7236
6000
  } catch (err) {
@@ -7299,7 +6063,7 @@ function sampleProcState(pid) {
7299
6063
  let sockets2 = 0;
7300
6064
  for (const tcpFile of ["/proc/" + pid + "/net/tcp", "/proc/" + pid + "/net/tcp6"]) {
7301
6065
  try {
7302
- const raw2 = readFileSync8(tcpFile, "utf-8");
6066
+ const raw2 = readFileSync6(tcpFile, "utf-8");
7303
6067
  const lines2 = raw2.split("\n");
7304
6068
  for (let i = 1; i < lines2.length; i++) {
7305
6069
  const line = lines2[i].trim();
@@ -7315,7 +6079,7 @@ function sampleProcState(pid) {
7315
6079
  }
7316
6080
  let rssMb = 0;
7317
6081
  try {
7318
- const statm = readFileSync8(`/proc/${pid}/statm`, "utf-8").trim().split(/\s+/);
6082
+ const statm = readFileSync6(`/proc/${pid}/statm`, "utf-8").trim().split(/\s+/);
7319
6083
  const rssPages = parseInt(statm[1] ?? "0", 10);
7320
6084
  if (Number.isFinite(rssPages)) rssMb = Math.round(rssPages * 4096 / (1024 * 1024));
7321
6085
  } catch {
@@ -7348,21 +6112,21 @@ function sampleProcState(pid) {
7348
6112
  return `proc_err=${JSON.stringify(msg.slice(0, 60))}`;
7349
6113
  }
7350
6114
  }
7351
- var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve8(process.cwd(), "..");
7352
- var ACCOUNTS_DIR = resolve8(PLATFORM_ROOT4, "..", "data/accounts");
7353
- if (!existsSync7(PLATFORM_ROOT4)) {
6115
+ var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
6116
+ var ACCOUNTS_DIR = resolve5(PLATFORM_ROOT3, "..", "data/accounts");
6117
+ if (!existsSync5(PLATFORM_ROOT3)) {
7354
6118
  throw new Error(
7355
- `PLATFORM_ROOT does not exist: ${PLATFORM_ROOT4}
6119
+ `PLATFORM_ROOT does not exist: ${PLATFORM_ROOT3}
7356
6120
  Set the MAXY_PLATFORM_ROOT environment variable to the absolute path of the platform directory.`
7357
6121
  );
7358
6122
  }
7359
6123
  function resolveAccount() {
7360
- if (!existsSync7(ACCOUNTS_DIR)) return null;
7361
- const usersFilePath = resolve8(PLATFORM_ROOT4, "config", "users.json");
6124
+ if (!existsSync5(ACCOUNTS_DIR)) return null;
6125
+ const usersFilePath = resolve5(PLATFORM_ROOT3, "config", "users.json");
7362
6126
  let usersJsonUserId = null;
7363
- if (existsSync7(usersFilePath)) {
6127
+ if (existsSync5(usersFilePath)) {
7364
6128
  try {
7365
- const raw2 = readFileSync8(usersFilePath, "utf-8").trim();
6129
+ const raw2 = readFileSync6(usersFilePath, "utf-8").trim();
7366
6130
  if (raw2) {
7367
6131
  const users = JSON.parse(raw2);
7368
6132
  if (users.length > 0) {
@@ -7376,9 +6140,9 @@ function resolveAccount() {
7376
6140
  let fallback = null;
7377
6141
  for (const entry of entries) {
7378
6142
  if (!entry.isDirectory()) continue;
7379
- const configPath2 = resolve8(ACCOUNTS_DIR, entry.name, "account.json");
7380
- if (!existsSync7(configPath2)) continue;
7381
- const raw2 = readFileSync8(configPath2, "utf-8");
6143
+ const configPath2 = resolve5(ACCOUNTS_DIR, entry.name, "account.json");
6144
+ if (!existsSync5(configPath2)) continue;
6145
+ const raw2 = readFileSync6(configPath2, "utf-8");
7382
6146
  let config;
7383
6147
  try {
7384
6148
  config = JSON.parse(raw2);
@@ -7393,7 +6157,7 @@ function resolveAccount() {
7393
6157
  }
7394
6158
  const result = {
7395
6159
  accountId: config.accountId,
7396
- accountDir: resolve8(ACCOUNTS_DIR, entry.name),
6160
+ accountDir: resolve5(ACCOUNTS_DIR, entry.name),
7397
6161
  config
7398
6162
  };
7399
6163
  if (usersJsonUserId && config.admins?.some((a) => a.userId === usersJsonUserId)) {
@@ -7411,9 +6175,9 @@ function resolveAccount() {
7411
6175
  return fallback;
7412
6176
  }
7413
6177
  function readAgentFile(accountDir, agentName, filename) {
7414
- const filePath = resolve8(accountDir, "agents", agentName, filename);
7415
- if (!existsSync7(filePath)) return null;
7416
- return readFileSync8(filePath, "utf-8");
6178
+ const filePath = resolve5(accountDir, "agents", agentName, filename);
6179
+ if (!existsSync5(filePath)) return null;
6180
+ return readFileSync6(filePath, "utf-8");
7417
6181
  }
7418
6182
  function readIdentity(accountDir, agentName) {
7419
6183
  return readAgentFile(accountDir, agentName, "IDENTITY.md");
@@ -7448,14 +6212,14 @@ function validateAgentSlug(slug) {
7448
6212
  return true;
7449
6213
  }
7450
6214
  function resolveDefaultAgentSlug(accountDir) {
7451
- const configPath2 = resolve8(accountDir, "account.json");
7452
- if (!existsSync7(configPath2)) {
6215
+ const configPath2 = resolve5(accountDir, "account.json");
6216
+ if (!existsSync5(configPath2)) {
7453
6217
  console.error("[agent-resolve] account.json not found \u2014 cannot resolve defaultAgent");
7454
6218
  return null;
7455
6219
  }
7456
6220
  let config;
7457
6221
  try {
7458
- config = JSON.parse(readFileSync8(configPath2, "utf-8"));
6222
+ config = JSON.parse(readFileSync6(configPath2, "utf-8"));
7459
6223
  } catch (err) {
7460
6224
  console.error("[agent-resolve] failed to read account.json:", err);
7461
6225
  return null;
@@ -7464,8 +6228,8 @@ function resolveDefaultAgentSlug(accountDir) {
7464
6228
  console.error("[agent-resolve] defaultAgent not configured in account.json \u2014 set it via the connect-whatsapp skill");
7465
6229
  return null;
7466
6230
  }
7467
- const agentConfigPath = resolve8(accountDir, "agents", config.defaultAgent, "config.json");
7468
- if (!existsSync7(agentConfigPath)) {
6231
+ const agentConfigPath = resolve5(accountDir, "agents", config.defaultAgent, "config.json");
6232
+ if (!existsSync5(agentConfigPath)) {
7469
6233
  console.error(`[agent-resolve] defaultAgent="${config.defaultAgent}" has no config.json at ${agentConfigPath}`);
7470
6234
  return null;
7471
6235
  }
@@ -7537,23 +6301,23 @@ function resolveAgentConfig(accountDir, agentName) {
7537
6301
  }
7538
6302
  let knowledge = null;
7539
6303
  let knowledgeBaked = false;
7540
- const agentDir = resolve8(accountDir, "agents", agentName);
7541
- const knowledgePath = resolve8(agentDir, "KNOWLEDGE.md");
7542
- const summaryPath = resolve8(agentDir, "KNOWLEDGE-SUMMARY.md");
7543
- const hasKnowledge = existsSync7(knowledgePath);
7544
- const hasSummary = existsSync7(summaryPath);
6304
+ const agentDir = resolve5(accountDir, "agents", agentName);
6305
+ const knowledgePath = resolve5(agentDir, "KNOWLEDGE.md");
6306
+ const summaryPath = resolve5(agentDir, "KNOWLEDGE-SUMMARY.md");
6307
+ const hasKnowledge = existsSync5(knowledgePath);
6308
+ const hasSummary = existsSync5(summaryPath);
7545
6309
  if (hasKnowledge && hasSummary) {
7546
6310
  const knowledgeMtime = statSync3(knowledgePath).mtimeMs;
7547
6311
  const summaryMtime = statSync3(summaryPath).mtimeMs;
7548
6312
  if (summaryMtime >= knowledgeMtime) {
7549
- knowledge = readFileSync8(summaryPath, "utf-8");
6313
+ knowledge = readFileSync6(summaryPath, "utf-8");
7550
6314
  } else {
7551
6315
  console.warn(`[agent-config] ${agentName}: KNOWLEDGE-SUMMARY.md is stale (KNOWLEDGE.md is newer) \u2014 using full knowledge`);
7552
- knowledge = readFileSync8(knowledgePath, "utf-8");
6316
+ knowledge = readFileSync6(knowledgePath, "utf-8");
7553
6317
  }
7554
6318
  knowledgeBaked = true;
7555
6319
  } else if (hasKnowledge) {
7556
- knowledge = readFileSync8(knowledgePath, "utf-8");
6320
+ knowledge = readFileSync6(knowledgePath, "utf-8");
7557
6321
  knowledgeBaked = true;
7558
6322
  }
7559
6323
  let budget = null;
@@ -7575,11 +6339,11 @@ function resolveAgentConfig(accountDir, agentName) {
7575
6339
  return { model, plugins, status, displayName, image, imageShape, showAgentName, knowledge, knowledgeBaked, liveMemory, knowledgeKeywords, budget, accessMode };
7576
6340
  }
7577
6341
  function parsePluginFrontmatter(pluginDir) {
7578
- const pluginPath = resolve8(PLATFORM_ROOT4, "plugins", pluginDir, "PLUGIN.md");
7579
- if (!existsSync7(pluginPath)) return null;
6342
+ const pluginPath = resolve5(PLATFORM_ROOT3, "plugins", pluginDir, "PLUGIN.md");
6343
+ if (!existsSync5(pluginPath)) return null;
7580
6344
  let raw2;
7581
6345
  try {
7582
- raw2 = readFileSync8(pluginPath, "utf-8");
6346
+ raw2 = readFileSync6(pluginPath, "utf-8");
7583
6347
  } catch {
7584
6348
  console.warn(`[plugins] cannot read ${pluginPath}`);
7585
6349
  return null;
@@ -7638,24 +6402,24 @@ function parsePluginFrontmatter(pluginDir) {
7638
6402
  function autoDeliverPremiumPlugins(purchasedPlugins) {
7639
6403
  if (!purchasedPlugins || purchasedPlugins.length === 0) return;
7640
6404
  const TAG19 = "[premium-auto-deliver]";
7641
- const stagingRoot = resolve8(PLATFORM_ROOT4, "../premium-plugins");
7642
- const pluginsDir = resolve8(PLATFORM_ROOT4, "plugins");
7643
- if (!existsSync7(stagingRoot)) {
6405
+ const stagingRoot = resolve5(PLATFORM_ROOT3, "../premium-plugins");
6406
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
6407
+ if (!existsSync5(stagingRoot)) {
7644
6408
  console.log(`${TAG19} no staging directory \u2014 skipping`);
7645
6409
  return;
7646
6410
  }
7647
6411
  for (const pluginName of purchasedPlugins) {
7648
- const stagingDir = resolve8(stagingRoot, pluginName);
7649
- if (!existsSync7(stagingDir)) {
6412
+ const stagingDir = resolve5(stagingRoot, pluginName);
6413
+ if (!existsSync5(stagingDir)) {
7650
6414
  console.log(`${TAG19} ${pluginName}: not in staging \u2014 skipping`);
7651
6415
  continue;
7652
6416
  }
7653
- const bundlePath = join4(stagingDir, "BUNDLE.md");
7654
- const isBundle = existsSync7(bundlePath);
6417
+ const bundlePath = join3(stagingDir, "BUNDLE.md");
6418
+ const isBundle = existsSync5(bundlePath);
7655
6419
  if (isBundle) {
7656
6420
  let bundleRaw;
7657
6421
  try {
7658
- bundleRaw = readFileSync8(bundlePath, "utf-8");
6422
+ bundleRaw = readFileSync6(bundlePath, "utf-8");
7659
6423
  } catch (err) {
7660
6424
  console.log(`${TAG19} ${pluginName}: cannot read BUNDLE.md \u2014 ${err instanceof Error ? err.message : String(err)}`);
7661
6425
  continue;
@@ -7686,13 +6450,13 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
7686
6450
  let delivered = 0;
7687
6451
  let skipped = 0;
7688
6452
  for (const sub of subPlugins) {
7689
- const target = resolve8(pluginsDir, sub);
7690
- if (existsSync7(resolve8(target, "PLUGIN.md"))) {
6453
+ const target = resolve5(pluginsDir, sub);
6454
+ if (existsSync5(resolve5(target, "PLUGIN.md"))) {
7691
6455
  skipped++;
7692
6456
  continue;
7693
6457
  }
7694
- const source = resolve8(stagingDir, "plugins", sub);
7695
- if (!existsSync7(source)) {
6458
+ const source = resolve5(stagingDir, "plugins", sub);
6459
+ if (!existsSync5(source)) {
7696
6460
  console.log(`${TAG19} ${pluginName}/${sub}: source missing in staging \u2014 skipping`);
7697
6461
  continue;
7698
6462
  }
@@ -7705,8 +6469,8 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
7705
6469
  }
7706
6470
  console.log(`${TAG19} ${pluginName} (bundle): ${delivered} delivered, ${skipped} already present`);
7707
6471
  } else {
7708
- const target = resolve8(pluginsDir, pluginName);
7709
- if (existsSync7(resolve8(target, "PLUGIN.md"))) {
6472
+ const target = resolve5(pluginsDir, pluginName);
6473
+ if (existsSync5(resolve5(target, "PLUGIN.md"))) {
7710
6474
  console.log(`${TAG19} ${pluginName}: already present \u2014 skipping`);
7711
6475
  continue;
7712
6476
  }
@@ -7746,21 +6510,21 @@ function migratePluginRenames(accountDir, config) {
7746
6510
  return name;
7747
6511
  });
7748
6512
  if (!changed) return;
7749
- const configPath2 = resolve8(accountDir, "account.json");
6513
+ const configPath2 = resolve5(accountDir, "account.json");
7750
6514
  try {
7751
- const raw2 = readFileSync8(configPath2, "utf-8");
6515
+ const raw2 = readFileSync6(configPath2, "utf-8");
7752
6516
  const parsed = JSON.parse(raw2);
7753
6517
  parsed.enabledPlugins = migrated;
7754
- writeFileSync6(configPath2, JSON.stringify(parsed, null, 2) + "\n");
6518
+ writeFileSync5(configPath2, JSON.stringify(parsed, null, 2) + "\n");
7755
6519
  config.enabledPlugins = migrated;
7756
6520
  console.log(`${TAG19} account.json updated (${migrated.length} plugins)`);
7757
6521
  } catch (err) {
7758
6522
  console.error(`${TAG19} failed to update account.json \u2014 ${err instanceof Error ? err.message : String(err)}`);
7759
6523
  }
7760
- const pluginsDir = resolve8(PLATFORM_ROOT4, "plugins");
6524
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
7761
6525
  for (const oldName of Object.keys(PLUGIN_RENAMES)) {
7762
- const orphan = resolve8(pluginsDir, oldName);
7763
- if (existsSync7(orphan)) {
6526
+ const orphan = resolve5(pluginsDir, oldName);
6527
+ if (existsSync5(orphan)) {
7764
6528
  try {
7765
6529
  rmSync2(orphan, { recursive: true });
7766
6530
  console.log(`${TAG19} removed orphan: ${oldName}`);
@@ -7773,22 +6537,22 @@ function migratePluginRenames(accountDir, config) {
7773
6537
  function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
7774
6538
  if (!purchasedPlugins || purchasedPlugins.length === 0) return;
7775
6539
  const TAG19 = "[bundle-agent-deliver]";
7776
- const stagingRoot = resolve8(PLATFORM_ROOT4, "../premium-plugins");
7777
- const specialistsDir = resolve8(accountDir, "specialists", "agents");
7778
- if (!existsSync7(stagingRoot)) return;
7779
- if (!existsSync7(specialistsDir)) {
7780
- mkdirSync6(specialistsDir, { recursive: true });
6540
+ const stagingRoot = resolve5(PLATFORM_ROOT3, "../premium-plugins");
6541
+ const specialistsDir = resolve5(accountDir, "specialists", "agents");
6542
+ if (!existsSync5(stagingRoot)) return;
6543
+ if (!existsSync5(specialistsDir)) {
6544
+ mkdirSync4(specialistsDir, { recursive: true });
7781
6545
  }
7782
- const agentsmdPath = resolve8(accountDir, "agents", "admin", "AGENTS.md");
6546
+ const agentsmdPath = resolve5(accountDir, "agents", "admin", "AGENTS.md");
7783
6547
  let agentsmd = "";
7784
6548
  try {
7785
- agentsmd = existsSync7(agentsmdPath) ? readFileSync8(agentsmdPath, "utf-8") : "";
6549
+ agentsmd = existsSync5(agentsmdPath) ? readFileSync6(agentsmdPath, "utf-8") : "";
7786
6550
  } catch {
7787
6551
  }
7788
6552
  let delivered = 0;
7789
6553
  for (const pluginName of purchasedPlugins) {
7790
- const bundleAgentsDir = resolve8(stagingRoot, pluginName, "agents");
7791
- if (!existsSync7(bundleAgentsDir)) continue;
6554
+ const bundleAgentsDir = resolve5(stagingRoot, pluginName, "agents");
6555
+ if (!existsSync5(bundleAgentsDir)) continue;
7792
6556
  let entries;
7793
6557
  try {
7794
6558
  entries = readdirSync2(bundleAgentsDir).filter((f) => f.endsWith(".md"));
@@ -7796,9 +6560,9 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
7796
6560
  continue;
7797
6561
  }
7798
6562
  for (const filename of entries) {
7799
- const target = resolve8(specialistsDir, filename);
7800
- if (existsSync7(target)) continue;
7801
- const source = resolve8(bundleAgentsDir, filename);
6563
+ const target = resolve5(specialistsDir, filename);
6564
+ if (existsSync5(target)) continue;
6565
+ const source = resolve5(bundleAgentsDir, filename);
7802
6566
  try {
7803
6567
  cpSync(source, target);
7804
6568
  } catch (err) {
@@ -7806,7 +6570,7 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
7806
6570
  continue;
7807
6571
  }
7808
6572
  try {
7809
- const content = readFileSync8(target, "utf-8");
6573
+ const content = readFileSync6(target, "utf-8");
7810
6574
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
7811
6575
  if (fmMatch) {
7812
6576
  const nameMatch = fmMatch[1].match(/^name:\s*(.+)/m);
@@ -7830,7 +6594,7 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
7830
6594
  }
7831
6595
  if (delivered > 0) {
7832
6596
  try {
7833
- writeFileSync6(agentsmdPath, agentsmd);
6597
+ writeFileSync5(agentsmdPath, agentsmd);
7834
6598
  console.log(`${TAG19} AGENTS.md updated (${delivered} agents added)`);
7835
6599
  } catch (err) {
7836
6600
  console.error(`${TAG19} AGENTS.md update failed \u2014 ${err instanceof Error ? err.message : String(err)}`);
@@ -7838,11 +6602,11 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
7838
6602
  }
7839
6603
  }
7840
6604
  function assemblePublicPluginContent(pluginDir) {
7841
- const pluginRoot = resolve8(PLATFORM_ROOT4, "plugins", pluginDir);
7842
- const pluginPath = resolve8(pluginRoot, "PLUGIN.md");
6605
+ const pluginRoot = resolve5(PLATFORM_ROOT3, "plugins", pluginDir);
6606
+ const pluginPath = resolve5(pluginRoot, "PLUGIN.md");
7843
6607
  let raw2;
7844
6608
  try {
7845
- raw2 = readFileSync8(pluginPath, "utf-8");
6609
+ raw2 = readFileSync6(pluginPath, "utf-8");
7846
6610
  } catch {
7847
6611
  return null;
7848
6612
  }
@@ -7851,7 +6615,7 @@ function assemblePublicPluginContent(pluginDir) {
7851
6615
  const parts = [pluginBody];
7852
6616
  let skillCount = 0;
7853
6617
  let refCount = 0;
7854
- const skillsDir = resolve8(pluginRoot, "skills");
6618
+ const skillsDir = resolve5(pluginRoot, "skills");
7855
6619
  let skillDirs;
7856
6620
  try {
7857
6621
  skillDirs = readdirSync2(skillsDir).sort();
@@ -7859,11 +6623,11 @@ function assemblePublicPluginContent(pluginDir) {
7859
6623
  return { body: pluginBody, skillCount: 0, refCount: 0 };
7860
6624
  }
7861
6625
  for (const skillName of skillDirs) {
7862
- const skillDir = resolve8(skillsDir, skillName);
7863
- const skillMdPath = resolve8(skillDir, "SKILL.md");
6626
+ const skillDir = resolve5(skillsDir, skillName);
6627
+ const skillMdPath = resolve5(skillDir, "SKILL.md");
7864
6628
  let skillRaw;
7865
6629
  try {
7866
- skillRaw = readFileSync8(skillMdPath, "utf-8");
6630
+ skillRaw = readFileSync6(skillMdPath, "utf-8");
7867
6631
  } catch {
7868
6632
  continue;
7869
6633
  }
@@ -7903,7 +6667,7 @@ function assemblePublicPluginContent(pluginDir) {
7903
6667
  parts.push(`
7904
6668
  <!-- skill: ${skillName} -->`);
7905
6669
  parts.push(skillBody);
7906
- const refsDir = resolve8(skillDir, "references");
6670
+ const refsDir = resolve5(skillDir, "references");
7907
6671
  let refFiles;
7908
6672
  try {
7909
6673
  refFiles = readdirSync2(refsDir).filter((f) => f.endsWith(".md")).filter((f) => !publicExcludeReferences.includes(f)).sort();
@@ -7914,7 +6678,7 @@ function assemblePublicPluginContent(pluginDir) {
7914
6678
  }
7915
6679
  for (const refFile of refFiles) {
7916
6680
  try {
7917
- const refContent = readFileSync8(resolve8(refsDir, refFile), "utf-8").trim();
6681
+ const refContent = readFileSync6(resolve5(refsDir, refFile), "utf-8").trim();
7918
6682
  if (refContent) {
7919
6683
  parts.push(`
7920
6684
  <!-- reference: ${refFile} -->`);
@@ -7932,7 +6696,7 @@ function assemblePublicPluginContent(pluginDir) {
7932
6696
  return { body: parts.join("\n"), skillCount, refCount };
7933
6697
  }
7934
6698
  function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
7935
- const pluginsDir = resolve8(PLATFORM_ROOT4, "plugins");
6699
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
7936
6700
  let dirs;
7937
6701
  try {
7938
6702
  dirs = readdirSync2(pluginsDir);
@@ -7985,10 +6749,10 @@ function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
7985
6749
  console.log(`[plugins] loaded ${dir} for public (${assembled.body.length} chars, ${assembled.skillCount} skills, ${assembled.refCount} refs)`);
7986
6750
  }
7987
6751
  } else {
7988
- const pluginPath = resolve8(pluginsDir, dir, "PLUGIN.md");
6752
+ const pluginPath = resolve5(pluginsDir, dir, "PLUGIN.md");
7989
6753
  let raw2;
7990
6754
  try {
7991
- raw2 = readFileSync8(pluginPath, "utf-8");
6755
+ raw2 = readFileSync6(pluginPath, "utf-8");
7992
6756
  } catch (err) {
7993
6757
  console.warn(`[plugins] ${dir}: failed to read PLUGIN.md for ${agentType} embed: ${String(err)}`);
7994
6758
  continue;
@@ -8012,14 +6776,14 @@ var mcpToolsCache = /* @__PURE__ */ new Map();
8012
6776
  function fetchMcpToolsList(pluginDir) {
8013
6777
  const cached = mcpToolsCache.get(pluginDir);
8014
6778
  if (cached) return Promise.resolve(cached);
8015
- const serverPath = resolve8(PLATFORM_ROOT4, "plugins", pluginDir, "mcp/dist/index.js");
8016
- if (!existsSync7(serverPath)) return Promise.resolve([]);
6779
+ const serverPath = resolve5(PLATFORM_ROOT3, "plugins", pluginDir, "mcp/dist/index.js");
6780
+ if (!existsSync5(serverPath)) return Promise.resolve([]);
8017
6781
  const startMs = Date.now();
8018
6782
  return new Promise((resolvePromise) => {
8019
6783
  const proc = spawn2(process.execPath, [serverPath], {
8020
6784
  env: {
8021
6785
  ...process.env,
8022
- PLATFORM_ROOT: PLATFORM_ROOT4,
6786
+ PLATFORM_ROOT: PLATFORM_ROOT3,
8023
6787
  ACCOUNT_ID: "__toolslist__",
8024
6788
  PLATFORM_PORT: process.env.PORT ?? "19200"
8025
6789
  }
@@ -8125,7 +6889,7 @@ var SPECIALIST_PLUGIN_DOMAINS = {
8125
6889
  // agent, so it retains a full manifest entry for routing clarity.
8126
6890
  };
8127
6891
  async function buildPluginManifest(enabledPlugins) {
8128
- const pluginsDir = resolve8(PLATFORM_ROOT4, "plugins");
6892
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
8129
6893
  let dirs;
8130
6894
  try {
8131
6895
  dirs = readdirSync2(pluginsDir);
@@ -8212,16 +6976,16 @@ ${specialist}: ${plugins.join(", ")}`);
8212
6976
  for (let j = 0; j < adminPlugins.length; j++) {
8213
6977
  const { dir, parsed } = adminPlugins[j];
8214
6978
  const mcpTools = adminMcpResults[j];
8215
- const pluginRoot = resolve8(pluginsDir, dir);
6979
+ const pluginRoot = resolve5(pluginsDir, dir);
8216
6980
  const skills = [];
8217
6981
  const references = [];
8218
6982
  const scanDir = (base, prefix, target) => {
8219
- const scanPath = resolve8(pluginRoot, base);
8220
- if (!existsSync7(scanPath)) return;
6983
+ const scanPath = resolve5(pluginRoot, base);
6984
+ if (!existsSync5(scanPath)) return;
8221
6985
  try {
8222
6986
  const walk = (current, rel) => {
8223
6987
  for (const entry of readdirSync2(current)) {
8224
- const full = resolve8(current, entry);
6988
+ const full = resolve5(current, entry);
8225
6989
  try {
8226
6990
  const stat5 = statSync3(full);
8227
6991
  if (stat5.isDirectory()) {
@@ -8249,8 +7013,8 @@ ${specialist}: ${plugins.join(", ")}`);
8249
7013
  toolLines.push(desc ? ` ${tool.name} \u2014 ${desc}` : ` ${tool.name}`);
8250
7014
  }
8251
7015
  } else if (parsed.tools.length > 0) {
8252
- const serverPath = resolve8(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
8253
- if (existsSync7(serverPath)) {
7016
+ const serverPath = resolve5(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
7017
+ if (existsSync5(serverPath)) {
8254
7018
  fallbackSourced++;
8255
7019
  console.error(`[plugin-manifest] ${dir}: tools/list empty \u2014 fallback to frontmatter (${parsed.tools.length} tools)`);
8256
7020
  }
@@ -8325,16 +7089,16 @@ function getDefaultAccountId() {
8325
7089
  return resolveAccount()?.accountId ?? null;
8326
7090
  }
8327
7091
  function resolveUserAccounts(userId) {
8328
- if (!existsSync7(ACCOUNTS_DIR)) return [];
7092
+ if (!existsSync5(ACCOUNTS_DIR)) return [];
8329
7093
  const results = [];
8330
7094
  const entries = readdirSync2(ACCOUNTS_DIR, { withFileTypes: true });
8331
7095
  for (const entry of entries) {
8332
7096
  if (!entry.isDirectory()) continue;
8333
- const configPath2 = resolve8(ACCOUNTS_DIR, entry.name, "account.json");
8334
- if (!existsSync7(configPath2)) continue;
7097
+ const configPath2 = resolve5(ACCOUNTS_DIR, entry.name, "account.json");
7098
+ if (!existsSync5(configPath2)) continue;
8335
7099
  let config;
8336
7100
  try {
8337
- config = JSON.parse(readFileSync8(configPath2, "utf-8"));
7101
+ config = JSON.parse(readFileSync6(configPath2, "utf-8"));
8338
7102
  } catch {
8339
7103
  console.error(`[session] account.json corrupt at ${configPath2} \u2014 skipping`);
8340
7104
  continue;
@@ -8343,7 +7107,7 @@ function resolveUserAccounts(userId) {
8343
7107
  if (adminEntry) {
8344
7108
  results.push({
8345
7109
  accountId: config.accountId,
8346
- accountDir: resolve8(ACCOUNTS_DIR, entry.name),
7110
+ accountDir: resolve5(ACCOUNTS_DIR, entry.name),
8347
7111
  config,
8348
7112
  role: adminEntry.role
8349
7113
  });
@@ -8404,8 +7168,75 @@ function clearSessionHistory(sessionKey) {
8404
7168
  session.stalledSubagents = void 0;
8405
7169
  session.pendingTrimmedMessages = void 0;
8406
7170
  session.pendingCommitmentOffers = void 0;
7171
+ session.pendingTurns = void 0;
8407
7172
  return previousConversationId;
8408
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
+ }
8409
7240
  function getAgentNameForSession(sessionKey) {
8410
7241
  return sessionStore.get(sessionKey)?.agentName;
8411
7242
  }
@@ -8560,8 +7391,8 @@ function consumeStalledSubagents(sessionKey) {
8560
7391
  return stalls && stalls.length > 0 ? stalls : void 0;
8561
7392
  }
8562
7393
  function streamLogPathFor(accountId, conversationId) {
8563
- const logDir = resolve8(ACCOUNTS_DIR, accountId, "logs");
8564
- const streamLogPath = resolve8(logDir, `claude-agent-stream-${conversationId}.log`);
7394
+ const logDir = resolve5(ACCOUNTS_DIR, accountId, "logs");
7395
+ const streamLogPath = resolve5(logDir, `claude-agent-stream-${conversationId}.log`);
8565
7396
  return { logDir, streamLogPath };
8566
7397
  }
8567
7398
  function buildSpawnEnv(accountId, accountDir, conversationId) {
@@ -8571,7 +7402,7 @@ function buildSpawnEnv(accountId, accountDir, conversationId) {
8571
7402
  const { logDir, streamLogPath } = streamLogPathFor(accountId, conversationId);
8572
7403
  return {
8573
7404
  ...process.env,
8574
- PLATFORM_ROOT: PLATFORM_ROOT4,
7405
+ PLATFORM_ROOT: PLATFORM_ROOT3,
8575
7406
  ACCOUNT_DIR: accountDir,
8576
7407
  ACCOUNT_ID: accountId,
8577
7408
  LOG_DIR: logDir,
@@ -8582,8 +7413,8 @@ var cachedBrandHostname = null;
8582
7413
  function readBrandHostname() {
8583
7414
  if (cachedBrandHostname !== null) return cachedBrandHostname;
8584
7415
  try {
8585
- const brandPath = resolve8(PLATFORM_ROOT4, "config", "brand.json");
8586
- const parsed = JSON.parse(readFileSync8(brandPath, "utf-8"));
7416
+ const brandPath = resolve5(PLATFORM_ROOT3, "config", "brand.json");
7417
+ const parsed = JSON.parse(readFileSync6(brandPath, "utf-8"));
8587
7418
  cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
8588
7419
  } catch {
8589
7420
  cachedBrandHostname = "maxy";
@@ -8612,7 +7443,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8612
7443
  const { logDir: LOG_DIR2, streamLogPath: STREAM_LOG_PATH } = streamLogPathFor(accountId, conversationId);
8613
7444
  const baseEnv = {
8614
7445
  ACCOUNT_ID: accountId,
8615
- PLATFORM_ROOT: PLATFORM_ROOT4,
7446
+ PLATFORM_ROOT: PLATFORM_ROOT3,
8616
7447
  LOG_DIR: LOG_DIR2,
8617
7448
  STREAM_LOG_PATH,
8618
7449
  NEO4J_URI: requireNeo4jUri()
@@ -8620,37 +7451,37 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8620
7451
  const servers = {
8621
7452
  "memory": {
8622
7453
  command: "node",
8623
- args: [resolve8(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js")],
7454
+ args: [resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js")],
8624
7455
  env: { ...baseEnv, ...userId ? { USER_ID: userId } : {} }
8625
7456
  },
8626
7457
  "contacts": {
8627
7458
  command: "node",
8628
- args: [resolve8(PLATFORM_ROOT4, "plugins/contacts/mcp/dist/index.js")],
7459
+ args: [resolve5(PLATFORM_ROOT3, "plugins/contacts/mcp/dist/index.js")],
8629
7460
  env: { ...baseEnv }
8630
7461
  },
8631
7462
  "whatsapp": {
8632
7463
  command: "node",
8633
- args: [resolve8(PLATFORM_ROOT4, "plugins/whatsapp/mcp/dist/index.js")],
7464
+ args: [resolve5(PLATFORM_ROOT3, "plugins/whatsapp/mcp/dist/index.js")],
8634
7465
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
8635
7466
  },
8636
7467
  "admin": {
8637
7468
  command: "node",
8638
- args: [resolve8(PLATFORM_ROOT4, "plugins/admin/mcp/dist/index.js")],
7469
+ args: [resolve5(PLATFORM_ROOT3, "plugins/admin/mcp/dist/index.js")],
8639
7470
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200", ...userId ? { USER_ID: userId } : {} }
8640
7471
  },
8641
7472
  "scheduling": {
8642
7473
  command: "node",
8643
- args: [resolve8(PLATFORM_ROOT4, "plugins/scheduling/mcp/dist/index.js")],
7474
+ args: [resolve5(PLATFORM_ROOT3, "plugins/scheduling/mcp/dist/index.js")],
8644
7475
  env: { ...baseEnv }
8645
7476
  },
8646
7477
  "tasks": {
8647
7478
  command: "node",
8648
- args: [resolve8(PLATFORM_ROOT4, "plugins/tasks/mcp/dist/index.js")],
7479
+ args: [resolve5(PLATFORM_ROOT3, "plugins/tasks/mcp/dist/index.js")],
8649
7480
  env: { ...baseEnv }
8650
7481
  },
8651
7482
  "email": {
8652
7483
  command: "node",
8653
- args: [resolve8(PLATFORM_ROOT4, "plugins/email/mcp/dist/index.js")],
7484
+ args: [resolve5(PLATFORM_ROOT3, "plugins/email/mcp/dist/index.js")],
8654
7485
  env: { ...baseEnv }
8655
7486
  },
8656
7487
  // Workflows MCP — persistent admin-session server for list/get/update/delete/
@@ -8661,7 +7492,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8661
7492
  // ToolSearches fruitlessly before degrading to a task-create stand-in (Task 571).
8662
7493
  "workflows": {
8663
7494
  command: "node",
8664
- args: [resolve8(PLATFORM_ROOT4, "plugins/workflows/mcp/dist/index.js")],
7495
+ args: [resolve5(PLATFORM_ROOT3, "plugins/workflows/mcp/dist/index.js")],
8665
7496
  env: { ...baseEnv }
8666
7497
  },
8667
7498
  // Playwright MCP server — browser automation for browser-specialist.
@@ -8683,7 +7514,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8683
7514
  // MAXY-PRD.md:627, not in any application-layer filter).
8684
7515
  "graph": {
8685
7516
  command: "node",
8686
- args: [resolve8(PLATFORM_ROOT4, "lib/graph-mcp/dist/index.js")],
7517
+ args: [resolve5(PLATFORM_ROOT3, "lib/graph-mcp/dist/index.js")],
8687
7518
  env: {
8688
7519
  ...baseEnv,
8689
7520
  BRAND: readBrandHostname(),
@@ -8699,7 +7530,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8699
7530
  if (tgBotToken) {
8700
7531
  servers["telegram"] = {
8701
7532
  command: "node",
8702
- args: [resolve8(PLATFORM_ROOT4, "plugins/telegram/mcp/dist/index.js")],
7533
+ args: [resolve5(PLATFORM_ROOT3, "plugins/telegram/mcp/dist/index.js")],
8703
7534
  env: { ...baseEnv, TELEGRAM_BOT_TOKEN: tgBotToken }
8704
7535
  };
8705
7536
  } else {
@@ -8707,11 +7538,11 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8707
7538
  }
8708
7539
  servers["cloudflare"] = {
8709
7540
  command: "node",
8710
- args: [resolve8(PLATFORM_ROOT4, "plugins/cloudflare/mcp/dist/index.js")],
7541
+ args: [resolve5(PLATFORM_ROOT3, "plugins/cloudflare/mcp/dist/index.js")],
8711
7542
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
8712
7543
  };
8713
7544
  if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
8714
- const pluginsDir = resolve8(PLATFORM_ROOT4, "plugins");
7545
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
8715
7546
  let dirs;
8716
7547
  try {
8717
7548
  dirs = readdirSync2(pluginsDir);
@@ -8734,8 +7565,8 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8734
7565
  continue;
8735
7566
  }
8736
7567
  }
8737
- const mcpEntry = resolve8(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
8738
- if (!existsSync7(mcpEntry)) continue;
7568
+ const mcpEntry = resolve5(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
7569
+ if (!existsSync5(mcpEntry)) continue;
8739
7570
  servers[dir] = {
8740
7571
  command: "node",
8741
7572
  args: [mcpEntry],
@@ -8870,7 +7701,7 @@ var ADMIN_CORE_TOOLS = [
8870
7701
  function getAdminAllowedTools(enabledPlugins) {
8871
7702
  const tools = [...ADMIN_CORE_TOOLS];
8872
7703
  if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
8873
- const pluginsDir = resolve8(PLATFORM_ROOT4, "plugins");
7704
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
8874
7705
  let dirs;
8875
7706
  try {
8876
7707
  dirs = readdirSync2(pluginsDir);
@@ -8978,18 +7809,18 @@ ${message.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`
8978
7809
  }
8979
7810
  }
8980
7811
  async function fetchMemoryContext(accountId, query, sessionKey, options) {
8981
- const serverPath = resolve8(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
8982
- if (!existsSync7(serverPath)) {
7812
+ const serverPath = resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
7813
+ if (!existsSync5(serverPath)) {
8983
7814
  console.error(`[fetchMemoryContext] MCP server not found: ${serverPath}`);
8984
7815
  return null;
8985
7816
  }
8986
7817
  const startMs = Date.now();
8987
- return new Promise((resolve31) => {
7818
+ return new Promise((resolve28) => {
8988
7819
  const proc = spawn2(process.execPath, [serverPath], {
8989
7820
  env: {
8990
7821
  ...process.env,
8991
7822
  ACCOUNT_ID: accountId,
8992
- PLATFORM_ROOT: PLATFORM_ROOT4,
7823
+ PLATFORM_ROOT: PLATFORM_ROOT3,
8993
7824
  READ_ONLY: "true",
8994
7825
  ALLOWED_SCOPES: "public,shared",
8995
7826
  ...sessionKey ? { SESSION_ID: sessionKey } : {},
@@ -9013,7 +7844,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
9013
7844
  } else {
9014
7845
  console.error(`[fetchMemoryContext] failed: ${reason} (${elapsed}ms)${stderrBuf ? ` stderr: ${stderrBuf.slice(0, 500)}` : ""}`);
9015
7846
  }
9016
- resolve31(value);
7847
+ resolve28(value);
9017
7848
  };
9018
7849
  proc.stdout.on("data", (chunk) => {
9019
7850
  buffer += chunk.toString();
@@ -9076,8 +7907,8 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
9076
7907
  }
9077
7908
  async function compactTrimmedMessages(accountId, trimmedMessages) {
9078
7909
  if (trimmedMessages.length === 0) return true;
9079
- const serverPath = resolve8(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
9080
- if (!existsSync7(serverPath)) return false;
7910
+ const serverPath = resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
7911
+ if (!existsSync5(serverPath)) return false;
9081
7912
  const briefing = trimmedMessages.map((m) => `[${m.role.toUpperCase()}] ${m.content}`).join("\n\n");
9082
7913
  return new Promise((resolvePromise) => {
9083
7914
  const proc = spawn2(process.execPath, [serverPath], {
@@ -9394,8 +8225,8 @@ Then respond with only: [COMPACTED]`;
9394
8225
  var COMPACTION_TIMEOUT_MS = 45e3;
9395
8226
  async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, conversationId, enabledPlugins) {
9396
8227
  const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, conversationId, void 0, enabledPlugins) });
9397
- const specialistsDir = resolve8(accountDir, "specialists");
9398
- if (!existsSync7(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir, conversationId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
8228
+ const specialistsDir = resolve5(accountDir, "specialists");
8229
+ if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir, conversationId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
9399
8230
  `);
9400
8231
  const args = [
9401
8232
  "--print",
@@ -9668,7 +8499,7 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId, a
9668
8499
  const { logDir } = streamLogPathFor(accountId, conversationId);
9669
8500
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
9670
8501
  for (const s of failed) {
9671
- const stderrPath = resolve8(logDir, `mcp-${s.name}-stderr-${date}.log`);
8502
+ const stderrPath = resolve5(logDir, `mcp-${s.name}-stderr-${date}.log`);
9672
8503
  let tail = "(no stderr file)";
9673
8504
  try {
9674
8505
  const stats = statSync3(stderrPath);
@@ -10298,20 +9129,24 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
10298
9129
  const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
10299
9130
  const resumeSessionId = sessionKey ? getAgentSessionId(sessionKey) : void 0;
10300
9131
  const spawnConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
10301
- if (!spawnConvId) {
10302
- throw new Error(`invokeAdminAgent: conversationId missing for sessionKey=${sessionKey?.slice(0, 8) ?? "none"} \u2014 ensureConversation must run before invoking the agent`);
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`);
10303
9138
  }
10304
9139
  const cdpOk = await ensureCdp();
10305
9140
  if (!cdpOk) {
10306
- const cdpLog = agentLogStream("claude-agent-stream", accountDir, spawnConvId);
9141
+ const cdpLog = agentLogStream("claude-agent-stream", accountDir, spawnLogKey);
10307
9142
  cdpLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
10308
9143
  `);
10309
9144
  cdpLog.end();
10310
9145
  }
10311
9146
  const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
10312
- const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, spawnConvId, ccUserId, enabledPlugins) });
10313
- const specialistsDir = resolve8(accountDir, "specialists");
10314
- if (!existsSync7(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnConvId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
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}
10315
9150
  `);
10316
9151
  const args = [
10317
9152
  "--print",
@@ -10342,19 +9177,19 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
10342
9177
  cwd: accountDir,
10343
9178
  stdio: ["ignore", "pipe", "pipe"],
10344
9179
  // Task 556: STREAM_LOG_PATH inherited by Bash-tool subprocesses.
10345
- env: buildSpawnEnv(accountId, accountDir, spawnConvId)
9180
+ env: buildSpawnEnv(accountId, accountDir, spawnLogKey)
10346
9181
  });
10347
- const stderrLog = agentLogStream("claude-agent-stderr", accountDir, spawnConvId);
9182
+ const stderrLog = agentLogStream("claude-agent-stderr", accountDir, spawnLogKey);
10348
9183
  stderrLog.on("error", () => {
10349
9184
  });
10350
9185
  proc.stderr?.pipe(stderrLog);
10351
- const streamLog = agentLogStream("claude-agent-stream", accountDir, spawnConvId);
9186
+ const streamLog = agentLogStream("claude-agent-stream", accountDir, spawnLogKey);
10352
9187
  streamLog.on("error", () => {
10353
9188
  });
10354
9189
  teeProcStderrToStreamLog(proc, streamLog);
10355
9190
  streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
10356
9191
  `);
10357
- 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
10358
9193
  `);
10359
9194
  if (sessionKey) {
10360
9195
  const prev = activeProcesses.get(sessionKey);
@@ -10364,7 +9199,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
10364
9199
  }
10365
9200
  activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
10366
9201
  }
10367
- 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}
10368
9203
  `);
10369
9204
  streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
10370
9205
  `);
@@ -10436,7 +9271,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
10436
9271
  }
10437
9272
  if (event.type === "usage" && sessionKey && currentAgentSessionId) {
10438
9273
  const peakReqPct = event.peak_request_pct ?? 0;
10439
- if (peakReqPct >= COMPACTION_THRESHOLD) {
9274
+ if (peakReqPct >= COMPACTION_THRESHOLD && spawnConvId) {
10440
9275
  const compactionIter = runCompactionTurn(accountDir, accountId, systemPrompt, currentAgentSessionId, adminModel, spawnConvId, enabledPlugins);
10441
9276
  let step = await compactionIter.next();
10442
9277
  while (!step.done) {
@@ -10546,9 +9381,9 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
10546
9381
  } else {
10547
9382
  gotDone = true;
10548
9383
  if (!sessionWasReset) {
9384
+ const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
10549
9385
  const convId = sessionKey ? sessionStore.get(sessionKey)?.conversationId : void 0;
10550
9386
  if (convId) {
10551
- const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
10552
9387
  persistMessage(convId, "user", fullMessage, accountId, void 0, userTimestamp).catch(() => {
10553
9388
  });
10554
9389
  autoLabelSession(convId, fullMessage).catch(() => {
@@ -10556,7 +9391,14 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
10556
9391
  if (responseText) persistMessage(convId, "assistant", responseText, accountId, capturedTokens, assistantTimestamp).catch(() => {
10557
9392
  });
10558
9393
  } else if (sessionKey) {
10559
- console.warn(`[persist] skipped: no conversationId for sessionKey=${sessionKey.slice(0, 12)}\u2026 (session registered=${sessionStore.has(sessionKey)})`);
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
+ }
10560
9402
  }
10561
9403
  if (sessionKey) {
10562
9404
  const commitSession = sessionStore.get(sessionKey);
@@ -10619,9 +9461,10 @@ ${summary}`;
10619
9461
  async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accountId, adminModel, sessionKey, maxTurns = 20, attachments = [], retryCount = 0, enabledPlugins, clientTimestamp, adherenceConstraints, agentName) {
10620
9462
  const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
10621
9463
  const managedConvId = getConversationIdForSession(sessionKey);
10622
- if (!managedConvId) {
10623
- throw new Error(`invokeManagedAdminAgent: conversationId missing for sessionKey=${sessionKey.slice(0, 8)} \u2014 ensureConversation must run first`);
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`);
10624
9466
  }
9467
+ const managedLogKey = managedConvId ?? preflushStreamLogKey(sessionKey);
10625
9468
  const pendingTrimmed = consumePendingTrimmedMessages(sessionKey);
10626
9469
  if (pendingTrimmed && pendingTrimmed.length > 0) {
10627
9470
  const ok = await compactTrimmedMessages(accountId, pendingTrimmed);
@@ -10629,7 +9472,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
10629
9472
  storePendingTrimmedMessages(sessionKey, pendingTrimmed);
10630
9473
  }
10631
9474
  }
10632
- const streamLog = agentLogStream("claude-agent-stream", accountDir, managedConvId);
9475
+ const streamLog = agentLogStream("claude-agent-stream", accountDir, managedLogKey);
10633
9476
  streamLog.on("error", () => {
10634
9477
  });
10635
9478
  const systemPromptTokens = estimateTokens(systemPrompt);
@@ -10660,9 +9503,9 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
10660
9503
  if (!cdpOk) streamLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
10661
9504
  `);
10662
9505
  const managedUserId = getUserIdForSession(sessionKey);
10663
- const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedConvId, managedUserId, enabledPlugins) });
10664
- const specialistsDir = resolve8(accountDir, "specialists");
10665
- if (!existsSync7(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
9506
+ const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedLogKey, managedUserId, enabledPlugins) });
9507
+ const specialistsDir = resolve5(accountDir, "specialists");
9508
+ if (!existsSync5(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
10666
9509
  `);
10667
9510
  const fullMessage = attachments.length > 0 ? message + buildAttachmentMetaText(attachments) : message;
10668
9511
  const args = [
@@ -10691,16 +9534,16 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
10691
9534
  cwd: accountDir,
10692
9535
  stdio: ["ignore", "pipe", "pipe"],
10693
9536
  // Task 556: STREAM_LOG_PATH inherited by Bash-tool subprocesses.
10694
- env: buildSpawnEnv(accountId, accountDir, managedConvId)
9537
+ env: buildSpawnEnv(accountId, accountDir, managedLogKey)
10695
9538
  });
10696
- const stderrLog = agentLogStream("claude-agent-stderr", accountDir, managedConvId);
9539
+ const stderrLog = agentLogStream("claude-agent-stderr", accountDir, managedLogKey);
10697
9540
  stderrLog.on("error", () => {
10698
9541
  });
10699
9542
  proc.stderr?.pipe(stderrLog);
10700
9543
  teeProcStderrToStreamLog(proc, streamLog);
10701
9544
  streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
10702
9545
  `);
10703
- 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
10704
9547
  `);
10705
9548
  if (sessionKey) {
10706
9549
  const prev = activeProcesses.get(sessionKey);
@@ -10710,13 +9553,13 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
10710
9553
  }
10711
9554
  activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
10712
9555
  }
10713
- 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}
10714
9557
  `);
10715
9558
  streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
10716
9559
  `);
10717
9560
  proc.on("exit", (code, signal) => {
10718
- console.log(`[process-exit] pid=${proc.pid} code=${code} signal=${signal} sessionKey=${sessionKey ?? "none"} conversationId=${managedConvId}`);
10719
- 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"}
10720
9563
  `);
10721
9564
  if (sessionKey) activeProcesses.delete(sessionKey);
10722
9565
  });
@@ -10866,17 +9709,24 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
10866
9709
  `);
10867
9710
  const successSession = sessionStore.get(sessionKey);
10868
9711
  if (successSession) successSession.lastPeakContextPct = peakContextPct;
9712
+ const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
10869
9713
  const convId = sessionStore.get(sessionKey)?.conversationId;
10870
9714
  if (convId) {
10871
- const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
10872
9715
  persistMessage(convId, "user", fullMessage, accountId, void 0, userTimestamp).catch(() => {
10873
9716
  });
10874
9717
  autoLabelSession(convId, fullMessage).catch(() => {
10875
9718
  });
10876
9719
  if (responseText) persistMessage(convId, "assistant", responseText, accountId, capturedTokens, assistantTimestamp).catch(() => {
10877
9720
  });
10878
- } else if (sessionKey) {
10879
- console.warn(`[persist] skipped: no conversationId for sessionKey=${sessionKey.slice(0, 12)}\u2026 (session registered=${sessionStore.has(sessionKey)})`);
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
+ }
10880
9730
  }
10881
9731
  const commitSession = sessionStore.get(sessionKey);
10882
9732
  if (commitSession?.pendingCommitmentOffers && commitSession.pendingCommitmentOffers.length > 0) {
@@ -10948,10 +9798,14 @@ async function* invokePublicAgent(message, systemPrompt, accountId, accountDir,
10948
9798
  return;
10949
9799
  }
10950
9800
  const publicConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
10951
- if (!publicConvId) {
10952
- throw new Error(`invokePublicAgent: conversationId missing for sessionKey=${sessionKey?.slice(0, 8) ?? "none"} \u2014 ensureConversation must run first`);
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`);
9803
+ }
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`);
10953
9807
  }
10954
- const streamLog = agentLogStream("public-agent-stream", accountDir, publicConvId);
9808
+ const streamLog = agentLogStream("public-agent-stream", accountDir, publicLogKey);
10955
9809
  streamLog.write(`[${isoTs()}] [public-user-message] ${JSON.stringify(message)}
10956
9810
  `);
10957
9811
  if (sessionKey) {
@@ -11188,10 +10042,10 @@ User messages are prefixed with the sender's name in brackets. Address participa
11188
10042
  `);
11189
10043
  }
11190
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;
11191
10048
  if (conversationId) {
11192
- const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
11193
- const sess = sessionKey ? sessionStore.get(sessionKey) : void 0;
11194
- const sender = sess?.groupSlug && sess.visitorId && sess.senderDisplayName ? { visitorId: sess.visitorId, displayName: sess.senderDisplayName } : void 0;
11195
10049
  persistMessage(conversationId, "user", message, accountId, void 0, userTimestamp, sender).catch(() => {
11196
10050
  });
11197
10051
  autoLabelSession(conversationId, message).catch(() => {
@@ -11199,7 +10053,14 @@ User messages are prefixed with the sender's name in brackets. Address participa
11199
10053
  if (fullText) persistMessage(conversationId, "assistant", fullText, accountId, void 0, assistantTimestamp).catch(() => {
11200
10054
  });
11201
10055
  } else if (sessionKey) {
11202
- console.warn(`[persist] skipped: no conversationId for sessionKey=${sessionKey.slice(0, 12)}\u2026 (session registered=${sessionStore.has(sessionKey)})`);
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
+ }
11203
10064
  }
11204
10065
  streamLog.end();
11205
10066
  }
@@ -11355,10 +10216,10 @@ ${sessionContext}`;
11355
10216
  console.log(`[onboarding-inject] accountId=${accountId.slice(0, 8)}\u2026 error=neo4j-unreachable injected=false`);
11356
10217
  } else if (onboardingStep < 8) {
11357
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.";
11358
- const skillPath = resolve8(PLATFORM_ROOT4, "plugins/admin/skills/onboarding/SKILL.md");
10219
+ const skillPath = resolve5(PLATFORM_ROOT3, "plugins/admin/skills/onboarding/SKILL.md");
11359
10220
  let skillContent = "";
11360
10221
  try {
11361
- skillContent = readFileSync8(skillPath, "utf-8");
10222
+ skillContent = readFileSync6(skillPath, "utf-8");
11362
10223
  } catch (err) {
11363
10224
  console.error(`[onboarding-inject] accountId=${accountId.slice(0, 8)}\u2026 error=skill-read-failed path=${skillPath} reason=${err instanceof Error ? err.message : String(err)}`);
11364
10225
  }
@@ -11408,9 +10269,9 @@ ${body}`;
11408
10269
 
11409
10270
  ${manifest}`;
11410
10271
  }
11411
- const graphRefPath = resolve8(PLATFORM_ROOT4, "plugins/memory/references/graph-primitives.md");
10272
+ const graphRefPath = resolve5(PLATFORM_ROOT3, "plugins/memory/references/graph-primitives.md");
11412
10273
  try {
11413
- const graphRef = readFileSync8(graphRefPath, "utf-8");
10274
+ const graphRef = readFileSync6(graphRefPath, "utf-8");
11414
10275
  baseSystemPrompt += `
11415
10276
 
11416
10277
  ${graphRef}`;
@@ -11496,7 +10357,7 @@ Current session key: ${sessionKey}` : systemPromptBase;
11496
10357
 
11497
10358
  ${gwParts.join("\n")}`;
11498
10359
  }
11499
- if (sessionKey) {
10360
+ if (sessionKey && isDmChannelSessionKey(sessionKey)) {
11500
10361
  try {
11501
10362
  await ensureConversation(accountId, agentType, sessionKey, void 0, void 0, sessionUserId);
11502
10363
  } catch (err) {
@@ -11625,8 +10486,8 @@ var clientIpMiddleware = async (c, next) => {
11625
10486
  };
11626
10487
 
11627
10488
  // server/routes/health.ts
11628
- import { existsSync as existsSync12, readFileSync as readFileSync13 } from "fs";
11629
- import { createConnection as createConnection4 } from "net";
10489
+ import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
10490
+ import { createConnection as createConnection2 } from "net";
11630
10491
 
11631
10492
  // app/lib/network.ts
11632
10493
  import { networkInterfaces } from "os";
@@ -11650,8 +10511,8 @@ function getLanIp() {
11650
10511
  import { basename as basename2 } from "path";
11651
10512
 
11652
10513
  // app/lib/review-detector/rules.ts
11653
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, existsSync as existsSync8, statSync as statSync4, mkdirSync as mkdirSync7, renameSync as renameSync2 } from "fs";
11654
- import { resolve as resolve9, dirname as dirname3 } from "path";
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";
11655
10516
  var DEFAULT_SCAN_INTERVAL_MS = 5e3;
11656
10517
  var RATE_LIMIT_PATTERN = "rate[- ]?limit(?:ed| reached| hit)|(?:HTTP|status)[^a-z]{0,3}429|too many requests";
11657
10518
  var RATE_LIMIT_PATTERN_V1 = "\\b429\\b|rate.?limit|too.?many.?requests";
@@ -12080,12 +10941,12 @@ function defaultRules() {
12080
10941
  ];
12081
10942
  }
12082
10943
  function rulesFilePath(configDir2) {
12083
- return resolve9(configDir2, "review-rules.json");
10944
+ return resolve6(configDir2, "review-rules.json");
12084
10945
  }
12085
10946
  function ensureRulesFile(configDir2) {
12086
10947
  const path2 = rulesFilePath(configDir2);
12087
- if (existsSync8(path2)) return { created: false, path: path2 };
12088
- mkdirSync7(dirname3(path2), { recursive: true });
10948
+ if (existsSync6(path2)) return { created: false, path: path2 };
10949
+ mkdirSync5(dirname3(path2), { recursive: true });
12089
10950
  const body = {
12090
10951
  scanIntervalMs: DEFAULT_SCAN_INTERVAL_MS,
12091
10952
  rules: defaultRules()
@@ -12095,10 +10956,10 @@ function ensureRulesFile(configDir2) {
12095
10956
  }
12096
10957
  function loadRules(configDir2) {
12097
10958
  const path2 = rulesFilePath(configDir2);
12098
- if (!existsSync8(path2)) {
10959
+ if (!existsSync6(path2)) {
12099
10960
  throw new Error(`rules file missing at ${path2}`);
12100
10961
  }
12101
- const raw2 = readFileSync9(path2, "utf-8");
10962
+ const raw2 = readFileSync7(path2, "utf-8");
12102
10963
  let parsed;
12103
10964
  try {
12104
10965
  parsed = JSON.parse(raw2);
@@ -12121,7 +10982,7 @@ function saveRules(configDir2, file) {
12121
10982
  }
12122
10983
  function atomicWriteJson(path2, body) {
12123
10984
  const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
12124
- writeFileSync7(tmp, JSON.stringify(body, null, 2) + "\n", "utf-8");
10985
+ writeFileSync6(tmp, JSON.stringify(body, null, 2) + "\n", "utf-8");
12125
10986
  renameSync2(tmp, path2);
12126
10987
  }
12127
10988
  function validateRulesFile(input, sourceLabel) {
@@ -12263,16 +11124,16 @@ function validateRule(input, label, seenIds) {
12263
11124
  }
12264
11125
 
12265
11126
  // app/lib/review-detector/sources.ts
12266
- import { existsSync as existsSync9, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync8, renameSync as renameSync3, mkdirSync as mkdirSync8, openSync as openSync3, readSync as readSync3, closeSync as closeSync3, readFileSync as readFileSync10 } from "fs";
12267
- import { resolve as resolve10, join as join5, basename, dirname as dirname4 } from "path";
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";
12268
11129
  function tailStatePath(configDir2) {
12269
- return resolve10(configDir2, "review-state.json");
11130
+ return resolve7(configDir2, "review-state.json");
12270
11131
  }
12271
11132
  function loadTailState(configDir2) {
12272
11133
  const path2 = tailStatePath(configDir2);
12273
- if (!existsSync9(path2)) return {};
11134
+ if (!existsSync7(path2)) return {};
12274
11135
  try {
12275
- const raw2 = readFileSync10(path2, "utf-8");
11136
+ const raw2 = readFileSync8(path2, "utf-8");
12276
11137
  const parsed = JSON.parse(raw2);
12277
11138
  if (!parsed || typeof parsed !== "object") return {};
12278
11139
  const clean = {};
@@ -12290,26 +11151,26 @@ function loadTailState(configDir2) {
12290
11151
  }
12291
11152
  function saveTailState(configDir2, state) {
12292
11153
  const path2 = tailStatePath(configDir2);
12293
- mkdirSync8(dirname4(path2), { recursive: true });
11154
+ mkdirSync6(dirname4(path2), { recursive: true });
12294
11155
  const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
12295
- writeFileSync8(tmp, JSON.stringify(state, null, 2) + "\n", "utf-8");
11156
+ writeFileSync7(tmp, JSON.stringify(state, null, 2) + "\n", "utf-8");
12296
11157
  renameSync3(tmp, path2);
12297
11158
  }
12298
11159
  function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
12299
11160
  if (logicalSource === "server") {
12300
- const p = resolve10(configDir2, "logs", "server.log");
12301
- return existsSync9(p) ? [{ logicalSource: "server", filepath: p }] : [];
11161
+ const p = resolve7(configDir2, "logs", "server.log");
11162
+ return existsSync7(p) ? [{ logicalSource: "server", filepath: p }] : [];
12302
11163
  }
12303
11164
  if (logicalSource === "vnc") {
12304
- const p = resolve10(configDir2, "logs", "vnc-boot.log");
12305
- return existsSync9(p) ? [{ logicalSource: "vnc", filepath: p }] : [];
11165
+ const p = resolve7(configDir2, "logs", "vnc-boot.log");
11166
+ return existsSync7(p) ? [{ logicalSource: "vnc", filepath: p }] : [];
12306
11167
  }
12307
11168
  if (logicalSource === "cloudflared") {
12308
11169
  const files2 = [];
12309
- const daemon = resolve10(configDir2, "logs", "cloudflared.log");
12310
- if (existsSync9(daemon)) files2.push({ logicalSource: "cloudflared", filepath: daemon });
12311
- const login = resolve10(configDir2, "logs", "cloudflared-login.log");
12312
- if (existsSync9(login)) files2.push({ logicalSource: "cloudflared", filepath: login });
11170
+ const daemon = resolve7(configDir2, "logs", "cloudflared.log");
11171
+ if (existsSync7(daemon)) files2.push({ logicalSource: "cloudflared", filepath: daemon });
11172
+ const login = resolve7(configDir2, "logs", "cloudflared-login.log");
11173
+ if (existsSync7(login)) files2.push({ logicalSource: "cloudflared", filepath: login });
12313
11174
  return files2;
12314
11175
  }
12315
11176
  const prefix = {
@@ -12319,7 +11180,7 @@ function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
12319
11180
  public: "public-agent-stream-",
12320
11181
  mcp: "mcp-"
12321
11182
  }[logicalSource];
12322
- if (!existsSync9(accountLogDir2)) return [];
11183
+ if (!existsSync7(accountLogDir2)) return [];
12323
11184
  const files = [];
12324
11185
  let scanned = 0;
12325
11186
  let skippedPrefixMismatch = 0;
@@ -12329,7 +11190,7 @@ function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
12329
11190
  const matchesPrefix = entry.startsWith(prefix);
12330
11191
  const isLog = entry.endsWith(".log");
12331
11192
  if (matchesPrefix && isLog) {
12332
- files.push({ logicalSource, filepath: join5(accountLogDir2, entry) });
11193
+ files.push({ logicalSource, filepath: join4(accountLogDir2, entry) });
12333
11194
  } else if (!matchesPrefix) {
12334
11195
  skippedPrefixMismatch += 1;
12335
11196
  } else {
@@ -12361,7 +11222,7 @@ function discoverAllSources(configDir2, accountLogDir2) {
12361
11222
  ];
12362
11223
  }
12363
11224
  function readNewLines(filepath, prev) {
12364
- if (!existsSync9(filepath)) return null;
11225
+ if (!existsSync7(filepath)) return null;
12365
11226
  const stat5 = statSync5(filepath);
12366
11227
  const size = stat5.size;
12367
11228
  const inode = stat5.ino;
@@ -12414,12 +11275,12 @@ function readNewLines(filepath, prev) {
12414
11275
  }
12415
11276
  }
12416
11277
  function countRecentWrites(dir, sinceMs) {
12417
- if (!existsSync9(dir)) return 0;
11278
+ if (!existsSync7(dir)) return 0;
12418
11279
  let count = 0;
12419
11280
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
12420
11281
  if (!entry.isFile()) continue;
12421
11282
  try {
12422
- const st = statSync5(join5(dir, entry.name));
11283
+ const st = statSync5(join4(dir, entry.name));
12423
11284
  if (st.mtimeMs >= sinceMs) count += 1;
12424
11285
  } catch {
12425
11286
  }
@@ -12427,7 +11288,7 @@ function countRecentWrites(dir, sinceMs) {
12427
11288
  return count;
12428
11289
  }
12429
11290
  function fileLastWriteMs(path2) {
12430
- if (!existsSync9(path2)) return null;
11291
+ if (!existsSync7(path2)) return null;
12431
11292
  try {
12432
11293
  return statSync5(path2).mtimeMs;
12433
11294
  } catch {
@@ -12435,31 +11296,31 @@ function fileLastWriteMs(path2) {
12435
11296
  }
12436
11297
  }
12437
11298
  function accountLogDir(accountDir) {
12438
- return resolve10(accountDir, "logs");
11299
+ return resolve7(accountDir, "logs");
12439
11300
  }
12440
11301
  function sourceKey(file) {
12441
11302
  return `${file.logicalSource}:${basename(file.filepath)}`;
12442
11303
  }
12443
11304
 
12444
11305
  // app/lib/review-detector/writer.ts
12445
- import { appendFileSync as appendFileSync4, existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync9, renameSync as renameSync4, statSync as statSync6 } from "fs";
12446
- import { resolve as resolve11, dirname as dirname5 } from "path";
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";
12447
11308
  import { randomUUID as randomUUID3 } from "crypto";
12448
11309
  function reviewLogPath(configDir2) {
12449
- return resolve11(configDir2, "logs", "review.log");
11310
+ return resolve8(configDir2, "logs", "review.log");
12450
11311
  }
12451
11312
  function pendingAlertsPath(configDir2) {
12452
- return resolve11(configDir2, "review-pending-alerts.jsonl");
11313
+ return resolve8(configDir2, "review-pending-alerts.jsonl");
12453
11314
  }
12454
11315
  function reviewLog(configDir2, event) {
12455
11316
  const path2 = reviewLogPath(configDir2);
12456
11317
  try {
12457
- mkdirSync9(dirname5(path2), { recursive: true });
11318
+ mkdirSync7(dirname5(path2), { recursive: true });
12458
11319
  const line = `${new Date(
12459
11320
  typeof event.ts === "number" ? event.ts : Date.now()
12460
11321
  ).toISOString()} [review] ${JSON.stringify(event)}
12461
11322
  `;
12462
- appendFileSync4(path2, line, "utf-8");
11323
+ appendFileSync2(path2, line, "utf-8");
12463
11324
  } catch (err) {
12464
11325
  console.error(`[review] failed to write review log at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
12465
11326
  }
@@ -12571,17 +11432,17 @@ async function upsertReviewAlert(accountId, match2) {
12571
11432
  function queueAlert(configDir2, accountId, match2) {
12572
11433
  const path2 = pendingAlertsPath(configDir2);
12573
11434
  try {
12574
- mkdirSync9(dirname5(path2), { recursive: true });
11435
+ mkdirSync7(dirname5(path2), { recursive: true });
12575
11436
  const line = JSON.stringify({ accountId, match: match2 }) + "\n";
12576
- appendFileSync4(path2, line, "utf-8");
11437
+ appendFileSync2(path2, line, "utf-8");
12577
11438
  } catch (err) {
12578
11439
  console.error(`[review] failed to queue alert at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
12579
11440
  }
12580
11441
  }
12581
11442
  async function drainPendingAlerts(configDir2) {
12582
11443
  const path2 = pendingAlertsPath(configDir2);
12583
- if (!existsSync10(path2)) return { drained: 0, remaining: 0 };
12584
- const raw2 = readFileSync11(path2, "utf-8");
11444
+ if (!existsSync8(path2)) return { drained: 0, remaining: 0 };
11445
+ const raw2 = readFileSync9(path2, "utf-8");
12585
11446
  const lines = raw2.split("\n").filter((l) => l.trim().length > 0);
12586
11447
  if (lines.length === 0) return { drained: 0, remaining: 0 };
12587
11448
  const remaining = [];
@@ -12602,9 +11463,9 @@ async function drainPendingAlerts(configDir2) {
12602
11463
  }
12603
11464
  const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
12604
11465
  if (remaining.length > 0) {
12605
- writeFileSync9(tmp, remaining.join("\n") + "\n", "utf-8");
11466
+ writeFileSync8(tmp, remaining.join("\n") + "\n", "utf-8");
12606
11467
  } else {
12607
- writeFileSync9(tmp, "", "utf-8");
11468
+ writeFileSync8(tmp, "", "utf-8");
12608
11469
  }
12609
11470
  renameSync4(tmp, path2);
12610
11471
  return { drained, remaining: remaining.length };
@@ -12704,7 +11565,7 @@ async function bootDetector() {
12704
11565
  }
12705
11566
 
12706
11567
  // app/lib/review-detector/scan-loop.ts
12707
- import { resolve as resolve12 } from "path";
11568
+ import { resolve as resolve9 } from "path";
12708
11569
 
12709
11570
  // app/lib/review-detector/evaluator.ts
12710
11571
  var SAMPLE_MAX_CHARS = 500;
@@ -12998,14 +11859,14 @@ async function runScanCycle(runtime) {
12998
11859
  match2 = result.match;
12999
11860
  }
13000
11861
  } else if (rule.type === "file-write-storm") {
13001
- const dir = resolve12(runtime.configDir, rule.watchPath ?? "");
11862
+ const dir = resolve9(runtime.configDir, rule.watchPath ?? "");
13002
11863
  const sinceMs = cycleStart - rule.thresholdWindowMinutes * 6e4;
13003
11864
  const count = countRecentWrites(dir, sinceMs);
13004
11865
  const result = evaluateFileWriteStormRule(rule, count, state, cycleStart);
13005
11866
  state = result.state;
13006
11867
  match2 = result.match;
13007
11868
  } else if (rule.type === "stale-log") {
13008
- const trackedPath = resolve12(runtime.configDir, rule.watchPath ?? "");
11869
+ const trackedPath = resolve9(runtime.configDir, rule.watchPath ?? "");
13009
11870
  const lastMs = fileLastWriteMs(trackedPath);
13010
11871
  const result = evaluateStaleLogRule(rule, lastMs, state, cycleStart);
13011
11872
  state = result.state;
@@ -13244,20 +12105,20 @@ var WhatsAppConfigSchema = z.object({
13244
12105
  });
13245
12106
 
13246
12107
  // app/lib/whatsapp/config-persist.ts
13247
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync10, existsSync as existsSync11 } from "fs";
13248
- import { resolve as resolve13, join as join6 } from "path";
12108
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "fs";
12109
+ import { resolve as resolve10, join as join5 } from "path";
13249
12110
  var TAG3 = "[whatsapp:config]";
13250
12111
  function configPath(accountDir) {
13251
- return resolve13(accountDir, "account.json");
12112
+ return resolve10(accountDir, "account.json");
13252
12113
  }
13253
12114
  function readConfig(accountDir) {
13254
12115
  const path2 = configPath(accountDir);
13255
- if (!existsSync11(path2)) throw new Error(`account.json not found at ${path2}`);
13256
- return JSON.parse(readFileSync12(path2, "utf-8"));
12116
+ if (!existsSync9(path2)) throw new Error(`account.json not found at ${path2}`);
12117
+ return JSON.parse(readFileSync10(path2, "utf-8"));
13257
12118
  }
13258
12119
  function writeConfig(accountDir, config) {
13259
12120
  const path2 = configPath(accountDir);
13260
- writeFileSync10(path2, JSON.stringify(config, null, 2) + "\n", "utf-8");
12121
+ writeFileSync9(path2, JSON.stringify(config, null, 2) + "\n", "utf-8");
13261
12122
  }
13262
12123
  function reloadManagerConfig(accountDir) {
13263
12124
  try {
@@ -13427,8 +12288,8 @@ function setPublicAgent(accountDir, slug) {
13427
12288
  if (!trimmed) {
13428
12289
  return { ok: false, error: "Agent slug cannot be empty." };
13429
12290
  }
13430
- const agentConfigPath = join6(accountDir, "agents", trimmed, "config.json");
13431
- if (!existsSync11(agentConfigPath)) {
12291
+ const agentConfigPath = join5(accountDir, "agents", trimmed, "config.json");
12292
+ if (!existsSync9(agentConfigPath)) {
13432
12293
  return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
13433
12294
  }
13434
12295
  try {
@@ -13703,7 +12564,7 @@ var credsSaveQueue = Promise.resolve();
13703
12564
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
13704
12565
  console.error(`${TAG5} draining credential save queue\u2026`);
13705
12566
  const timer = new Promise(
13706
- (resolve31) => setTimeout(() => resolve31("timeout"), timeoutMs)
12567
+ (resolve28) => setTimeout(() => resolve28("timeout"), timeoutMs)
13707
12568
  );
13708
12569
  const result = await Promise.race([
13709
12570
  credsSaveQueue.then(() => "drained"),
@@ -13831,11 +12692,11 @@ async function createWaSocket(opts) {
13831
12692
  return sock;
13832
12693
  }
13833
12694
  async function waitForConnection(sock) {
13834
- return new Promise((resolve31, reject) => {
12695
+ return new Promise((resolve28, reject) => {
13835
12696
  const handler = (update) => {
13836
12697
  if (update.connection === "open") {
13837
12698
  sock.ev.off("connection.update", handler);
13838
- resolve31();
12699
+ resolve28();
13839
12700
  }
13840
12701
  if (update.connection === "close") {
13841
12702
  sock.ev.off("connection.update", handler);
@@ -13949,14 +12810,14 @@ ${inspected}`;
13949
12810
  return inspect2(err, INSPECT_OPTS2);
13950
12811
  }
13951
12812
  function withTimeout(label, promise, timeoutMs) {
13952
- return new Promise((resolve31, reject) => {
12813
+ return new Promise((resolve28, reject) => {
13953
12814
  const timer = setTimeout(() => {
13954
12815
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
13955
12816
  }, timeoutMs);
13956
12817
  promise.then(
13957
12818
  (value) => {
13958
12819
  clearTimeout(timer);
13959
- resolve31(value);
12820
+ resolve28(value);
13960
12821
  },
13961
12822
  (err) => {
13962
12823
  clearTimeout(timer);
@@ -14468,7 +13329,7 @@ async function sendMediaMessage(sock, to, media, opts) {
14468
13329
  // app/lib/whatsapp/inbound/media.ts
14469
13330
  import { randomUUID as randomUUID5 } from "crypto";
14470
13331
  import { writeFile, mkdir } from "fs/promises";
14471
- import { join as join7 } from "path";
13332
+ import { join as join6 } from "path";
14472
13333
  import {
14473
13334
  downloadMediaMessage,
14474
13335
  downloadContentFromMessage,
@@ -14554,7 +13415,7 @@ async function downloadInboundMedia(msg, sock, opts) {
14554
13415
  await mkdir(MEDIA_DIR, { recursive: true });
14555
13416
  const ext = mimeToExt(mimetype ?? "application/octet-stream");
14556
13417
  const filename = `${randomUUID5()}.${ext}`;
14557
- const filePath = join7(MEDIA_DIR, filename);
13418
+ const filePath = join6(MEDIA_DIR, filename);
14558
13419
  await writeFile(filePath, buffer);
14559
13420
  const sizeKB = (buffer.length / 1024).toFixed(0);
14560
13421
  console.error(`${TAG9} media downloaded type=${mimetype ?? "unknown"} size=${sizeKB}KB path=${filePath}`);
@@ -14898,6 +13759,14 @@ function deriveSessionKey(input) {
14898
13759
  if (input.isOwnerMirror || input.agentType === "admin") {
14899
13760
  return `whatsapp:${input.accountId}`;
14900
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
+ }
14901
13770
  return `whatsapp:${input.accountId}:${input.senderPhone}`;
14902
13771
  }
14903
13772
  async function init(opts) {
@@ -15162,11 +14031,11 @@ async function connectWithReconnect(conn) {
15162
14031
  console.error(
15163
14032
  `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
15164
14033
  );
15165
- await new Promise((resolve31) => {
15166
- const timer = setTimeout(resolve31, delay);
14034
+ await new Promise((resolve28) => {
14035
+ const timer = setTimeout(resolve28, delay);
15167
14036
  conn.abortController.signal.addEventListener("abort", () => {
15168
14037
  clearTimeout(timer);
15169
- resolve31();
14038
+ resolve28();
15170
14039
  }, { once: true });
15171
14040
  });
15172
14041
  }
@@ -15174,16 +14043,16 @@ async function connectWithReconnect(conn) {
15174
14043
  }
15175
14044
  }
15176
14045
  function waitForDisconnectEvent(conn) {
15177
- return new Promise((resolve31) => {
14046
+ return new Promise((resolve28) => {
15178
14047
  if (!conn.sock) {
15179
- resolve31();
14048
+ resolve28();
15180
14049
  return;
15181
14050
  }
15182
14051
  const sock = conn.sock;
15183
14052
  const handler = (update) => {
15184
14053
  if (update.connection === "close") {
15185
14054
  sock.ev.off("connection.update", handler);
15186
- resolve31();
14055
+ resolve28();
15187
14056
  }
15188
14057
  };
15189
14058
  sock.ev.on("connection.update", handler);
@@ -15369,6 +14238,8 @@ async function handleInboundMessage(conn, msg) {
15369
14238
  agentType: "admin",
15370
14239
  accountId: conn.accountId,
15371
14240
  senderPhone: senderPhone2,
14241
+ isGroup: isGroup2,
14242
+ groupJid: isGroup2 ? remoteJid : void 0,
15372
14243
  isOwnerMirror: true
15373
14244
  }),
15374
14245
  isOwnerMirror: true
@@ -15398,8 +14269,8 @@ async function handleInboundMessage(conn, msg) {
15398
14269
  const conversationKey = isGroup ? remoteJid : senderPhone;
15399
14270
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
15400
14271
  let resolvePending;
15401
- const sttPending = new Promise((resolve31) => {
15402
- resolvePending = resolve31;
14272
+ const sttPending = new Promise((resolve28) => {
14273
+ resolvePending = resolve28;
15403
14274
  });
15404
14275
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
15405
14276
  try {
@@ -15452,7 +14323,9 @@ async function handleInboundMessage(conn, msg) {
15452
14323
  const sessionKey = deriveSessionKey({
15453
14324
  agentType: accessResult.agentType,
15454
14325
  accountId: conn.accountId,
15455
- senderPhone
14326
+ senderPhone,
14327
+ isGroup,
14328
+ groupJid: isGroup ? remoteJid : void 0
15456
14329
  });
15457
14330
  conn.lastMessageAt = Date.now();
15458
14331
  const payload = {
@@ -15510,44 +14383,30 @@ async function probeApiKey() {
15510
14383
  return result.status;
15511
14384
  }
15512
14385
  function checkPort(port2, timeoutMs = 500) {
15513
- return new Promise((resolve31) => {
15514
- const socket = createConnection4(port2, "127.0.0.1");
14386
+ return new Promise((resolve28) => {
14387
+ const socket = createConnection2(port2, "127.0.0.1");
15515
14388
  socket.setTimeout(timeoutMs);
15516
14389
  socket.once("connect", () => {
15517
14390
  socket.destroy();
15518
- resolve31(true);
14391
+ resolve28(true);
15519
14392
  });
15520
14393
  socket.once("error", () => {
15521
14394
  socket.destroy();
15522
- resolve31(false);
14395
+ resolve28(false);
15523
14396
  });
15524
14397
  socket.once("timeout", () => {
15525
14398
  socket.destroy();
15526
- resolve31(false);
14399
+ resolve28(false);
15527
14400
  });
15528
14401
  });
15529
14402
  }
15530
- var TTYD_PROBE_CACHE_MS = 2e3;
15531
- var TTYD_PROBE_TIMEOUT_MS = 200;
15532
- var cachedTtydReady = null;
15533
- async function probeTerminalReady() {
15534
- if (cachedTtydReady && Date.now() - cachedTtydReady.checkedAt < TTYD_PROBE_CACHE_MS) {
15535
- return cachedTtydReady.ok;
15536
- }
15537
- const ok = await checkPort(7681, TTYD_PROBE_TIMEOUT_MS);
15538
- if (!cachedTtydReady || cachedTtydReady.ok !== ok) {
15539
- console.log(`[health] terminal_ready: ${cachedTtydReady?.ok ?? "unknown"} \u2192 ${ok}`);
15540
- }
15541
- cachedTtydReady = { ok, checkedAt: Date.now() };
15542
- return ok;
15543
- }
15544
14403
  var app = new Hono2();
15545
14404
  app.get("/", async (c) => {
15546
14405
  const browserTransport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
15547
14406
  let pinConfigured = false;
15548
14407
  try {
15549
- if (existsSync12(USERS_FILE)) {
15550
- const raw2 = readFileSync13(USERS_FILE, "utf-8").trim();
14408
+ if (existsSync10(USERS_FILE)) {
14409
+ const raw2 = readFileSync11(USERS_FILE, "utf-8").trim();
15551
14410
  if (raw2) {
15552
14411
  const users = JSON.parse(raw2);
15553
14412
  pinConfigured = Array.isArray(users) && users.length > 0;
@@ -15564,10 +14423,9 @@ app.get("/", async (c) => {
15564
14423
  }
15565
14424
  const claudeAuthenticated = authHealth.status === "ok" || authHealth.status === "expiring";
15566
14425
  const vncRunning = await checkPort(6080);
15567
- const terminalReady = await probeTerminalReady();
15568
14426
  let apiKeyConfigured = false;
15569
14427
  try {
15570
- apiKeyConfigured = existsSync12(keyFilePath());
14428
+ apiKeyConfigured = existsSync10(keyFilePath());
15571
14429
  } catch {
15572
14430
  }
15573
14431
  let apiKeyStatus = "missing";
@@ -15608,7 +14466,6 @@ app.get("/", async (c) => {
15608
14466
  claude_authenticated: claudeAuthenticated,
15609
14467
  ...onboardingComplete !== void 0 && { onboarding_complete: onboardingComplete },
15610
14468
  vnc_running: vncRunning,
15611
- terminal_ready: terminalReady,
15612
14469
  browser_transport: browserTransport,
15613
14470
  auth_status: authHealth.status,
15614
14471
  auth_expires_at: authHealth.expiresAt ?? null,
@@ -15636,14 +14493,14 @@ app.get("/", async (c) => {
15636
14493
  var health_default = app;
15637
14494
 
15638
14495
  // server/routes/session.ts
15639
- import { resolve as resolve14 } from "path";
15640
- import { existsSync as existsSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10 } from "fs";
14496
+ import { resolve as resolve11 } from "path";
14497
+ import { existsSync as existsSync11, writeFileSync as writeFileSync10, mkdirSync as mkdirSync8 } from "fs";
15641
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;
15642
14499
  function writeBrandingCache(accountId, agentSlug, branding) {
15643
14500
  try {
15644
- const cacheDir = resolve14(MAXY_DIR, "branding-cache", accountId);
15645
- mkdirSync10(cacheDir, { recursive: true });
15646
- writeFileSync11(resolve14(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
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");
15647
14504
  } catch (err) {
15648
14505
  console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
15649
14506
  }
@@ -15713,9 +14570,9 @@ app2.post("/", async (c) => {
15713
14570
  }
15714
14571
  let agentConfig = null;
15715
14572
  if (account) {
15716
- const agentDir = resolve14(account.accountDir, "agents", agentSlug);
15717
- const agentConfigPath = resolve14(agentDir, "config.json");
15718
- if (!existsSync13(agentDir) || !existsSync13(agentConfigPath)) {
14573
+ const agentDir = resolve11(account.accountDir, "agents", agentSlug);
14574
+ const agentConfigPath = resolve11(agentDir, "config.json");
14575
+ if (!existsSync11(agentDir) || !existsSync11(agentConfigPath)) {
15719
14576
  return c.json({ error: "Agent not found" }, 404);
15720
14577
  }
15721
14578
  agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
@@ -15905,8 +14762,6 @@ app2.post("/", async (c) => {
15905
14762
  const newVisitorId = visitorId ?? crypto.randomUUID();
15906
14763
  const sessionKey = crypto.randomUUID();
15907
14764
  registerSession(sessionKey, "public", accountId, agentSlug);
15908
- ensureConversation(accountId, "public", sessionKey, newVisitorId, agentSlug).catch(() => {
15909
- });
15910
14765
  const hasImage = agentConfig?.image ? "yes" : "no";
15911
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}`);
15912
14767
  return withVisitorCookie(
@@ -15961,9 +14816,9 @@ ${raw2}`;
15961
14816
  import { randomUUID as randomUUID6 } from "crypto";
15962
14817
  import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
15963
14818
  import { realpathSync } from "fs";
15964
- import { resolve as resolve15, extname, basename as basename3 } from "path";
15965
- var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve15(process.cwd(), "../platform");
15966
- var ATTACHMENTS_ROOT = resolve15(PLATFORM_ROOT5, "..", "data/uploads");
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");
15967
14822
  var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
15968
14823
  "image/jpeg",
15969
14824
  "image/png",
@@ -15987,11 +14842,11 @@ function assertSupportedMime(mimeType) {
15987
14842
  }
15988
14843
  async function writeAttachment(scope, filename, mimeType, sizeBytes, buffer) {
15989
14844
  const attachmentId = randomUUID6();
15990
- const dir = resolve15(ATTACHMENTS_ROOT, scope, attachmentId);
14845
+ const dir = resolve12(ATTACHMENTS_ROOT, scope, attachmentId);
15991
14846
  await mkdir2(dir, { recursive: true });
15992
14847
  const ext = extname(filename) || "";
15993
- const storagePath = resolve15(dir, `${attachmentId}${ext}`);
15994
- const metaPath = resolve15(dir, `${attachmentId}.meta.json`);
14848
+ const storagePath = resolve12(dir, `${attachmentId}${ext}`);
14849
+ const metaPath = resolve12(dir, `${attachmentId}.meta.json`);
15995
14850
  const meta = {
15996
14851
  attachmentId,
15997
14852
  scope,
@@ -16066,7 +14921,7 @@ async function storeGeneratedFile(accountId, filePath) {
16066
14921
  // app/lib/stt/voice-note.ts
16067
14922
  import { writeFile as writeFile3, mkdtemp, rm } from "fs/promises";
16068
14923
  import { tmpdir } from "os";
16069
- import { join as join8 } from "path";
14924
+ import { join as join7 } from "path";
16070
14925
  var TAG14 = "[voice]";
16071
14926
  var AUDIO_MIME_TYPES = /* @__PURE__ */ new Set([
16072
14927
  "audio/ogg",
@@ -16104,9 +14959,9 @@ async function transcribeVoiceNote(file, source) {
16104
14959
  let tempDir;
16105
14960
  let tempPath;
16106
14961
  try {
16107
- tempDir = await mkdtemp(join8(tmpdir(), "voice-"));
14962
+ tempDir = await mkdtemp(join7(tmpdir(), "voice-"));
16108
14963
  const ext = audioExtension(mimeType);
16109
- tempPath = join8(tempDir, `recording${ext}`);
14964
+ tempPath = join7(tempDir, `recording${ext}`);
16110
14965
  const buffer = Buffer.from(await file.arrayBuffer());
16111
14966
  await writeFile3(tempPath, buffer);
16112
14967
  } catch (err) {
@@ -16668,16 +15523,16 @@ var group_default = app4;
16668
15523
 
16669
15524
  // app/lib/access-gate.ts
16670
15525
  import neo4j2 from "neo4j-driver";
16671
- import { readFileSync as readFileSync14 } from "fs";
16672
- import { resolve as resolve16 } from "path";
15526
+ import { readFileSync as readFileSync12 } from "fs";
15527
+ import { resolve as resolve13 } from "path";
16673
15528
  import { randomUUID as randomUUID7, randomInt } from "crypto";
16674
- var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
15529
+ var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
16675
15530
  var driver2 = null;
16676
15531
  function readPassword2() {
16677
15532
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
16678
- const passwordFile = resolve16(PLATFORM_ROOT6, "config/.neo4j-password");
15533
+ const passwordFile = resolve13(PLATFORM_ROOT5, "config/.neo4j-password");
16679
15534
  try {
16680
- return readFileSync14(passwordFile, "utf-8").trim();
15535
+ return readFileSync12(passwordFile, "utf-8").trim();
16681
15536
  } catch {
16682
15537
  throw new Error(
16683
15538
  `Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
@@ -16710,13 +15565,13 @@ process.on("SIGINT", async () => {
16710
15565
  driver2 = null;
16711
15566
  }
16712
15567
  });
16713
- var rateLimitMap2 = /* @__PURE__ */ new Map();
15568
+ var rateLimitMap = /* @__PURE__ */ new Map();
16714
15569
  var ACCESS_MAX_ATTEMPTS = 5;
16715
15570
  var ACCESS_LOCKOUT_MS = 15 * 60 * 1e3;
16716
15571
  var ACCESS_WINDOW_MS = 15 * 60 * 1e3;
16717
15572
  function checkAccessRateLimit(ip, agentSlug) {
16718
15573
  const key = `${ip}:${agentSlug}`;
16719
- const entry = rateLimitMap2.get(key);
15574
+ const entry = rateLimitMap.get(key);
16720
15575
  if (!entry) return null;
16721
15576
  const now = Date.now();
16722
15577
  if (entry.lockedUntil && now < entry.lockedUntil) {
@@ -16725,7 +15580,7 @@ function checkAccessRateLimit(ip, agentSlug) {
16725
15580
  return `Too many attempts. Try again in ${remainingS}s`;
16726
15581
  }
16727
15582
  if (now - entry.firstAttempt > ACCESS_WINDOW_MS) {
16728
- rateLimitMap2.delete(key);
15583
+ rateLimitMap.delete(key);
16729
15584
  return null;
16730
15585
  }
16731
15586
  return null;
@@ -16733,9 +15588,9 @@ function checkAccessRateLimit(ip, agentSlug) {
16733
15588
  function recordAccessFailedAttempt(ip, agentSlug) {
16734
15589
  const key = `${ip}:${agentSlug}`;
16735
15590
  const now = Date.now();
16736
- const entry = rateLimitMap2.get(key);
15591
+ const entry = rateLimitMap.get(key);
16737
15592
  if (!entry || now - entry.firstAttempt > ACCESS_WINDOW_MS) {
16738
- rateLimitMap2.set(key, { attempts: 1, firstAttempt: now });
15593
+ rateLimitMap.set(key, { attempts: 1, firstAttempt: now });
16739
15594
  return;
16740
15595
  }
16741
15596
  entry.attempts++;
@@ -16744,7 +15599,7 @@ function recordAccessFailedAttempt(ip, agentSlug) {
16744
15599
  }
16745
15600
  }
16746
15601
  function clearAccessRateLimit(ip, agentSlug) {
16747
- rateLimitMap2.delete(`${ip}:${agentSlug}`);
15602
+ rateLimitMap.delete(`${ip}:${agentSlug}`);
16748
15603
  }
16749
15604
  var forgotPwdMap = /* @__PURE__ */ new Map();
16750
15605
  var FORGOT_PWD_MAX = 3;
@@ -16988,19 +15843,19 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
16988
15843
  }
16989
15844
 
16990
15845
  // app/lib/brevo-sms.ts
16991
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, mkdirSync as mkdirSync11, existsSync as existsSync14, chmodSync } from "fs";
15846
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, existsSync as existsSync12, chmodSync } from "fs";
16992
15847
  import { dirname as dirname6 } from "path";
16993
- import { resolve as resolve17 } from "path";
16994
- var BREVO_API_KEY_FILE = resolve17(MAXY_DIR, ".brevo-api-key");
15848
+ import { resolve as resolve14 } from "path";
15849
+ var BREVO_API_KEY_FILE = resolve14(MAXY_DIR, ".brevo-api-key");
16995
15850
  var BREVO_API_URL = "https://api.brevo.com/v3/transactionalSMS/sms";
16996
15851
  var BREVO_TIMEOUT_MS = 1e4;
16997
15852
  var BREVO_SENDER = "Maxy";
16998
- var platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
16999
- if (platformRoot2) {
15853
+ var platformRoot = process.env.MAXY_PLATFORM_ROOT;
15854
+ if (platformRoot) {
17000
15855
  try {
17001
- const brandPath = resolve17(platformRoot2, "config", "brand.json");
17002
- if (existsSync14(brandPath)) {
17003
- const brand = JSON.parse(readFileSync15(brandPath, "utf-8"));
15856
+ const brandPath = resolve14(platformRoot, "config", "brand.json");
15857
+ if (existsSync12(brandPath)) {
15858
+ const brand = JSON.parse(readFileSync13(brandPath, "utf-8"));
17004
15859
  if (brand.productName) BREVO_SENDER = brand.productName;
17005
15860
  }
17006
15861
  } catch {
@@ -17008,7 +15863,7 @@ if (platformRoot2) {
17008
15863
  }
17009
15864
  function readBrevoApiKey() {
17010
15865
  try {
17011
- const key = readFileSync15(BREVO_API_KEY_FILE, "utf-8").trim();
15866
+ const key = readFileSync13(BREVO_API_KEY_FILE, "utf-8").trim();
17012
15867
  if (!key) {
17013
15868
  throw new Error(`Brevo API key file is empty: ${BREVO_API_KEY_FILE}`);
17014
15869
  }
@@ -17023,7 +15878,7 @@ function readBrevoApiKey() {
17023
15878
  }
17024
15879
  }
17025
15880
  function hasBrevoApiKey() {
17026
- return existsSync14(BREVO_API_KEY_FILE);
15881
+ return existsSync12(BREVO_API_KEY_FILE);
17027
15882
  }
17028
15883
  async function sendSms(recipient, content, opts) {
17029
15884
  let apiKey;
@@ -17395,8 +16250,8 @@ app5.post("/forgot-password", async (c) => {
17395
16250
  app5.post("/send-otp", async (c) => {
17396
16251
  const socketAddr = c.env?.incoming?.socket?.remoteAddress;
17397
16252
  const hasXff = !!c.req.header("x-forwarded-for");
17398
- const isLoopback2 = socketAddr === "127.0.0.1" || socketAddr === "::1" || socketAddr === "::ffff:127.0.0.1";
17399
- if (!isLoopback2 || hasXff) {
16253
+ const isLoopback = socketAddr === "127.0.0.1" || socketAddr === "::1" || socketAddr === "::ffff:127.0.0.1";
16254
+ if (!isLoopback || hasXff) {
17400
16255
  console.error(`[access-gate] send-otp rejected: socket=${socketAddr ?? "unknown"} xff=${hasXff}`);
17401
16256
  return c.json({ error: "Forbidden" }, 403);
17402
16257
  }
@@ -17439,8 +16294,8 @@ app5.post("/send-otp", async (c) => {
17439
16294
  var access_default = app5;
17440
16295
 
17441
16296
  // server/routes/telegram.ts
17442
- import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
17443
- import { timingSafeEqual as timingSafeEqual2 } from "crypto";
16297
+ import { existsSync as existsSync13, readFileSync as readFileSync14 } from "fs";
16298
+ import { timingSafeEqual } from "crypto";
17444
16299
 
17445
16300
  // app/lib/telegram/access-control.ts
17446
16301
  function checkTelegramAccess(params) {
@@ -17476,8 +16331,8 @@ var TELEGRAM_API = "https://api.telegram.org";
17476
16331
  function getWebhookSecret(botType) {
17477
16332
  const filePath = botType === "admin" ? TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE : TELEGRAM_WEBHOOK_SECRET_FILE;
17478
16333
  try {
17479
- if (!existsSync15(filePath)) return null;
17480
- const secret = readFileSync16(filePath, "utf-8").trim();
16334
+ if (!existsSync13(filePath)) return null;
16335
+ const secret = readFileSync14(filePath, "utf-8").trim();
17481
16336
  return secret || null;
17482
16337
  } catch {
17483
16338
  return null;
@@ -17487,7 +16342,7 @@ function verifyWebhookSecret(headerValue, storedSecret) {
17487
16342
  const a = Buffer.from(headerValue);
17488
16343
  const b = Buffer.from(storedSecret);
17489
16344
  if (a.length !== b.length) return false;
17490
- return timingSafeEqual2(a, b);
16345
+ return timingSafeEqual(a, b);
17491
16346
  }
17492
16347
  async function handleInbound(params) {
17493
16348
  const { chatId, senderId, text, botType, botToken, accountId, agentType } = params;
@@ -17635,9 +16490,9 @@ app6.post("/webhook", async (c) => {
17635
16490
  var telegram_default = app6;
17636
16491
 
17637
16492
  // server/routes/whatsapp.ts
17638
- import { join as join9, resolve as resolve18, basename as basename4 } from "path";
16493
+ import { join as join8, resolve as resolve15, basename as basename4 } from "path";
17639
16494
  import { readFile as readFile2, stat as stat3 } from "fs/promises";
17640
- import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync17, existsSync as existsSync16 } from "fs";
16495
+ import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
17641
16496
 
17642
16497
  // app/lib/whatsapp/login.ts
17643
16498
  import { randomUUID as randomUUID8 } from "crypto";
@@ -17743,8 +16598,8 @@ async function startLogin(opts) {
17743
16598
  resetActiveLogin(accountId);
17744
16599
  let resolveQr = null;
17745
16600
  let rejectQr = null;
17746
- const qrPromise = new Promise((resolve31, reject) => {
17747
- resolveQr = resolve31;
16601
+ const qrPromise = new Promise((resolve28, reject) => {
16602
+ resolveQr = resolve28;
17748
16603
  rejectQr = reject;
17749
16604
  });
17750
16605
  const qrTimer = setTimeout(
@@ -17960,7 +16815,7 @@ function serializeWhatsAppSchema() {
17960
16815
 
17961
16816
  // server/routes/whatsapp.ts
17962
16817
  var TAG18 = "[whatsapp:api]";
17963
- var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
16818
+ var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT || "";
17964
16819
  var app7 = new Hono2();
17965
16820
  app7.get("/status", (c) => {
17966
16821
  try {
@@ -17978,7 +16833,7 @@ app7.post("/login/start", async (c) => {
17978
16833
  const body = await c.req.json().catch(() => ({}));
17979
16834
  const accountId = validateAccountId(body.accountId);
17980
16835
  const force = body.force ?? false;
17981
- const authDir = join9(MAXY_DIR, "credentials", "whatsapp", accountId);
16836
+ const authDir = join8(MAXY_DIR, "credentials", "whatsapp", accountId);
17982
16837
  const result = await startLogin({ accountId, authDir, force });
17983
16838
  console.error(`${TAG18} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
17984
16839
  return c.json(result);
@@ -18115,17 +16970,17 @@ app7.post("/config", async (c) => {
18115
16970
  return c.json({ ok: true, slug: currentSlug });
18116
16971
  }
18117
16972
  case "list-public-agents": {
18118
- const agentsDir = resolve18(account.accountDir, "agents");
16973
+ const agentsDir = resolve15(account.accountDir, "agents");
18119
16974
  const agents = [];
18120
- if (existsSync16(agentsDir)) {
16975
+ if (existsSync14(agentsDir)) {
18121
16976
  try {
18122
16977
  const entries = readdirSync4(agentsDir, { withFileTypes: true });
18123
16978
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
18124
16979
  if (!entry.isDirectory() || entry.name === "admin") continue;
18125
- const configPath2 = resolve18(agentsDir, entry.name, "config.json");
18126
- if (!existsSync16(configPath2)) continue;
16980
+ const configPath2 = resolve15(agentsDir, entry.name, "config.json");
16981
+ if (!existsSync14(configPath2)) continue;
18127
16982
  try {
18128
- const config = JSON.parse(readFileSync17(configPath2, "utf-8"));
16983
+ const config = JSON.parse(readFileSync15(configPath2, "utf-8"));
18129
16984
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
18130
16985
  } catch {
18131
16986
  console.error(`${TAG18} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
@@ -18197,10 +17052,10 @@ app7.post("/send-document", async (c) => {
18197
17052
  if (!to || !filePath) {
18198
17053
  return c.json({ error: "Missing required fields: to, filePath" }, 400);
18199
17054
  }
18200
- if (!maxyAccountId || !PLATFORM_ROOT7) {
17055
+ if (!maxyAccountId || !PLATFORM_ROOT6) {
18201
17056
  return c.json({ error: "Cannot validate file path: missing account or platform context" }, 400);
18202
17057
  }
18203
- const accountDir = resolve18(PLATFORM_ROOT7, "..", "data/accounts", maxyAccountId);
17058
+ const accountDir = resolve15(PLATFORM_ROOT6, "..", "data/accounts", maxyAccountId);
18204
17059
  let resolvedPath;
18205
17060
  try {
18206
17061
  resolvedPath = realpathSync2(filePath);
@@ -18337,16 +17192,16 @@ var whatsapp_default = app7;
18337
17192
 
18338
17193
  // server/routes/onboarding.ts
18339
17194
  import { spawn as spawn3, execFileSync as execFileSync2 } from "child_process";
18340
- import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync13, writeSync, existsSync as existsSync17, mkdirSync as mkdirSync12, readFileSync as readFileSync18, unlinkSync as unlinkSync4 } from "fs";
18341
- import { resolve as resolve19, dirname as dirname7 } from "path";
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";
18342
17197
  import { createHash, randomUUID as randomUUID9 } from "crypto";
18343
- var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
17198
+ var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
18344
17199
  function hashPin(pin) {
18345
17200
  return createHash("sha256").update(pin).digest("hex");
18346
17201
  }
18347
17202
  function readUsersFile() {
18348
- if (!existsSync17(USERS_FILE)) return null;
18349
- const raw2 = readFileSync18(USERS_FILE, "utf-8").trim();
17203
+ if (!existsSync15(USERS_FILE)) return null;
17204
+ const raw2 = readFileSync16(USERS_FILE, "utf-8").trim();
18350
17205
  if (!raw2) return [];
18351
17206
  return JSON.parse(raw2);
18352
17207
  }
@@ -18412,7 +17267,7 @@ app8.post("/claude-auth", async (c) => {
18412
17267
  if (!vncReady) return c.json({ error: "VNC display failed to start" }, 500);
18413
17268
  }
18414
17269
  await ensureCdp(transport);
18415
- writeFileSync13(logPath("claude-auth"), "");
17270
+ writeFileSync12(logPath("claude-auth"), "");
18416
17271
  const chromiumWrapper = writeChromiumWrapper();
18417
17272
  const x11Env = buildX11Env(chromiumWrapper, transport);
18418
17273
  vncLog("claude-auth", { action: "start", transport });
@@ -18451,17 +17306,17 @@ app8.post("/set-pin", async (c) => {
18451
17306
  }
18452
17307
  const hash = hashPin(body.pin);
18453
17308
  const userId = randomUUID9();
18454
- mkdirSync12(dirname7(USERS_FILE), { recursive: true });
18455
- writeFileSync13(USERS_FILE, JSON.stringify([{ userId, name: "Owner", pin: hash }]), { mode: 384 });
17309
+ mkdirSync10(dirname7(USERS_FILE), { recursive: true });
17310
+ writeFileSync12(USERS_FILE, JSON.stringify([{ userId, name: "Owner", pin: hash }]), { mode: 384 });
18456
17311
  console.log(`[set-pin] created users.json: userId=${userId.slice(0, 8)}\u2026 hash=${hash.slice(0, 8)}\u2026`);
18457
17312
  const account = resolveAccount();
18458
17313
  if (account) {
18459
17314
  try {
18460
- const config = JSON.parse(readFileSync18(`${account.accountDir}/account.json`, "utf-8"));
17315
+ const config = JSON.parse(readFileSync16(`${account.accountDir}/account.json`, "utf-8"));
18461
17316
  if (!config.admins) config.admins = [];
18462
17317
  if (!config.admins.some((a) => a.userId === userId)) {
18463
17318
  config.admins.push({ userId, role: "owner" });
18464
- writeFileSync13(`${account.accountDir}/account.json`, JSON.stringify(config, null, 2) + "\n");
17319
+ writeFileSync12(`${account.accountDir}/account.json`, JSON.stringify(config, null, 2) + "\n");
18465
17320
  console.log(`[set-pin] added userId=${userId.slice(0, 8)}\u2026 to account.json admins`);
18466
17321
  }
18467
17322
  } catch (err) {
@@ -18499,7 +17354,7 @@ app8.delete("/set-pin", async (c) => {
18499
17354
  unlinkSync4(USERS_FILE);
18500
17355
  console.log(`[set-pin] cleared users.json (last entry removed): userId=${matchedUser.userId.slice(0, 8)}\u2026`);
18501
17356
  } else {
18502
- writeFileSync13(USERS_FILE, JSON.stringify(remaining), { mode: 384 });
17357
+ writeFileSync12(USERS_FILE, JSON.stringify(remaining), { mode: 384 });
18503
17358
  console.log(`[set-pin] removed entry from users.json: userId=${matchedUser.userId.slice(0, 8)}\u2026 remaining=${remaining.length}`);
18504
17359
  }
18505
17360
  return c.json({ ok: true });
@@ -18512,19 +17367,19 @@ app8.post("/skip", async (c) => {
18512
17367
  }
18513
17368
  const { accountId, accountDir } = account;
18514
17369
  let agentName = "Maxy";
18515
- const brandPath = PLATFORM_ROOT8 ? resolve19(PLATFORM_ROOT8, "config", "brand.json") : "";
18516
- if (brandPath && existsSync17(brandPath)) {
17370
+ const brandPath = PLATFORM_ROOT7 ? resolve16(PLATFORM_ROOT7, "config", "brand.json") : "";
17371
+ if (brandPath && existsSync15(brandPath)) {
18517
17372
  try {
18518
- const brand = JSON.parse(readFileSync18(brandPath, "utf-8"));
17373
+ const brand = JSON.parse(readFileSync16(brandPath, "utf-8"));
18519
17374
  if (brand.productName) agentName = brand.productName;
18520
17375
  } catch (err) {
18521
17376
  console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
18522
17377
  }
18523
17378
  }
18524
- const soulPath = resolve19(accountDir, "agents", "admin", "SOUL.md");
17379
+ const soulPath = resolve16(accountDir, "agents", "admin", "SOUL.md");
18525
17380
  try {
18526
- mkdirSync12(dirname7(soulPath), { recursive: true });
18527
- writeFileSync13(soulPath, `You are ${agentName}, an AI operations manager.
17381
+ mkdirSync10(dirname7(soulPath), { recursive: true });
17382
+ writeFileSync12(soulPath, `You are ${agentName}, an AI operations manager.
18528
17383
  `);
18529
17384
  console.log(`[onboarding-skip] wrote SOUL.md: ${soulPath}`);
18530
17385
  } catch (err) {
@@ -18562,9 +17417,9 @@ app8.post("/skip", async (c) => {
18562
17417
  var onboarding_default = app8;
18563
17418
 
18564
17419
  // server/routes/client-error.ts
18565
- import { appendFileSync as appendFileSync5, existsSync as existsSync18, renameSync as renameSync5, statSync as statSync7 } from "fs";
18566
- import { join as join10 } from "path";
18567
- var CLIENT_ERRORS_LOG = join10(LOG_DIR, "client-errors.log");
17420
+ import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as renameSync5, statSync as statSync7 } from "fs";
17421
+ import { join as join9 } from "path";
17422
+ var CLIENT_ERRORS_LOG = join9(LOG_DIR, "client-errors.log");
18568
17423
  var MAX_LOG_SIZE = 10 * 1024 * 1024;
18569
17424
  var MAX_BODY_SIZE = 8 * 1024;
18570
17425
  var MAX_STACK_LEN = 2e3;
@@ -18616,7 +17471,7 @@ function stackHead(stack) {
18616
17471
  }
18617
17472
  function rotateIfNeeded() {
18618
17473
  try {
18619
- if (!existsSync18(CLIENT_ERRORS_LOG)) return;
17474
+ if (!existsSync16(CLIENT_ERRORS_LOG)) return;
18620
17475
  const stats = statSync7(CLIENT_ERRORS_LOG);
18621
17476
  if (stats.size < MAX_LOG_SIZE) return;
18622
17477
  renameSync5(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
@@ -18699,7 +17554,7 @@ app9.post("/", async (c) => {
18699
17554
  tag: typeof body.tag === "string" ? truncate2(body.tag, 32) : void 0,
18700
17555
  status: typeof body.status === "number" ? body.status : void 0
18701
17556
  };
18702
- appendFileSync5(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
17557
+ appendFileSync3(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
18703
17558
  } catch (err) {
18704
17559
  console.error(`[client-error] append failed: ${err instanceof Error ? err.message : String(err)}`);
18705
17560
  }
@@ -18709,14 +17564,14 @@ app9.post("/", async (c) => {
18709
17564
  var client_error_default = app9;
18710
17565
 
18711
17566
  // server/routes/admin/session.ts
18712
- import { readFileSync as readFileSync19, existsSync as existsSync19 } from "fs";
17567
+ import { readFileSync as readFileSync17, existsSync as existsSync17 } from "fs";
18713
17568
  import { createHash as createHash2 } from "crypto";
18714
17569
  function hashPin2(pin) {
18715
17570
  return createHash2("sha256").update(pin).digest("hex");
18716
17571
  }
18717
17572
  function readUsersFile2() {
18718
- if (!existsSync19(USERS_FILE)) return null;
18719
- const raw2 = readFileSync19(USERS_FILE, "utf-8").trim();
17573
+ if (!existsSync17(USERS_FILE)) return null;
17574
+ const raw2 = readFileSync17(USERS_FILE, "utf-8").trim();
18720
17575
  if (!raw2) return [];
18721
17576
  return JSON.parse(raw2);
18722
17577
  }
@@ -18738,11 +17593,7 @@ async function createAdminSession(accountId, thinkingView, userId, userName) {
18738
17593
  businessName = branding?.name || void 0;
18739
17594
  } catch {
18740
17595
  }
18741
- let conversationId = null;
18742
- if (userId) {
18743
- conversationId = await createNewAdminConversation(userId, accountId, sessionKey);
18744
- }
18745
- 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)}`);
18746
17597
  return {
18747
17598
  session_key: sessionKey,
18748
17599
  agent_id: "admin",
@@ -18751,7 +17602,7 @@ async function createAdminSession(accountId, thinkingView, userId, userName) {
18751
17602
  thinkingView: effectiveThinkingView,
18752
17603
  onboardingComplete,
18753
17604
  businessName,
18754
- conversationId
17605
+ conversationId: null
18755
17606
  };
18756
17607
  }
18757
17608
  var app10 = new Hono2();
@@ -18846,11 +17697,11 @@ app10.post("/", async (c) => {
18846
17697
  var session_default2 = app10;
18847
17698
 
18848
17699
  // server/routes/admin/chat.ts
18849
- import { resolve as resolve20 } from "path";
17700
+ import { resolve as resolve17 } from "path";
18850
17701
 
18851
17702
  // app/lib/script-stream-tailer.ts
18852
17703
  import * as childProcess from "child_process";
18853
- import { appendFileSync as appendFileSync6, createReadStream as createReadStream2, mkdirSync as mkdirSync13, statSync as statSync8 } from "fs";
17704
+ import { appendFileSync as appendFileSync4, createReadStream as createReadStream2, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
18854
17705
  import { dirname as dirname8 } from "path";
18855
17706
  import { StringDecoder as StringDecoder2 } from "string_decoder";
18856
17707
  var SCRIPT_STREAM_RE = /^\[([^\]]+)\] \[script:([a-z][a-z0-9-]*)((?::[a-z0-9:_-]+)?)\] (.*)$/;
@@ -18960,8 +17811,8 @@ function writeRouteMilestone(streamLogPath, scope, line) {
18960
17811
  }
18961
17812
  const ts = (/* @__PURE__ */ new Date()).toISOString();
18962
17813
  try {
18963
- mkdirSync13(dirname8(streamLogPath), { recursive: true });
18964
- appendFileSync6(streamLogPath, `[${ts}] [script:${scope}] ${line}
17814
+ mkdirSync11(dirname8(streamLogPath), { recursive: true });
17815
+ appendFileSync4(streamLogPath, `[${ts}] [script:${scope}] ${line}
18965
17816
  `);
18966
17817
  } catch (err) {
18967
17818
  console.error(
@@ -19291,7 +18142,7 @@ app11.post("/", requireAdminSession, async (c) => {
19291
18142
  try {
19292
18143
  registerAdminSSE(sseEntry);
19293
18144
  if (sseConvId) {
19294
- const streamLogPath = resolve20(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
18145
+ const streamLogPath = resolve17(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
19295
18146
  tailer = startScriptStreamTailer({
19296
18147
  path: streamLogPath,
19297
18148
  onEvent: (event) => {
@@ -19416,8 +18267,8 @@ app12.post("/", requireAdminSession, async (c) => {
19416
18267
  var compact_default = app12;
19417
18268
 
19418
18269
  // server/routes/admin/logs.ts
19419
- import { existsSync as existsSync20, readdirSync as readdirSync5, readFileSync as readFileSync20, statSync as statSync9 } from "fs";
19420
- import { resolve as resolve21, basename as basename5 } from "path";
18270
+ import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync18, statSync as statSync9 } from "fs";
18271
+ import { resolve as resolve18, basename as basename5 } from "path";
19421
18272
  var TAIL_BYTES = 8192;
19422
18273
  var app13 = new Hono2();
19423
18274
  app13.get("/", async (c) => {
@@ -19426,16 +18277,16 @@ app13.get("/", async (c) => {
19426
18277
  const conversationIdParam = c.req.query("conversationId");
19427
18278
  const download = c.req.query("download") === "1";
19428
18279
  const account = resolveAccount();
19429
- const accountLogDir2 = account ? resolve21(account.accountDir, "logs") : null;
18280
+ const accountLogDir2 = account ? resolve18(account.accountDir, "logs") : null;
19430
18281
  if (fileParam) {
19431
18282
  const safe = basename5(fileParam);
19432
18283
  const searched = [];
19433
18284
  for (const dir of [accountLogDir2, LOG_DIR]) {
19434
18285
  if (!dir) continue;
19435
- const filePath = resolve21(dir, safe);
18286
+ const filePath = resolve18(dir, safe);
19436
18287
  searched.push(filePath);
19437
18288
  try {
19438
- const content = readFileSync20(filePath, "utf-8");
18289
+ const content = readFileSync18(filePath, "utf-8");
19439
18290
  const headers = { "Content-Type": "text/plain; charset=utf-8" };
19440
18291
  if (download) headers["Content-Disposition"] = `attachment; filename="${safe}"`;
19441
18292
  return new Response(content, { headers });
@@ -19474,10 +18325,10 @@ app13.get("/", async (c) => {
19474
18325
  const searched = [];
19475
18326
  for (const dir of [accountLogDir2, LOG_DIR]) {
19476
18327
  if (!dir) continue;
19477
- const filePath = resolve21(dir, fileName);
18328
+ const filePath = resolve18(dir, fileName);
19478
18329
  searched.push(filePath);
19479
18330
  try {
19480
- const content = readFileSync20(filePath, "utf-8");
18331
+ const content = readFileSync18(filePath, "utf-8");
19481
18332
  const headers = { "Content-Type": "text/plain; charset=utf-8" };
19482
18333
  if (download) headers["Content-Disposition"] = `attachment; filename="${fileName}"`;
19483
18334
  return new Response(content, { headers });
@@ -19492,7 +18343,7 @@ app13.get("/", async (c) => {
19492
18343
  const seen = /* @__PURE__ */ new Set();
19493
18344
  const logs = {};
19494
18345
  for (const dir of [accountLogDir2, LOG_DIR]) {
19495
- if (!dir || !existsSync20(dir)) continue;
18346
+ if (!dir || !existsSync18(dir)) continue;
19496
18347
  let files;
19497
18348
  try {
19498
18349
  files = readdirSync5(dir).filter((f) => f.endsWith(".log"));
@@ -19501,10 +18352,10 @@ app13.get("/", async (c) => {
19501
18352
  console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
19502
18353
  continue;
19503
18354
  }
19504
- files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve21(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
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 }) => {
19505
18356
  seen.add(name);
19506
18357
  try {
19507
- const content = readFileSync20(resolve21(dir, name));
18358
+ const content = readFileSync18(resolve18(dir, name));
19508
18359
  const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
19509
18360
  logs[name] = tail.trim() || "(empty)";
19510
18361
  } catch (err) {
@@ -19544,8 +18395,8 @@ var claude_info_default = app14;
19544
18395
 
19545
18396
  // server/routes/admin/attachment.ts
19546
18397
  import { readFile as readFile3, readdir } from "fs/promises";
19547
- import { existsSync as existsSync21 } from "fs";
19548
- import { resolve as resolve22 } from "path";
18398
+ import { existsSync as existsSync19 } from "fs";
18399
+ import { resolve as resolve19 } from "path";
19549
18400
  var app15 = new Hono2();
19550
18401
  app15.get("/:attachmentId", requireAdminSession, async (c) => {
19551
18402
  const attachmentId = c.req.param("attachmentId");
@@ -19557,12 +18408,12 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
19557
18408
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
19558
18409
  return new Response("Not found", { status: 404 });
19559
18410
  }
19560
- const dir = resolve22(ATTACHMENTS_ROOT, accountId, attachmentId);
19561
- if (!existsSync21(dir)) {
18411
+ const dir = resolve19(ATTACHMENTS_ROOT, accountId, attachmentId);
18412
+ if (!existsSync19(dir)) {
19562
18413
  return new Response("Not found", { status: 404 });
19563
18414
  }
19564
- const metaPath = resolve22(dir, `${attachmentId}.meta.json`);
19565
- if (!existsSync21(metaPath)) {
18415
+ const metaPath = resolve19(dir, `${attachmentId}.meta.json`);
18416
+ if (!existsSync19(metaPath)) {
19566
18417
  return new Response("Not found", { status: 404 });
19567
18418
  }
19568
18419
  let meta;
@@ -19576,7 +18427,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
19576
18427
  if (!dataFile) {
19577
18428
  return new Response("Not found", { status: 404 });
19578
18429
  }
19579
- const filePath = resolve22(dir, dataFile);
18430
+ const filePath = resolve19(dir, dataFile);
19580
18431
  const buffer = await readFile3(filePath);
19581
18432
  return new Response(new Uint8Array(buffer), {
19582
18433
  headers: {
@@ -19589,8 +18440,8 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
19589
18440
  var attachment_default = app15;
19590
18441
 
19591
18442
  // server/routes/admin/account.ts
19592
- import { readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
19593
- import { resolve as resolve23 } from "path";
18443
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
18444
+ import { resolve as resolve20 } from "path";
19594
18445
  var VALID_CONTEXT_MODES = ["managed", "claude-code"];
19595
18446
  var app16 = new Hono2();
19596
18447
  app16.patch("/", requireAdminSession, async (c) => {
@@ -19606,12 +18457,12 @@ app16.patch("/", requireAdminSession, async (c) => {
19606
18457
  }
19607
18458
  const account = resolveAccount();
19608
18459
  if (!account) return c.json({ error: "No account configured" }, 500);
19609
- const configPath2 = resolve23(account.accountDir, "account.json");
18460
+ const configPath2 = resolve20(account.accountDir, "account.json");
19610
18461
  try {
19611
- const raw2 = readFileSync21(configPath2, "utf-8");
18462
+ const raw2 = readFileSync19(configPath2, "utf-8");
19612
18463
  const config = JSON.parse(raw2);
19613
18464
  config.contextMode = contextMode;
19614
- writeFileSync14(configPath2, JSON.stringify(config, null, 2) + "\n", "utf-8");
18465
+ writeFileSync13(configPath2, JSON.stringify(config, null, 2) + "\n", "utf-8");
19615
18466
  console.error(`[account-update] contextMode=${contextMode}`);
19616
18467
  return c.json({ ok: true, contextMode });
19617
18468
  } catch (err) {
@@ -19622,24 +18473,24 @@ app16.patch("/", requireAdminSession, async (c) => {
19622
18473
  var account_default = app16;
19623
18474
 
19624
18475
  // server/routes/admin/agents.ts
19625
- import { resolve as resolve24 } from "path";
19626
- import { readdirSync as readdirSync6, readFileSync as readFileSync22, existsSync as existsSync22, rmSync as rmSync3 } from "fs";
18476
+ import { resolve as resolve21 } from "path";
18477
+ import { readdirSync as readdirSync6, readFileSync as readFileSync20, existsSync as existsSync20, rmSync as rmSync3 } from "fs";
19627
18478
  var app17 = new Hono2();
19628
18479
  app17.get("/", (c) => {
19629
18480
  const account = resolveAccount();
19630
18481
  if (!account) return c.json({ agents: [] });
19631
- const agentsDir = resolve24(account.accountDir, "agents");
19632
- if (!existsSync22(agentsDir)) return c.json({ agents: [] });
18482
+ const agentsDir = resolve21(account.accountDir, "agents");
18483
+ if (!existsSync20(agentsDir)) return c.json({ agents: [] });
19633
18484
  const agents = [];
19634
18485
  try {
19635
18486
  const entries = readdirSync6(agentsDir, { withFileTypes: true });
19636
18487
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
19637
18488
  if (!entry.isDirectory()) continue;
19638
18489
  if (entry.name === "admin") continue;
19639
- const configPath2 = resolve24(agentsDir, entry.name, "config.json");
19640
- if (!existsSync22(configPath2)) continue;
18490
+ const configPath2 = resolve21(agentsDir, entry.name, "config.json");
18491
+ if (!existsSync20(configPath2)) continue;
19641
18492
  try {
19642
- const config = JSON.parse(readFileSync22(configPath2, "utf-8"));
18493
+ const config = JSON.parse(readFileSync20(configPath2, "utf-8"));
19643
18494
  agents.push({
19644
18495
  slug: entry.name,
19645
18496
  displayName: config.displayName ?? entry.name,
@@ -19665,8 +18516,8 @@ app17.delete("/:slug", (c) => {
19665
18516
  if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
19666
18517
  return c.json({ error: "Invalid agent slug" }, 400);
19667
18518
  }
19668
- const agentDir = resolve24(account.accountDir, "agents", slug);
19669
- if (!existsSync22(agentDir)) {
18519
+ const agentDir = resolve21(account.accountDir, "agents", slug);
18520
+ if (!existsSync20(agentDir)) {
19670
18521
  return c.json({ error: "Agent not found" }, 404);
19671
18522
  }
19672
18523
  try {
@@ -19681,27 +18532,27 @@ app17.delete("/:slug", (c) => {
19681
18532
  var agents_default = app17;
19682
18533
 
19683
18534
  // server/routes/admin/version.ts
19684
- import { existsSync as existsSync23, readFileSync as readFileSync23 } from "fs";
19685
- import { resolve as resolve25, join as join11 } from "path";
19686
- var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
18535
+ import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
18536
+ import { resolve as resolve22, join as join10 } from "path";
18537
+ var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
19687
18538
  var brandHostname = "maxy";
19688
18539
  var brandNpmPackage = "@rubytech/create-maxy";
19689
- var brandJsonPath = join11(PLATFORM_ROOT9, "config", "brand.json");
19690
- if (existsSync23(brandJsonPath)) {
18540
+ var brandJsonPath = join10(PLATFORM_ROOT8, "config", "brand.json");
18541
+ if (existsSync21(brandJsonPath)) {
19691
18542
  try {
19692
- const brand = JSON.parse(readFileSync23(brandJsonPath, "utf-8"));
18543
+ const brand = JSON.parse(readFileSync21(brandJsonPath, "utf-8"));
19693
18544
  if (brand.hostname) brandHostname = brand.hostname;
19694
18545
  if (brand.npm?.packageName) brandNpmPackage = brand.npm.packageName;
19695
18546
  } catch {
19696
18547
  }
19697
18548
  }
19698
- var VERSION_FILE = resolve25(PLATFORM_ROOT9, `config/.${brandHostname}-version`);
18549
+ var VERSION_FILE = resolve22(PLATFORM_ROOT8, `config/.${brandHostname}-version`);
19699
18550
  var NPM_PACKAGE = brandNpmPackage;
19700
18551
  var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
19701
18552
  var FETCH_TIMEOUT_MS = 5e3;
19702
18553
  function readInstalled() {
19703
- if (!existsSync23(VERSION_FILE)) return "unknown";
19704
- const content = readFileSync23(VERSION_FILE, "utf-8").trim();
18554
+ if (!existsSync21(VERSION_FILE)) return "unknown";
18555
+ const content = readFileSync21(VERSION_FILE, "utf-8").trim();
19705
18556
  return content || "unknown";
19706
18557
  }
19707
18558
  async function fetchLatest() {
@@ -19811,16 +18662,10 @@ app19.post("/new", requireAdminSession, async (c) => {
19811
18662
  const newSessionKey = crypto3.randomUUID();
19812
18663
  const userName = getUserNameForSession(oldSessionKey);
19813
18664
  registerSession(newSessionKey, "admin", accountId, void 0, userId, userName);
19814
- const conversationId = await createNewAdminConversation(userId, accountId, newSessionKey);
19815
- if (!conversationId) {
19816
- unregisterSession(newSessionKey);
19817
- 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`);
19818
- return c.json({ error: "Failed to create conversation" }, 500);
19819
- }
19820
18665
  const previousConversationId = clearSessionHistory(oldSessionKey);
19821
18666
  unregisterSession(oldSessionKey);
19822
- 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=${conversationId.slice(0, 8)}\u2026`);
19823
- 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 });
19824
18669
  });
19825
18670
  app19.delete("/:id", requireAdminSession, async (c) => {
19826
18671
  const conversationId = c.req.param("id");
@@ -20194,9 +19039,9 @@ app23.post("/", async (c) => {
20194
19039
  var events_default = app23;
20195
19040
 
20196
19041
  // server/routes/admin/cloudflare.ts
20197
- import { homedir as homedir4 } from "os";
20198
- import { resolve as resolve27 } from "path";
20199
- import { readFileSync as readFileSync25 } from "fs";
19042
+ import { homedir as homedir3 } from "os";
19043
+ import { resolve as resolve24 } from "path";
19044
+ import { readFileSync as readFileSync23 } from "fs";
20200
19045
 
20201
19046
  // app/lib/dns-label.ts
20202
19047
  var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
@@ -20212,14 +19057,14 @@ function isValidDomain(value) {
20212
19057
  }
20213
19058
 
20214
19059
  // app/lib/alias-domains.ts
20215
- import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync24, writeFileSync as writeFileSync15 } from "fs";
19060
+ import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
20216
19061
  import { dirname as dirname9 } from "path";
20217
- import { resolve as resolve26 } from "path";
20218
- var ALIAS_DOMAINS_PATH = resolve26(MAXY_DIR, "alias-domains.json");
19062
+ import { resolve as resolve23 } from "path";
19063
+ var ALIAS_DOMAINS_PATH = resolve23(MAXY_DIR, "alias-domains.json");
20219
19064
  function readExisting() {
20220
- if (!existsSync24(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
19065
+ if (!existsSync22(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
20221
19066
  try {
20222
- const parsed = JSON.parse(readFileSync24(ALIAS_DOMAINS_PATH, "utf-8"));
19067
+ const parsed = JSON.parse(readFileSync22(ALIAS_DOMAINS_PATH, "utf-8"));
20223
19068
  if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
20224
19069
  return new Set(parsed.filter((h) => typeof h === "string"));
20225
19070
  } catch {
@@ -20230,18 +19075,18 @@ function addAliasDomain(hostname2) {
20230
19075
  const existing = readExisting();
20231
19076
  if (existing.has(hostname2)) return;
20232
19077
  existing.add(hostname2);
20233
- mkdirSync14(dirname9(ALIAS_DOMAINS_PATH), { recursive: true });
20234
- writeFileSync15(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
19078
+ mkdirSync12(dirname9(ALIAS_DOMAINS_PATH), { recursive: true });
19079
+ writeFileSync14(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
20235
19080
  }
20236
19081
 
20237
19082
  // server/routes/admin/cloudflare.ts
20238
19083
  var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
20239
19084
  var DOMAINS_TIMEOUT_MS = 40 * 1e3;
20240
19085
  function loadBrandInfo() {
20241
- const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve27(process.cwd(), "..");
20242
- const brandPath = resolve27(platformRoot3, "config", "brand.json");
19086
+ const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
19087
+ const brandPath = resolve24(platformRoot2, "config", "brand.json");
20243
19088
  try {
20244
- const parsed = JSON.parse(readFileSync25(brandPath, "utf-8"));
19089
+ const parsed = JSON.parse(readFileSync23(brandPath, "utf-8"));
20245
19090
  const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
20246
19091
  const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
20247
19092
  return { hostname: hostname2, configDir: configDir2 };
@@ -20344,7 +19189,7 @@ app24.get("/domains", requireAdminSession, async (c) => {
20344
19189
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
20345
19190
  log2(`phase=stream-log-resolved path=${streamLogPath}`);
20346
19191
  const brand = loadBrandInfo();
20347
- const scriptPath = resolve27(homedir4(), "list-cf-domains.sh");
19192
+ const scriptPath = resolve24(homedir3(), "list-cf-domains.sh");
20348
19193
  const result = await runFormSpawn({
20349
19194
  scriptPath,
20350
19195
  args: [brand.hostname],
@@ -20469,7 +19314,7 @@ app24.post("/setup", requireAdminSession, async (c) => {
20469
19314
  }
20470
19315
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
20471
19316
  log2(`phase=stream-log-resolved path=${streamLogPath}`);
20472
- const scriptPath = resolve27(homedir4(), "setup-tunnel.sh");
19317
+ const scriptPath = resolve24(homedir3(), "setup-tunnel.sh");
20473
19318
  const args = [brand.hostname, String(port2), adminFqdn];
20474
19319
  if (publicFqdn) args.push(publicFqdn);
20475
19320
  if (apex) args.push(apex);
@@ -20538,17 +19383,17 @@ var cloudflare_default = app24;
20538
19383
  import { createReadStream as createReadStream3 } from "fs";
20539
19384
  import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
20540
19385
  import { realpathSync as realpathSync4 } from "fs";
20541
- import { basename as basename6, dirname as dirname10, join as join12, resolve as resolve29, sep as sep2 } from "path";
19386
+ import { basename as basename6, dirname as dirname10, join as join11, resolve as resolve26, sep as sep2 } from "path";
20542
19387
  import { Readable as Readable3 } from "stream";
20543
19388
 
20544
19389
  // app/lib/data-path.ts
20545
19390
  import { realpathSync as realpathSync3 } from "fs";
20546
- import { resolve as resolve28, normalize, sep, relative } from "path";
20547
- var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT ?? resolve28(process.cwd(), "../platform");
20548
- var DATA_ROOT = resolve28(PLATFORM_ROOT10, "..", "data");
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");
20549
19394
  function resolveDataPath(raw2) {
20550
19395
  const cleaned = normalize("/" + (raw2 ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
20551
- const absolute = resolve28(DATA_ROOT, cleaned);
19396
+ const absolute = resolve25(DATA_ROOT, cleaned);
20552
19397
  let dataRootReal;
20553
19398
  try {
20554
19399
  dataRootReal = realpathSync3(DATA_ROOT);
@@ -20815,7 +19660,7 @@ async function cascadeDeleteDocument(params) {
20815
19660
  var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20816
19661
  async function readMeta(absDir, baseName) {
20817
19662
  try {
20818
- const raw2 = await readFile4(join12(absDir, `${baseName}.meta.json`), "utf8");
19663
+ const raw2 = await readFile4(join11(absDir, `${baseName}.meta.json`), "utf8");
20819
19664
  const parsed = JSON.parse(raw2);
20820
19665
  if (typeof parsed?.filename === "string") {
20821
19666
  return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
@@ -20826,7 +19671,7 @@ async function readMeta(absDir, baseName) {
20826
19671
  }
20827
19672
  async function readAccountNames() {
20828
19673
  const map = /* @__PURE__ */ new Map();
20829
- const accountsDir = resolve29(DATA_ROOT, "accounts");
19674
+ const accountsDir = resolve26(DATA_ROOT, "accounts");
20830
19675
  let names;
20831
19676
  try {
20832
19677
  names = await readdir2(accountsDir);
@@ -20835,7 +19680,7 @@ async function readAccountNames() {
20835
19680
  }
20836
19681
  for (const name of names) {
20837
19682
  if (!UUID_RE3.test(name)) continue;
20838
- const configPath2 = resolve29(accountsDir, name, "account.json");
19683
+ const configPath2 = resolve26(accountsDir, name, "account.json");
20839
19684
  try {
20840
19685
  const raw2 = await readFile4(configPath2, "utf8");
20841
19686
  const parsed = JSON.parse(raw2);
@@ -20853,7 +19698,7 @@ async function readAccountNames() {
20853
19698
  }
20854
19699
  async function enrich(absolute, entry, accountNames) {
20855
19700
  if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
20856
- const meta = await readMeta(join12(absolute, entry.name), entry.name);
19701
+ const meta = await readMeta(join11(absolute, entry.name), entry.name);
20857
19702
  if (meta?.filename) {
20858
19703
  entry.displayName = meta.filename;
20859
19704
  entry.mimeType = meta.mimeType;
@@ -20912,7 +19757,7 @@ app25.get("/", requireAdminSession, async (c) => {
20912
19757
  continue;
20913
19758
  }
20914
19759
  try {
20915
- const entryPath = join12(absolute, name);
19760
+ const entryPath = join11(absolute, name);
20916
19761
  const s = await stat4(entryPath);
20917
19762
  entries.push({
20918
19763
  name,
@@ -21026,8 +19871,8 @@ app25.post("/upload", requireAdminSession, async (c) => {
21026
19871
  }
21027
19872
  const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
21028
19873
  const finalName = `${Date.now()}-${safeName}`;
21029
- const destDir = resolve29(DATA_ROOT, "uploads", accountId);
21030
- const destPath = resolve29(destDir, finalName);
19874
+ const destDir = resolve26(DATA_ROOT, "uploads", accountId);
19875
+ const destPath = resolve26(destDir, finalName);
21031
19876
  try {
21032
19877
  await mkdir3(destDir, { recursive: true });
21033
19878
  const dataRootReal = realpathSync4(DATA_ROOT);
@@ -21085,7 +19930,7 @@ app25.delete("/", requireAdminSession, async (c) => {
21085
19930
  }
21086
19931
  const dot = base.lastIndexOf(".");
21087
19932
  const stem = dot === -1 ? base : base.slice(0, dot);
21088
- const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join12(dirname10(absolute), `${stem}.meta.json`) : null;
19933
+ const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join11(dirname10(absolute), `${stem}.meta.json`) : null;
21089
19934
  await unlink2(absolute);
21090
19935
  if (sidecarPath) {
21091
19936
  try {
@@ -21180,24 +20025,35 @@ var GRAPH_LABEL_COLOURS = {
21180
20025
  Review: "#059669",
21181
20026
  ImageObject: "#6EE7B7",
21182
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.
21183
20051
  Conversation: "#7C3AED",
21184
- // Task 633 — admin vs public disambiguation on /graph. Darker violet for
21185
- // admin conversations (the operator-facing side), lighter for public
21186
- // (customer-facing) so the operator can read chat topology by colour
21187
- // alone. Both within the Conversation family — the hue shift is subtle
21188
- // enough to stay legible on adjacent nodes yet distinct enough to be
21189
- // unmistakable in the popover legend.
21190
- AdminConversation: "#5B21B6",
21191
- PublicConversation: "#A78BFA",
21192
- Message: "#A78BFA",
21193
- // Task 633 — user vs assistant disambiguation on /graph. UserMessage
21194
- // picks up the Person/Preference orange family cue (the user is a
21195
- // person); AssistantMessage stays violet-adjacent to reinforce the
21196
- // AdminConversation cue (both are admin-side). system/tool Messages
21197
- // (not written by the persist path today; reserved shape) stay on the
21198
- // base `:Message` colour.
20052
+ AdminConversation: "#4338CA",
20053
+ PublicConversation: "#A855F7",
20054
+ Message: "#64748B",
21199
20055
  UserMessage: "#F97316",
21200
- AssistantMessage: "#8B5CF6",
20056
+ AssistantMessage: "#FACC15",
21201
20057
  ToolCall: "#C4B5FD",
21202
20058
  // Tasks / projects / events
21203
20059
  Task: "#DB2777",
@@ -21961,15 +20817,15 @@ app34.route("/adherence", adherence_default);
21961
20817
  var admin_default = app34;
21962
20818
 
21963
20819
  // server/index.ts
21964
- var PLATFORM_ROOT11 = process.env.MAXY_PLATFORM_ROOT || "";
21965
- var BRAND_JSON_PATH = PLATFORM_ROOT11 ? join13(PLATFORM_ROOT11, "config", "brand.json") : "";
20820
+ var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT || "";
20821
+ var BRAND_JSON_PATH = PLATFORM_ROOT10 ? join12(PLATFORM_ROOT10, "config", "brand.json") : "";
21966
20822
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
21967
- if (BRAND_JSON_PATH && !existsSync25(BRAND_JSON_PATH)) {
20823
+ if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
21968
20824
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
21969
20825
  }
21970
- if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
20826
+ if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
21971
20827
  try {
21972
- const parsed = JSON.parse(readFileSync26(BRAND_JSON_PATH, "utf-8"));
20828
+ const parsed = JSON.parse(readFileSync24(BRAND_JSON_PATH, "utf-8"));
21973
20829
  BRAND = { ...BRAND, ...parsed };
21974
20830
  } catch (err) {
21975
20831
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -21988,11 +20844,11 @@ var brandLoginOpts = {
21988
20844
  bodyFont: BRAND.defaultFonts?.body,
21989
20845
  logoContainsName: !!BRAND.logoContainsName
21990
20846
  };
21991
- var ALIAS_DOMAINS_PATH2 = join13(homedir5(), BRAND.configDir, "alias-domains.json");
20847
+ var ALIAS_DOMAINS_PATH2 = join12(homedir4(), BRAND.configDir, "alias-domains.json");
21992
20848
  function loadAliasDomains() {
21993
20849
  try {
21994
- if (!existsSync25(ALIAS_DOMAINS_PATH2)) return null;
21995
- const parsed = JSON.parse(readFileSync26(ALIAS_DOMAINS_PATH2, "utf-8"));
20850
+ if (!existsSync23(ALIAS_DOMAINS_PATH2)) return null;
20851
+ const parsed = JSON.parse(readFileSync24(ALIAS_DOMAINS_PATH2, "utf-8"));
21996
20852
  if (!Array.isArray(parsed)) {
21997
20853
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
21998
20854
  return null;
@@ -22332,20 +21188,20 @@ app35.get("/agent-assets/:slug/:filename", (c) => {
22332
21188
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
22333
21189
  return c.text("Not found", 404);
22334
21190
  }
22335
- const filePath = resolve30(account.accountDir, "agents", slug, "assets", filename);
22336
- const expectedDir = resolve30(account.accountDir, "agents", slug, "assets");
21191
+ const filePath = resolve27(account.accountDir, "agents", slug, "assets", filename);
21192
+ const expectedDir = resolve27(account.accountDir, "agents", slug, "assets");
22337
21193
  if (!filePath.startsWith(expectedDir + "/")) {
22338
21194
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
22339
21195
  return c.text("Forbidden", 403);
22340
21196
  }
22341
- if (!existsSync25(filePath)) {
21197
+ if (!existsSync23(filePath)) {
22342
21198
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
22343
21199
  return c.text("Not found", 404);
22344
21200
  }
22345
21201
  const ext = "." + filename.split(".").pop()?.toLowerCase();
22346
21202
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
22347
21203
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
22348
- const body = readFileSync26(filePath);
21204
+ const body = readFileSync24(filePath);
22349
21205
  return c.body(body, 200, {
22350
21206
  "Content-Type": contentType,
22351
21207
  "Cache-Control": "public, max-age=3600"
@@ -22362,20 +21218,20 @@ app35.get("/generated/:filename", (c) => {
22362
21218
  console.error(`[generated] serve file=${filename} status=404`);
22363
21219
  return c.text("Not found", 404);
22364
21220
  }
22365
- const filePath = resolve30(account.accountDir, "generated", filename);
22366
- const expectedDir = resolve30(account.accountDir, "generated");
21221
+ const filePath = resolve27(account.accountDir, "generated", filename);
21222
+ const expectedDir = resolve27(account.accountDir, "generated");
22367
21223
  if (!filePath.startsWith(expectedDir + "/")) {
22368
21224
  console.error(`[generated] serve file=${filename} status=403`);
22369
21225
  return c.text("Forbidden", 403);
22370
21226
  }
22371
- if (!existsSync25(filePath)) {
21227
+ if (!existsSync23(filePath)) {
22372
21228
  console.error(`[generated] serve file=${filename} status=404`);
22373
21229
  return c.text("Not found", 404);
22374
21230
  }
22375
21231
  const ext = "." + filename.split(".").pop()?.toLowerCase();
22376
21232
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
22377
21233
  console.log(`[generated] serve file=${filename} status=200`);
22378
- const body = readFileSync26(filePath);
21234
+ const body = readFileSync24(filePath);
22379
21235
  return c.body(body, 200, {
22380
21236
  "Content-Type": contentType,
22381
21237
  "Cache-Control": "public, max-age=86400"
@@ -22384,9 +21240,9 @@ app35.get("/generated/:filename", (c) => {
22384
21240
  var htmlCache = /* @__PURE__ */ new Map();
22385
21241
  var brandLogoPath = "/brand/maxy-monochrome.png";
22386
21242
  var brandIconPath = "/brand/maxy-monochrome.png";
22387
- if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
21243
+ if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
22388
21244
  try {
22389
- const fullBrand = JSON.parse(readFileSync26(BRAND_JSON_PATH, "utf-8"));
21245
+ const fullBrand = JSON.parse(readFileSync24(BRAND_JSON_PATH, "utf-8"));
22390
21246
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
22391
21247
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
22392
21248
  } catch {
@@ -22402,10 +21258,10 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
22402
21258
  })}</script>`;
22403
21259
  function readInstalledVersion() {
22404
21260
  try {
22405
- if (!PLATFORM_ROOT11) return "unknown";
22406
- const versionFile = join13(PLATFORM_ROOT11, "config", `.${BRAND.hostname}-version`);
22407
- if (!existsSync25(versionFile)) return "unknown";
22408
- const content = readFileSync26(versionFile, "utf-8").trim();
21261
+ if (!PLATFORM_ROOT10) return "unknown";
21262
+ const versionFile = join12(PLATFORM_ROOT10, "config", `.${BRAND.hostname}-version`);
21263
+ if (!existsSync23(versionFile)) return "unknown";
21264
+ const content = readFileSync24(versionFile, "utf-8").trim();
22409
21265
  return content || "unknown";
22410
21266
  } catch {
22411
21267
  return "unknown";
@@ -22446,9 +21302,9 @@ var clientErrorReporterScript = `<script>
22446
21302
  function cachedHtml(file) {
22447
21303
  let html = htmlCache.get(file);
22448
21304
  if (!html) {
22449
- html = readFileSync26(resolve30(process.cwd(), "public", file), "utf-8");
22450
- html = html.replace("<title>Maxy</title>", `<title>${escapeHtml2(BRAND.productName)}</title>`);
22451
- html = html.replace('href="/favicon.ico"', `href="${escapeHtml2(brandFaviconPath)}"`);
21305
+ html = readFileSync24(resolve27(process.cwd(), "public", file), "utf-8");
21306
+ html = html.replace("<title>Maxy</title>", `<title>${escapeHtml(BRAND.productName)}</title>`);
21307
+ html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
22452
21308
  const headInjection = file === "index.html" ? `${brandScript}
22453
21309
  ${versionScript}
22454
21310
  ${clientErrorReporterScript}
@@ -22461,26 +21317,26 @@ ${clientErrorReporterScript}
22461
21317
  }
22462
21318
  var brandedHtmlCache = /* @__PURE__ */ new Map();
22463
21319
  function loadBrandingCache(agentSlug) {
22464
- const configDir2 = join13(homedir5(), BRAND.configDir);
21320
+ const configDir2 = join12(homedir4(), BRAND.configDir);
22465
21321
  try {
22466
- const accountJsonPath = join13(configDir2, "account.json");
22467
- if (!existsSync25(accountJsonPath)) return null;
22468
- const account = JSON.parse(readFileSync26(accountJsonPath, "utf-8"));
21322
+ const accountJsonPath = join12(configDir2, "account.json");
21323
+ if (!existsSync23(accountJsonPath)) return null;
21324
+ const account = JSON.parse(readFileSync24(accountJsonPath, "utf-8"));
22469
21325
  const accountId = account.accountId;
22470
21326
  if (!accountId) return null;
22471
- const cachePath = join13(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
22472
- if (!existsSync25(cachePath)) return null;
22473
- return JSON.parse(readFileSync26(cachePath, "utf-8"));
21327
+ const cachePath = join12(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
21328
+ if (!existsSync23(cachePath)) return null;
21329
+ return JSON.parse(readFileSync24(cachePath, "utf-8"));
22474
21330
  } catch {
22475
21331
  return null;
22476
21332
  }
22477
21333
  }
22478
21334
  function resolveDefaultSlug() {
22479
21335
  try {
22480
- const configDir2 = join13(homedir5(), BRAND.configDir);
22481
- const accountJsonPath = join13(configDir2, "account.json");
22482
- if (!existsSync25(accountJsonPath)) return null;
22483
- const account = JSON.parse(readFileSync26(accountJsonPath, "utf-8"));
21336
+ const configDir2 = join12(homedir4(), BRAND.configDir);
21337
+ const accountJsonPath = join12(configDir2, "account.json");
21338
+ if (!existsSync23(accountJsonPath)) return null;
21339
+ const account = JSON.parse(readFileSync24(accountJsonPath, "utf-8"));
22484
21340
  return account.defaultAgent || null;
22485
21341
  } catch {
22486
21342
  return null;
@@ -22494,26 +21350,26 @@ function brandedPublicHtml(agentSlug) {
22494
21350
  if (!branding) return baseHtml;
22495
21351
  const brandHash = JSON.stringify(branding).length;
22496
21352
  if (cached && cached.mtime === brandHash) return cached.html;
22497
- const title = escapeHtml2(branding.name);
22498
- const description = branding.tagline ? escapeHtml2(branding.tagline) : "";
21353
+ const title = escapeHtml(branding.name);
21354
+ const description = branding.tagline ? escapeHtml(branding.tagline) : "";
22499
21355
  const themeColor = branding.primaryColor || "";
22500
21356
  const ogImage = branding.logoUrl || "";
22501
21357
  const favicon = branding.faviconUrl || "";
22502
21358
  const metaTags = [
22503
21359
  ` <title>${title}</title>`,
22504
- favicon ? ` <link rel="icon" href="${escapeHtml2(favicon)}">` : ` <link rel="icon" href="${escapeHtml2(brandFaviconPath)}">`,
22505
- themeColor ? ` <meta name="theme-color" content="${escapeHtml2(themeColor)}">` : "",
21360
+ favicon ? ` <link rel="icon" href="${escapeHtml(favicon)}">` : ` <link rel="icon" href="${escapeHtml(brandFaviconPath)}">`,
21361
+ themeColor ? ` <meta name="theme-color" content="${escapeHtml(themeColor)}">` : "",
22506
21362
  ` <meta property="og:title" content="${title}">`,
22507
21363
  description ? ` <meta property="og:description" content="${description}">` : "",
22508
- ogImage ? ` <meta property="og:image" content="${escapeHtml2(ogImage)}">` : "",
21364
+ ogImage ? ` <meta property="og:image" content="${escapeHtml(ogImage)}">` : "",
22509
21365
  ' <meta property="og:type" content="website">'
22510
21366
  ].filter(Boolean).join("\n");
22511
- const html = baseHtml.replace(` <title>${escapeHtml2(BRAND.productName)}</title>
22512
- <link rel="icon" href="${escapeHtml2(brandFaviconPath)}">`, metaTags);
21367
+ const html = baseHtml.replace(` <title>${escapeHtml(BRAND.productName)}</title>
21368
+ <link rel="icon" href="${escapeHtml(brandFaviconPath)}">`, metaTags);
22513
21369
  brandedHtmlCache.set(agentSlug, { html, mtime: brandHash });
22514
21370
  return html;
22515
21371
  }
22516
- function escapeHtml2(s) {
21372
+ function escapeHtml(s) {
22517
21373
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
22518
21374
  }
22519
21375
  app35.get("/", (c) => {
@@ -22553,11 +21409,11 @@ app35.use("/vnc-popout.html", logViewerFetch);
22553
21409
  app35.get("/vnc-popout.html", (c) => {
22554
21410
  let html = htmlCache.get("vnc-popout.html");
22555
21411
  if (!html) {
22556
- html = readFileSync26(resolve30(process.cwd(), "public", "vnc-popout.html"), "utf-8");
22557
- const name = escapeHtml2(BRAND.productName);
21412
+ html = readFileSync24(resolve27(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21413
+ const name = escapeHtml(BRAND.productName);
22558
21414
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
22559
21415
  html = html.replace("</head>", ` ${brandScript}
22560
- <link rel="icon" href="${escapeHtml2(brandFaviconPath)}">
21416
+ <link rel="icon" href="${escapeHtml(brandFaviconPath)}">
22561
21417
  </head>`);
22562
21418
  htmlCache.set("vnc-popout.html", html);
22563
21419
  }
@@ -22607,36 +21463,10 @@ app35.get("/:slug", async (c, next) => {
22607
21463
  await next();
22608
21464
  });
22609
21465
  app35.use("/*", serveStatic({ root: "./public" }));
22610
- var port = parseInt(process.env.PORT ?? "19200", 10);
22611
- var hostname = process.env.HOSTNAME ?? "0.0.0.0";
21466
+ var port = parseInt(process.env.PORT ?? "19199", 10);
21467
+ var hostname = process.env.HOSTNAME ?? "127.0.0.1";
22612
21468
  var httpServer = serve({ fetch: app35.fetch, port, hostname });
22613
- attachVncWsProxy(httpServer, {
22614
- isPublicHost,
22615
- upstreamHost: "127.0.0.1",
22616
- upstreamPort: 6080
22617
- });
22618
- attachTerminalWsProxy(httpServer, {
22619
- isPublicHost,
22620
- upstreamHost: "127.0.0.1",
22621
- upstreamPort: 7681
22622
- });
22623
21469
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
22624
- setTimeout(() => {
22625
- const socket = createConnection5(7681, "127.0.0.1");
22626
- socket.setTimeout(500);
22627
- socket.once("connect", () => {
22628
- socket.destroy();
22629
- console.log("[ttyd] upstream ready on 127.0.0.1:7681");
22630
- });
22631
- socket.once("error", (err) => {
22632
- socket.destroy();
22633
- console.error(`[ttyd] upstream NOT reachable on 127.0.0.1:7681 \u2014 admin terminal will be unavailable. Check 'sudo systemctl --user status maxy-ttyd' and /usr/local/bin/ttyd. (${err.message})`);
22634
- });
22635
- socket.once("timeout", () => {
22636
- socket.destroy();
22637
- console.error("[ttyd] upstream NOT reachable on 127.0.0.1:7681 (timeout) \u2014 admin terminal will be unavailable. Check 'sudo systemctl --user status maxy-ttyd'.");
22638
- });
22639
- }, 5e3);
22640
21470
  var SUBAPP_MANIFEST = [
22641
21471
  { prefix: "/api/health", file: "server/routes/health.ts", subapp: health_default },
22642
21472
  { prefix: "/api/session", file: "server/routes/session.ts", subapp: session_default },
@@ -22669,8 +21499,8 @@ try {
22669
21499
  (async () => {
22670
21500
  try {
22671
21501
  let userId = "";
22672
- if (existsSync25(USERS_FILE)) {
22673
- const users = JSON.parse(readFileSync26(USERS_FILE, "utf-8").trim() || "[]");
21502
+ if (existsSync23(USERS_FILE)) {
21503
+ const users = JSON.parse(readFileSync24(USERS_FILE, "utf-8").trim() || "[]");
22674
21504
  userId = users[0]?.userId ?? "";
22675
21505
  }
22676
21506
  await backfillNullUserIdConversations(userId);
@@ -22696,7 +21526,7 @@ if (bootAccountConfig?.whatsapp) {
22696
21526
  }
22697
21527
  init({
22698
21528
  configDir: configDirForWhatsApp,
22699
- platformRoot: resolve30(process.env.MAXY_PLATFORM_ROOT ?? join13(__dirname, "..")),
21529
+ platformRoot: resolve27(process.env.MAXY_PLATFORM_ROOT ?? join12(__dirname, "..")),
22700
21530
  accountConfig: bootAccountConfig,
22701
21531
  onMessage: async (msg) => {
22702
21532
  try {