@rubytech/create-maxy 1.0.676 → 1.0.677

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  });
@@ -8560,8 +7324,8 @@ function consumeStalledSubagents(sessionKey) {
8560
7324
  return stalls && stalls.length > 0 ? stalls : void 0;
8561
7325
  }
8562
7326
  function streamLogPathFor(accountId, conversationId) {
8563
- const logDir = resolve8(ACCOUNTS_DIR, accountId, "logs");
8564
- const streamLogPath = resolve8(logDir, `claude-agent-stream-${conversationId}.log`);
7327
+ const logDir = resolve5(ACCOUNTS_DIR, accountId, "logs");
7328
+ const streamLogPath = resolve5(logDir, `claude-agent-stream-${conversationId}.log`);
8565
7329
  return { logDir, streamLogPath };
8566
7330
  }
8567
7331
  function buildSpawnEnv(accountId, accountDir, conversationId) {
@@ -8571,7 +7335,7 @@ function buildSpawnEnv(accountId, accountDir, conversationId) {
8571
7335
  const { logDir, streamLogPath } = streamLogPathFor(accountId, conversationId);
8572
7336
  return {
8573
7337
  ...process.env,
8574
- PLATFORM_ROOT: PLATFORM_ROOT4,
7338
+ PLATFORM_ROOT: PLATFORM_ROOT3,
8575
7339
  ACCOUNT_DIR: accountDir,
8576
7340
  ACCOUNT_ID: accountId,
8577
7341
  LOG_DIR: logDir,
@@ -8582,8 +7346,8 @@ var cachedBrandHostname = null;
8582
7346
  function readBrandHostname() {
8583
7347
  if (cachedBrandHostname !== null) return cachedBrandHostname;
8584
7348
  try {
8585
- const brandPath = resolve8(PLATFORM_ROOT4, "config", "brand.json");
8586
- const parsed = JSON.parse(readFileSync8(brandPath, "utf-8"));
7349
+ const brandPath = resolve5(PLATFORM_ROOT3, "config", "brand.json");
7350
+ const parsed = JSON.parse(readFileSync6(brandPath, "utf-8"));
8587
7351
  cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
8588
7352
  } catch {
8589
7353
  cachedBrandHostname = "maxy";
@@ -8612,7 +7376,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8612
7376
  const { logDir: LOG_DIR2, streamLogPath: STREAM_LOG_PATH } = streamLogPathFor(accountId, conversationId);
8613
7377
  const baseEnv = {
8614
7378
  ACCOUNT_ID: accountId,
8615
- PLATFORM_ROOT: PLATFORM_ROOT4,
7379
+ PLATFORM_ROOT: PLATFORM_ROOT3,
8616
7380
  LOG_DIR: LOG_DIR2,
8617
7381
  STREAM_LOG_PATH,
8618
7382
  NEO4J_URI: requireNeo4jUri()
@@ -8620,37 +7384,37 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8620
7384
  const servers = {
8621
7385
  "memory": {
8622
7386
  command: "node",
8623
- args: [resolve8(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js")],
7387
+ args: [resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js")],
8624
7388
  env: { ...baseEnv, ...userId ? { USER_ID: userId } : {} }
8625
7389
  },
8626
7390
  "contacts": {
8627
7391
  command: "node",
8628
- args: [resolve8(PLATFORM_ROOT4, "plugins/contacts/mcp/dist/index.js")],
7392
+ args: [resolve5(PLATFORM_ROOT3, "plugins/contacts/mcp/dist/index.js")],
8629
7393
  env: { ...baseEnv }
8630
7394
  },
8631
7395
  "whatsapp": {
8632
7396
  command: "node",
8633
- args: [resolve8(PLATFORM_ROOT4, "plugins/whatsapp/mcp/dist/index.js")],
7397
+ args: [resolve5(PLATFORM_ROOT3, "plugins/whatsapp/mcp/dist/index.js")],
8634
7398
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
8635
7399
  },
8636
7400
  "admin": {
8637
7401
  command: "node",
8638
- args: [resolve8(PLATFORM_ROOT4, "plugins/admin/mcp/dist/index.js")],
7402
+ args: [resolve5(PLATFORM_ROOT3, "plugins/admin/mcp/dist/index.js")],
8639
7403
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200", ...userId ? { USER_ID: userId } : {} }
8640
7404
  },
8641
7405
  "scheduling": {
8642
7406
  command: "node",
8643
- args: [resolve8(PLATFORM_ROOT4, "plugins/scheduling/mcp/dist/index.js")],
7407
+ args: [resolve5(PLATFORM_ROOT3, "plugins/scheduling/mcp/dist/index.js")],
8644
7408
  env: { ...baseEnv }
8645
7409
  },
8646
7410
  "tasks": {
8647
7411
  command: "node",
8648
- args: [resolve8(PLATFORM_ROOT4, "plugins/tasks/mcp/dist/index.js")],
7412
+ args: [resolve5(PLATFORM_ROOT3, "plugins/tasks/mcp/dist/index.js")],
8649
7413
  env: { ...baseEnv }
8650
7414
  },
8651
7415
  "email": {
8652
7416
  command: "node",
8653
- args: [resolve8(PLATFORM_ROOT4, "plugins/email/mcp/dist/index.js")],
7417
+ args: [resolve5(PLATFORM_ROOT3, "plugins/email/mcp/dist/index.js")],
8654
7418
  env: { ...baseEnv }
8655
7419
  },
8656
7420
  // Workflows MCP — persistent admin-session server for list/get/update/delete/
@@ -8661,7 +7425,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8661
7425
  // ToolSearches fruitlessly before degrading to a task-create stand-in (Task 571).
8662
7426
  "workflows": {
8663
7427
  command: "node",
8664
- args: [resolve8(PLATFORM_ROOT4, "plugins/workflows/mcp/dist/index.js")],
7428
+ args: [resolve5(PLATFORM_ROOT3, "plugins/workflows/mcp/dist/index.js")],
8665
7429
  env: { ...baseEnv }
8666
7430
  },
8667
7431
  // Playwright MCP server — browser automation for browser-specialist.
@@ -8683,7 +7447,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8683
7447
  // MAXY-PRD.md:627, not in any application-layer filter).
8684
7448
  "graph": {
8685
7449
  command: "node",
8686
- args: [resolve8(PLATFORM_ROOT4, "lib/graph-mcp/dist/index.js")],
7450
+ args: [resolve5(PLATFORM_ROOT3, "lib/graph-mcp/dist/index.js")],
8687
7451
  env: {
8688
7452
  ...baseEnv,
8689
7453
  BRAND: readBrandHostname(),
@@ -8699,7 +7463,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8699
7463
  if (tgBotToken) {
8700
7464
  servers["telegram"] = {
8701
7465
  command: "node",
8702
- args: [resolve8(PLATFORM_ROOT4, "plugins/telegram/mcp/dist/index.js")],
7466
+ args: [resolve5(PLATFORM_ROOT3, "plugins/telegram/mcp/dist/index.js")],
8703
7467
  env: { ...baseEnv, TELEGRAM_BOT_TOKEN: tgBotToken }
8704
7468
  };
8705
7469
  } else {
@@ -8707,11 +7471,11 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8707
7471
  }
8708
7472
  servers["cloudflare"] = {
8709
7473
  command: "node",
8710
- args: [resolve8(PLATFORM_ROOT4, "plugins/cloudflare/mcp/dist/index.js")],
7474
+ args: [resolve5(PLATFORM_ROOT3, "plugins/cloudflare/mcp/dist/index.js")],
8711
7475
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
8712
7476
  };
8713
7477
  if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
8714
- const pluginsDir = resolve8(PLATFORM_ROOT4, "plugins");
7478
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
8715
7479
  let dirs;
8716
7480
  try {
8717
7481
  dirs = readdirSync2(pluginsDir);
@@ -8734,8 +7498,8 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8734
7498
  continue;
8735
7499
  }
8736
7500
  }
8737
- const mcpEntry = resolve8(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
8738
- if (!existsSync7(mcpEntry)) continue;
7501
+ const mcpEntry = resolve5(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
7502
+ if (!existsSync5(mcpEntry)) continue;
8739
7503
  servers[dir] = {
8740
7504
  command: "node",
8741
7505
  args: [mcpEntry],
@@ -8870,7 +7634,7 @@ var ADMIN_CORE_TOOLS = [
8870
7634
  function getAdminAllowedTools(enabledPlugins) {
8871
7635
  const tools = [...ADMIN_CORE_TOOLS];
8872
7636
  if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
8873
- const pluginsDir = resolve8(PLATFORM_ROOT4, "plugins");
7637
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
8874
7638
  let dirs;
8875
7639
  try {
8876
7640
  dirs = readdirSync2(pluginsDir);
@@ -8978,18 +7742,18 @@ ${message.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`
8978
7742
  }
8979
7743
  }
8980
7744
  async function fetchMemoryContext(accountId, query, sessionKey, options) {
8981
- const serverPath = resolve8(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
8982
- if (!existsSync7(serverPath)) {
7745
+ const serverPath = resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
7746
+ if (!existsSync5(serverPath)) {
8983
7747
  console.error(`[fetchMemoryContext] MCP server not found: ${serverPath}`);
8984
7748
  return null;
8985
7749
  }
8986
7750
  const startMs = Date.now();
8987
- return new Promise((resolve31) => {
7751
+ return new Promise((resolve28) => {
8988
7752
  const proc = spawn2(process.execPath, [serverPath], {
8989
7753
  env: {
8990
7754
  ...process.env,
8991
7755
  ACCOUNT_ID: accountId,
8992
- PLATFORM_ROOT: PLATFORM_ROOT4,
7756
+ PLATFORM_ROOT: PLATFORM_ROOT3,
8993
7757
  READ_ONLY: "true",
8994
7758
  ALLOWED_SCOPES: "public,shared",
8995
7759
  ...sessionKey ? { SESSION_ID: sessionKey } : {},
@@ -9013,7 +7777,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
9013
7777
  } else {
9014
7778
  console.error(`[fetchMemoryContext] failed: ${reason} (${elapsed}ms)${stderrBuf ? ` stderr: ${stderrBuf.slice(0, 500)}` : ""}`);
9015
7779
  }
9016
- resolve31(value);
7780
+ resolve28(value);
9017
7781
  };
9018
7782
  proc.stdout.on("data", (chunk) => {
9019
7783
  buffer += chunk.toString();
@@ -9076,8 +7840,8 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
9076
7840
  }
9077
7841
  async function compactTrimmedMessages(accountId, trimmedMessages) {
9078
7842
  if (trimmedMessages.length === 0) return true;
9079
- const serverPath = resolve8(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
9080
- if (!existsSync7(serverPath)) return false;
7843
+ const serverPath = resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
7844
+ if (!existsSync5(serverPath)) return false;
9081
7845
  const briefing = trimmedMessages.map((m) => `[${m.role.toUpperCase()}] ${m.content}`).join("\n\n");
9082
7846
  return new Promise((resolvePromise) => {
9083
7847
  const proc = spawn2(process.execPath, [serverPath], {
@@ -9394,8 +8158,8 @@ Then respond with only: [COMPACTED]`;
9394
8158
  var COMPACTION_TIMEOUT_MS = 45e3;
9395
8159
  async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, conversationId, enabledPlugins) {
9396
8160
  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}
8161
+ const specialistsDir = resolve5(accountDir, "specialists");
8162
+ if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir, conversationId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
9399
8163
  `);
9400
8164
  const args = [
9401
8165
  "--print",
@@ -9668,7 +8432,7 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId, a
9668
8432
  const { logDir } = streamLogPathFor(accountId, conversationId);
9669
8433
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
9670
8434
  for (const s of failed) {
9671
- const stderrPath = resolve8(logDir, `mcp-${s.name}-stderr-${date}.log`);
8435
+ const stderrPath = resolve5(logDir, `mcp-${s.name}-stderr-${date}.log`);
9672
8436
  let tail = "(no stderr file)";
9673
8437
  try {
9674
8438
  const stats = statSync3(stderrPath);
@@ -10310,8 +9074,8 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
10310
9074
  }
10311
9075
  const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
10312
9076
  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}
9077
+ const specialistsDir = resolve5(accountDir, "specialists");
9078
+ if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnConvId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
10315
9079
  `);
10316
9080
  const args = [
10317
9081
  "--print",
@@ -10661,8 +9425,8 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
10661
9425
  `);
10662
9426
  const managedUserId = getUserIdForSession(sessionKey);
10663
9427
  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}
9428
+ const specialistsDir = resolve5(accountDir, "specialists");
9429
+ if (!existsSync5(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
10666
9430
  `);
10667
9431
  const fullMessage = attachments.length > 0 ? message + buildAttachmentMetaText(attachments) : message;
10668
9432
  const args = [
@@ -11355,10 +10119,10 @@ ${sessionContext}`;
11355
10119
  console.log(`[onboarding-inject] accountId=${accountId.slice(0, 8)}\u2026 error=neo4j-unreachable injected=false`);
11356
10120
  } else if (onboardingStep < 8) {
11357
10121
  const GENERIC_FALLBACK = "At every session start, call `onboarding-get`. If `currentStep` is less than 8, load the onboarding skill via `plugin-read` (find its path in the manifest under `admin`) and follow it \u2014 before any business setup. If `onboarding-get` fails (Neo4j unreachable), tell the user and skip onboarding for this session \u2014 it resumes automatically when the graph is available.";
11358
- const skillPath = resolve8(PLATFORM_ROOT4, "plugins/admin/skills/onboarding/SKILL.md");
10122
+ const skillPath = resolve5(PLATFORM_ROOT3, "plugins/admin/skills/onboarding/SKILL.md");
11359
10123
  let skillContent = "";
11360
10124
  try {
11361
- skillContent = readFileSync8(skillPath, "utf-8");
10125
+ skillContent = readFileSync6(skillPath, "utf-8");
11362
10126
  } catch (err) {
11363
10127
  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
10128
  }
@@ -11408,9 +10172,9 @@ ${body}`;
11408
10172
 
11409
10173
  ${manifest}`;
11410
10174
  }
11411
- const graphRefPath = resolve8(PLATFORM_ROOT4, "plugins/memory/references/graph-primitives.md");
10175
+ const graphRefPath = resolve5(PLATFORM_ROOT3, "plugins/memory/references/graph-primitives.md");
11412
10176
  try {
11413
- const graphRef = readFileSync8(graphRefPath, "utf-8");
10177
+ const graphRef = readFileSync6(graphRefPath, "utf-8");
11414
10178
  baseSystemPrompt += `
11415
10179
 
11416
10180
  ${graphRef}`;
@@ -11625,8 +10389,8 @@ var clientIpMiddleware = async (c, next) => {
11625
10389
  };
11626
10390
 
11627
10391
  // server/routes/health.ts
11628
- import { existsSync as existsSync12, readFileSync as readFileSync13 } from "fs";
11629
- import { createConnection as createConnection4 } from "net";
10392
+ import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
10393
+ import { createConnection as createConnection2 } from "net";
11630
10394
 
11631
10395
  // app/lib/network.ts
11632
10396
  import { networkInterfaces } from "os";
@@ -11650,8 +10414,8 @@ function getLanIp() {
11650
10414
  import { basename as basename2 } from "path";
11651
10415
 
11652
10416
  // 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";
10417
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, statSync as statSync4, mkdirSync as mkdirSync5, renameSync as renameSync2 } from "fs";
10418
+ import { resolve as resolve6, dirname as dirname3 } from "path";
11655
10419
  var DEFAULT_SCAN_INTERVAL_MS = 5e3;
11656
10420
  var RATE_LIMIT_PATTERN = "rate[- ]?limit(?:ed| reached| hit)|(?:HTTP|status)[^a-z]{0,3}429|too many requests";
11657
10421
  var RATE_LIMIT_PATTERN_V1 = "\\b429\\b|rate.?limit|too.?many.?requests";
@@ -12080,12 +10844,12 @@ function defaultRules() {
12080
10844
  ];
12081
10845
  }
12082
10846
  function rulesFilePath(configDir2) {
12083
- return resolve9(configDir2, "review-rules.json");
10847
+ return resolve6(configDir2, "review-rules.json");
12084
10848
  }
12085
10849
  function ensureRulesFile(configDir2) {
12086
10850
  const path2 = rulesFilePath(configDir2);
12087
- if (existsSync8(path2)) return { created: false, path: path2 };
12088
- mkdirSync7(dirname3(path2), { recursive: true });
10851
+ if (existsSync6(path2)) return { created: false, path: path2 };
10852
+ mkdirSync5(dirname3(path2), { recursive: true });
12089
10853
  const body = {
12090
10854
  scanIntervalMs: DEFAULT_SCAN_INTERVAL_MS,
12091
10855
  rules: defaultRules()
@@ -12095,10 +10859,10 @@ function ensureRulesFile(configDir2) {
12095
10859
  }
12096
10860
  function loadRules(configDir2) {
12097
10861
  const path2 = rulesFilePath(configDir2);
12098
- if (!existsSync8(path2)) {
10862
+ if (!existsSync6(path2)) {
12099
10863
  throw new Error(`rules file missing at ${path2}`);
12100
10864
  }
12101
- const raw2 = readFileSync9(path2, "utf-8");
10865
+ const raw2 = readFileSync7(path2, "utf-8");
12102
10866
  let parsed;
12103
10867
  try {
12104
10868
  parsed = JSON.parse(raw2);
@@ -12121,7 +10885,7 @@ function saveRules(configDir2, file) {
12121
10885
  }
12122
10886
  function atomicWriteJson(path2, body) {
12123
10887
  const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
12124
- writeFileSync7(tmp, JSON.stringify(body, null, 2) + "\n", "utf-8");
10888
+ writeFileSync6(tmp, JSON.stringify(body, null, 2) + "\n", "utf-8");
12125
10889
  renameSync2(tmp, path2);
12126
10890
  }
12127
10891
  function validateRulesFile(input, sourceLabel) {
@@ -12263,16 +11027,16 @@ function validateRule(input, label, seenIds) {
12263
11027
  }
12264
11028
 
12265
11029
  // 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";
11030
+ import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync3, mkdirSync as mkdirSync6, openSync as openSync3, readSync as readSync3, closeSync as closeSync3, readFileSync as readFileSync8 } from "fs";
11031
+ import { resolve as resolve7, join as join4, basename, dirname as dirname4 } from "path";
12268
11032
  function tailStatePath(configDir2) {
12269
- return resolve10(configDir2, "review-state.json");
11033
+ return resolve7(configDir2, "review-state.json");
12270
11034
  }
12271
11035
  function loadTailState(configDir2) {
12272
11036
  const path2 = tailStatePath(configDir2);
12273
- if (!existsSync9(path2)) return {};
11037
+ if (!existsSync7(path2)) return {};
12274
11038
  try {
12275
- const raw2 = readFileSync10(path2, "utf-8");
11039
+ const raw2 = readFileSync8(path2, "utf-8");
12276
11040
  const parsed = JSON.parse(raw2);
12277
11041
  if (!parsed || typeof parsed !== "object") return {};
12278
11042
  const clean = {};
@@ -12290,26 +11054,26 @@ function loadTailState(configDir2) {
12290
11054
  }
12291
11055
  function saveTailState(configDir2, state) {
12292
11056
  const path2 = tailStatePath(configDir2);
12293
- mkdirSync8(dirname4(path2), { recursive: true });
11057
+ mkdirSync6(dirname4(path2), { recursive: true });
12294
11058
  const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
12295
- writeFileSync8(tmp, JSON.stringify(state, null, 2) + "\n", "utf-8");
11059
+ writeFileSync7(tmp, JSON.stringify(state, null, 2) + "\n", "utf-8");
12296
11060
  renameSync3(tmp, path2);
12297
11061
  }
12298
11062
  function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
12299
11063
  if (logicalSource === "server") {
12300
- const p = resolve10(configDir2, "logs", "server.log");
12301
- return existsSync9(p) ? [{ logicalSource: "server", filepath: p }] : [];
11064
+ const p = resolve7(configDir2, "logs", "server.log");
11065
+ return existsSync7(p) ? [{ logicalSource: "server", filepath: p }] : [];
12302
11066
  }
12303
11067
  if (logicalSource === "vnc") {
12304
- const p = resolve10(configDir2, "logs", "vnc-boot.log");
12305
- return existsSync9(p) ? [{ logicalSource: "vnc", filepath: p }] : [];
11068
+ const p = resolve7(configDir2, "logs", "vnc-boot.log");
11069
+ return existsSync7(p) ? [{ logicalSource: "vnc", filepath: p }] : [];
12306
11070
  }
12307
11071
  if (logicalSource === "cloudflared") {
12308
11072
  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 });
11073
+ const daemon = resolve7(configDir2, "logs", "cloudflared.log");
11074
+ if (existsSync7(daemon)) files2.push({ logicalSource: "cloudflared", filepath: daemon });
11075
+ const login = resolve7(configDir2, "logs", "cloudflared-login.log");
11076
+ if (existsSync7(login)) files2.push({ logicalSource: "cloudflared", filepath: login });
12313
11077
  return files2;
12314
11078
  }
12315
11079
  const prefix = {
@@ -12319,7 +11083,7 @@ function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
12319
11083
  public: "public-agent-stream-",
12320
11084
  mcp: "mcp-"
12321
11085
  }[logicalSource];
12322
- if (!existsSync9(accountLogDir2)) return [];
11086
+ if (!existsSync7(accountLogDir2)) return [];
12323
11087
  const files = [];
12324
11088
  let scanned = 0;
12325
11089
  let skippedPrefixMismatch = 0;
@@ -12329,7 +11093,7 @@ function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
12329
11093
  const matchesPrefix = entry.startsWith(prefix);
12330
11094
  const isLog = entry.endsWith(".log");
12331
11095
  if (matchesPrefix && isLog) {
12332
- files.push({ logicalSource, filepath: join5(accountLogDir2, entry) });
11096
+ files.push({ logicalSource, filepath: join4(accountLogDir2, entry) });
12333
11097
  } else if (!matchesPrefix) {
12334
11098
  skippedPrefixMismatch += 1;
12335
11099
  } else {
@@ -12361,7 +11125,7 @@ function discoverAllSources(configDir2, accountLogDir2) {
12361
11125
  ];
12362
11126
  }
12363
11127
  function readNewLines(filepath, prev) {
12364
- if (!existsSync9(filepath)) return null;
11128
+ if (!existsSync7(filepath)) return null;
12365
11129
  const stat5 = statSync5(filepath);
12366
11130
  const size = stat5.size;
12367
11131
  const inode = stat5.ino;
@@ -12414,12 +11178,12 @@ function readNewLines(filepath, prev) {
12414
11178
  }
12415
11179
  }
12416
11180
  function countRecentWrites(dir, sinceMs) {
12417
- if (!existsSync9(dir)) return 0;
11181
+ if (!existsSync7(dir)) return 0;
12418
11182
  let count = 0;
12419
11183
  for (const entry of readdirSync3(dir, { withFileTypes: true })) {
12420
11184
  if (!entry.isFile()) continue;
12421
11185
  try {
12422
- const st = statSync5(join5(dir, entry.name));
11186
+ const st = statSync5(join4(dir, entry.name));
12423
11187
  if (st.mtimeMs >= sinceMs) count += 1;
12424
11188
  } catch {
12425
11189
  }
@@ -12427,7 +11191,7 @@ function countRecentWrites(dir, sinceMs) {
12427
11191
  return count;
12428
11192
  }
12429
11193
  function fileLastWriteMs(path2) {
12430
- if (!existsSync9(path2)) return null;
11194
+ if (!existsSync7(path2)) return null;
12431
11195
  try {
12432
11196
  return statSync5(path2).mtimeMs;
12433
11197
  } catch {
@@ -12435,31 +11199,31 @@ function fileLastWriteMs(path2) {
12435
11199
  }
12436
11200
  }
12437
11201
  function accountLogDir(accountDir) {
12438
- return resolve10(accountDir, "logs");
11202
+ return resolve7(accountDir, "logs");
12439
11203
  }
12440
11204
  function sourceKey(file) {
12441
11205
  return `${file.logicalSource}:${basename(file.filepath)}`;
12442
11206
  }
12443
11207
 
12444
11208
  // 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";
11209
+ import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync8, renameSync as renameSync4, statSync as statSync6 } from "fs";
11210
+ import { resolve as resolve8, dirname as dirname5 } from "path";
12447
11211
  import { randomUUID as randomUUID3 } from "crypto";
12448
11212
  function reviewLogPath(configDir2) {
12449
- return resolve11(configDir2, "logs", "review.log");
11213
+ return resolve8(configDir2, "logs", "review.log");
12450
11214
  }
12451
11215
  function pendingAlertsPath(configDir2) {
12452
- return resolve11(configDir2, "review-pending-alerts.jsonl");
11216
+ return resolve8(configDir2, "review-pending-alerts.jsonl");
12453
11217
  }
12454
11218
  function reviewLog(configDir2, event) {
12455
11219
  const path2 = reviewLogPath(configDir2);
12456
11220
  try {
12457
- mkdirSync9(dirname5(path2), { recursive: true });
11221
+ mkdirSync7(dirname5(path2), { recursive: true });
12458
11222
  const line = `${new Date(
12459
11223
  typeof event.ts === "number" ? event.ts : Date.now()
12460
11224
  ).toISOString()} [review] ${JSON.stringify(event)}
12461
11225
  `;
12462
- appendFileSync4(path2, line, "utf-8");
11226
+ appendFileSync2(path2, line, "utf-8");
12463
11227
  } catch (err) {
12464
11228
  console.error(`[review] failed to write review log at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
12465
11229
  }
@@ -12571,17 +11335,17 @@ async function upsertReviewAlert(accountId, match2) {
12571
11335
  function queueAlert(configDir2, accountId, match2) {
12572
11336
  const path2 = pendingAlertsPath(configDir2);
12573
11337
  try {
12574
- mkdirSync9(dirname5(path2), { recursive: true });
11338
+ mkdirSync7(dirname5(path2), { recursive: true });
12575
11339
  const line = JSON.stringify({ accountId, match: match2 }) + "\n";
12576
- appendFileSync4(path2, line, "utf-8");
11340
+ appendFileSync2(path2, line, "utf-8");
12577
11341
  } catch (err) {
12578
11342
  console.error(`[review] failed to queue alert at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
12579
11343
  }
12580
11344
  }
12581
11345
  async function drainPendingAlerts(configDir2) {
12582
11346
  const path2 = pendingAlertsPath(configDir2);
12583
- if (!existsSync10(path2)) return { drained: 0, remaining: 0 };
12584
- const raw2 = readFileSync11(path2, "utf-8");
11347
+ if (!existsSync8(path2)) return { drained: 0, remaining: 0 };
11348
+ const raw2 = readFileSync9(path2, "utf-8");
12585
11349
  const lines = raw2.split("\n").filter((l) => l.trim().length > 0);
12586
11350
  if (lines.length === 0) return { drained: 0, remaining: 0 };
12587
11351
  const remaining = [];
@@ -12602,9 +11366,9 @@ async function drainPendingAlerts(configDir2) {
12602
11366
  }
12603
11367
  const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
12604
11368
  if (remaining.length > 0) {
12605
- writeFileSync9(tmp, remaining.join("\n") + "\n", "utf-8");
11369
+ writeFileSync8(tmp, remaining.join("\n") + "\n", "utf-8");
12606
11370
  } else {
12607
- writeFileSync9(tmp, "", "utf-8");
11371
+ writeFileSync8(tmp, "", "utf-8");
12608
11372
  }
12609
11373
  renameSync4(tmp, path2);
12610
11374
  return { drained, remaining: remaining.length };
@@ -12704,7 +11468,7 @@ async function bootDetector() {
12704
11468
  }
12705
11469
 
12706
11470
  // app/lib/review-detector/scan-loop.ts
12707
- import { resolve as resolve12 } from "path";
11471
+ import { resolve as resolve9 } from "path";
12708
11472
 
12709
11473
  // app/lib/review-detector/evaluator.ts
12710
11474
  var SAMPLE_MAX_CHARS = 500;
@@ -12998,14 +11762,14 @@ async function runScanCycle(runtime) {
12998
11762
  match2 = result.match;
12999
11763
  }
13000
11764
  } else if (rule.type === "file-write-storm") {
13001
- const dir = resolve12(runtime.configDir, rule.watchPath ?? "");
11765
+ const dir = resolve9(runtime.configDir, rule.watchPath ?? "");
13002
11766
  const sinceMs = cycleStart - rule.thresholdWindowMinutes * 6e4;
13003
11767
  const count = countRecentWrites(dir, sinceMs);
13004
11768
  const result = evaluateFileWriteStormRule(rule, count, state, cycleStart);
13005
11769
  state = result.state;
13006
11770
  match2 = result.match;
13007
11771
  } else if (rule.type === "stale-log") {
13008
- const trackedPath = resolve12(runtime.configDir, rule.watchPath ?? "");
11772
+ const trackedPath = resolve9(runtime.configDir, rule.watchPath ?? "");
13009
11773
  const lastMs = fileLastWriteMs(trackedPath);
13010
11774
  const result = evaluateStaleLogRule(rule, lastMs, state, cycleStart);
13011
11775
  state = result.state;
@@ -13244,20 +12008,20 @@ var WhatsAppConfigSchema = z.object({
13244
12008
  });
13245
12009
 
13246
12010
  // 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";
12011
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "fs";
12012
+ import { resolve as resolve10, join as join5 } from "path";
13249
12013
  var TAG3 = "[whatsapp:config]";
13250
12014
  function configPath(accountDir) {
13251
- return resolve13(accountDir, "account.json");
12015
+ return resolve10(accountDir, "account.json");
13252
12016
  }
13253
12017
  function readConfig(accountDir) {
13254
12018
  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"));
12019
+ if (!existsSync9(path2)) throw new Error(`account.json not found at ${path2}`);
12020
+ return JSON.parse(readFileSync10(path2, "utf-8"));
13257
12021
  }
13258
12022
  function writeConfig(accountDir, config) {
13259
12023
  const path2 = configPath(accountDir);
13260
- writeFileSync10(path2, JSON.stringify(config, null, 2) + "\n", "utf-8");
12024
+ writeFileSync9(path2, JSON.stringify(config, null, 2) + "\n", "utf-8");
13261
12025
  }
13262
12026
  function reloadManagerConfig(accountDir) {
13263
12027
  try {
@@ -13427,8 +12191,8 @@ function setPublicAgent(accountDir, slug) {
13427
12191
  if (!trimmed) {
13428
12192
  return { ok: false, error: "Agent slug cannot be empty." };
13429
12193
  }
13430
- const agentConfigPath = join6(accountDir, "agents", trimmed, "config.json");
13431
- if (!existsSync11(agentConfigPath)) {
12194
+ const agentConfigPath = join5(accountDir, "agents", trimmed, "config.json");
12195
+ if (!existsSync9(agentConfigPath)) {
13432
12196
  return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
13433
12197
  }
13434
12198
  try {
@@ -13703,7 +12467,7 @@ var credsSaveQueue = Promise.resolve();
13703
12467
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
13704
12468
  console.error(`${TAG5} draining credential save queue\u2026`);
13705
12469
  const timer = new Promise(
13706
- (resolve31) => setTimeout(() => resolve31("timeout"), timeoutMs)
12470
+ (resolve28) => setTimeout(() => resolve28("timeout"), timeoutMs)
13707
12471
  );
13708
12472
  const result = await Promise.race([
13709
12473
  credsSaveQueue.then(() => "drained"),
@@ -13831,11 +12595,11 @@ async function createWaSocket(opts) {
13831
12595
  return sock;
13832
12596
  }
13833
12597
  async function waitForConnection(sock) {
13834
- return new Promise((resolve31, reject) => {
12598
+ return new Promise((resolve28, reject) => {
13835
12599
  const handler = (update) => {
13836
12600
  if (update.connection === "open") {
13837
12601
  sock.ev.off("connection.update", handler);
13838
- resolve31();
12602
+ resolve28();
13839
12603
  }
13840
12604
  if (update.connection === "close") {
13841
12605
  sock.ev.off("connection.update", handler);
@@ -13949,14 +12713,14 @@ ${inspected}`;
13949
12713
  return inspect2(err, INSPECT_OPTS2);
13950
12714
  }
13951
12715
  function withTimeout(label, promise, timeoutMs) {
13952
- return new Promise((resolve31, reject) => {
12716
+ return new Promise((resolve28, reject) => {
13953
12717
  const timer = setTimeout(() => {
13954
12718
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
13955
12719
  }, timeoutMs);
13956
12720
  promise.then(
13957
12721
  (value) => {
13958
12722
  clearTimeout(timer);
13959
- resolve31(value);
12723
+ resolve28(value);
13960
12724
  },
13961
12725
  (err) => {
13962
12726
  clearTimeout(timer);
@@ -14468,7 +13232,7 @@ async function sendMediaMessage(sock, to, media, opts) {
14468
13232
  // app/lib/whatsapp/inbound/media.ts
14469
13233
  import { randomUUID as randomUUID5 } from "crypto";
14470
13234
  import { writeFile, mkdir } from "fs/promises";
14471
- import { join as join7 } from "path";
13235
+ import { join as join6 } from "path";
14472
13236
  import {
14473
13237
  downloadMediaMessage,
14474
13238
  downloadContentFromMessage,
@@ -14554,7 +13318,7 @@ async function downloadInboundMedia(msg, sock, opts) {
14554
13318
  await mkdir(MEDIA_DIR, { recursive: true });
14555
13319
  const ext = mimeToExt(mimetype ?? "application/octet-stream");
14556
13320
  const filename = `${randomUUID5()}.${ext}`;
14557
- const filePath = join7(MEDIA_DIR, filename);
13321
+ const filePath = join6(MEDIA_DIR, filename);
14558
13322
  await writeFile(filePath, buffer);
14559
13323
  const sizeKB = (buffer.length / 1024).toFixed(0);
14560
13324
  console.error(`${TAG9} media downloaded type=${mimetype ?? "unknown"} size=${sizeKB}KB path=${filePath}`);
@@ -14898,6 +13662,14 @@ function deriveSessionKey(input) {
14898
13662
  if (input.isOwnerMirror || input.agentType === "admin") {
14899
13663
  return `whatsapp:${input.accountId}`;
14900
13664
  }
13665
+ if (input.isGroup) {
13666
+ if (!input.groupJid) {
13667
+ throw new Error(
13668
+ `deriveSessionKey: isGroup=true requires groupJid (accountId=${input.accountId}, senderPhone=${input.senderPhone})`
13669
+ );
13670
+ }
13671
+ return `whatsapp:${input.accountId}:group:${input.groupJid}`;
13672
+ }
14901
13673
  return `whatsapp:${input.accountId}:${input.senderPhone}`;
14902
13674
  }
14903
13675
  async function init(opts) {
@@ -15162,11 +13934,11 @@ async function connectWithReconnect(conn) {
15162
13934
  console.error(
15163
13935
  `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
15164
13936
  );
15165
- await new Promise((resolve31) => {
15166
- const timer = setTimeout(resolve31, delay);
13937
+ await new Promise((resolve28) => {
13938
+ const timer = setTimeout(resolve28, delay);
15167
13939
  conn.abortController.signal.addEventListener("abort", () => {
15168
13940
  clearTimeout(timer);
15169
- resolve31();
13941
+ resolve28();
15170
13942
  }, { once: true });
15171
13943
  });
15172
13944
  }
@@ -15174,16 +13946,16 @@ async function connectWithReconnect(conn) {
15174
13946
  }
15175
13947
  }
15176
13948
  function waitForDisconnectEvent(conn) {
15177
- return new Promise((resolve31) => {
13949
+ return new Promise((resolve28) => {
15178
13950
  if (!conn.sock) {
15179
- resolve31();
13951
+ resolve28();
15180
13952
  return;
15181
13953
  }
15182
13954
  const sock = conn.sock;
15183
13955
  const handler = (update) => {
15184
13956
  if (update.connection === "close") {
15185
13957
  sock.ev.off("connection.update", handler);
15186
- resolve31();
13958
+ resolve28();
15187
13959
  }
15188
13960
  };
15189
13961
  sock.ev.on("connection.update", handler);
@@ -15369,6 +14141,8 @@ async function handleInboundMessage(conn, msg) {
15369
14141
  agentType: "admin",
15370
14142
  accountId: conn.accountId,
15371
14143
  senderPhone: senderPhone2,
14144
+ isGroup: isGroup2,
14145
+ groupJid: isGroup2 ? remoteJid : void 0,
15372
14146
  isOwnerMirror: true
15373
14147
  }),
15374
14148
  isOwnerMirror: true
@@ -15398,8 +14172,8 @@ async function handleInboundMessage(conn, msg) {
15398
14172
  const conversationKey = isGroup ? remoteJid : senderPhone;
15399
14173
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
15400
14174
  let resolvePending;
15401
- const sttPending = new Promise((resolve31) => {
15402
- resolvePending = resolve31;
14175
+ const sttPending = new Promise((resolve28) => {
14176
+ resolvePending = resolve28;
15403
14177
  });
15404
14178
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
15405
14179
  try {
@@ -15452,7 +14226,9 @@ async function handleInboundMessage(conn, msg) {
15452
14226
  const sessionKey = deriveSessionKey({
15453
14227
  agentType: accessResult.agentType,
15454
14228
  accountId: conn.accountId,
15455
- senderPhone
14229
+ senderPhone,
14230
+ isGroup,
14231
+ groupJid: isGroup ? remoteJid : void 0
15456
14232
  });
15457
14233
  conn.lastMessageAt = Date.now();
15458
14234
  const payload = {
@@ -15510,44 +14286,30 @@ async function probeApiKey() {
15510
14286
  return result.status;
15511
14287
  }
15512
14288
  function checkPort(port2, timeoutMs = 500) {
15513
- return new Promise((resolve31) => {
15514
- const socket = createConnection4(port2, "127.0.0.1");
14289
+ return new Promise((resolve28) => {
14290
+ const socket = createConnection2(port2, "127.0.0.1");
15515
14291
  socket.setTimeout(timeoutMs);
15516
14292
  socket.once("connect", () => {
15517
14293
  socket.destroy();
15518
- resolve31(true);
14294
+ resolve28(true);
15519
14295
  });
15520
14296
  socket.once("error", () => {
15521
14297
  socket.destroy();
15522
- resolve31(false);
14298
+ resolve28(false);
15523
14299
  });
15524
14300
  socket.once("timeout", () => {
15525
14301
  socket.destroy();
15526
- resolve31(false);
14302
+ resolve28(false);
15527
14303
  });
15528
14304
  });
15529
14305
  }
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
14306
  var app = new Hono2();
15545
14307
  app.get("/", async (c) => {
15546
14308
  const browserTransport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
15547
14309
  let pinConfigured = false;
15548
14310
  try {
15549
- if (existsSync12(USERS_FILE)) {
15550
- const raw2 = readFileSync13(USERS_FILE, "utf-8").trim();
14311
+ if (existsSync10(USERS_FILE)) {
14312
+ const raw2 = readFileSync11(USERS_FILE, "utf-8").trim();
15551
14313
  if (raw2) {
15552
14314
  const users = JSON.parse(raw2);
15553
14315
  pinConfigured = Array.isArray(users) && users.length > 0;
@@ -15564,10 +14326,9 @@ app.get("/", async (c) => {
15564
14326
  }
15565
14327
  const claudeAuthenticated = authHealth.status === "ok" || authHealth.status === "expiring";
15566
14328
  const vncRunning = await checkPort(6080);
15567
- const terminalReady = await probeTerminalReady();
15568
14329
  let apiKeyConfigured = false;
15569
14330
  try {
15570
- apiKeyConfigured = existsSync12(keyFilePath());
14331
+ apiKeyConfigured = existsSync10(keyFilePath());
15571
14332
  } catch {
15572
14333
  }
15573
14334
  let apiKeyStatus = "missing";
@@ -15608,7 +14369,6 @@ app.get("/", async (c) => {
15608
14369
  claude_authenticated: claudeAuthenticated,
15609
14370
  ...onboardingComplete !== void 0 && { onboarding_complete: onboardingComplete },
15610
14371
  vnc_running: vncRunning,
15611
- terminal_ready: terminalReady,
15612
14372
  browser_transport: browserTransport,
15613
14373
  auth_status: authHealth.status,
15614
14374
  auth_expires_at: authHealth.expiresAt ?? null,
@@ -15636,14 +14396,14 @@ app.get("/", async (c) => {
15636
14396
  var health_default = app;
15637
14397
 
15638
14398
  // server/routes/session.ts
15639
- import { resolve as resolve14 } from "path";
15640
- import { existsSync as existsSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10 } from "fs";
14399
+ import { resolve as resolve11 } from "path";
14400
+ import { existsSync as existsSync11, writeFileSync as writeFileSync10, mkdirSync as mkdirSync8 } from "fs";
15641
14401
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
15642
14402
  function writeBrandingCache(accountId, agentSlug, branding) {
15643
14403
  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");
14404
+ const cacheDir = resolve11(MAXY_DIR, "branding-cache", accountId);
14405
+ mkdirSync8(cacheDir, { recursive: true });
14406
+ writeFileSync10(resolve11(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
15647
14407
  } catch (err) {
15648
14408
  console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
15649
14409
  }
@@ -15713,9 +14473,9 @@ app2.post("/", async (c) => {
15713
14473
  }
15714
14474
  let agentConfig = null;
15715
14475
  if (account) {
15716
- const agentDir = resolve14(account.accountDir, "agents", agentSlug);
15717
- const agentConfigPath = resolve14(agentDir, "config.json");
15718
- if (!existsSync13(agentDir) || !existsSync13(agentConfigPath)) {
14476
+ const agentDir = resolve11(account.accountDir, "agents", agentSlug);
14477
+ const agentConfigPath = resolve11(agentDir, "config.json");
14478
+ if (!existsSync11(agentDir) || !existsSync11(agentConfigPath)) {
15719
14479
  return c.json({ error: "Agent not found" }, 404);
15720
14480
  }
15721
14481
  agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
@@ -15961,9 +14721,9 @@ ${raw2}`;
15961
14721
  import { randomUUID as randomUUID6 } from "crypto";
15962
14722
  import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
15963
14723
  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");
14724
+ import { resolve as resolve12, extname, basename as basename3 } from "path";
14725
+ var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve12(process.cwd(), "../platform");
14726
+ var ATTACHMENTS_ROOT = resolve12(PLATFORM_ROOT4, "..", "data/uploads");
15967
14727
  var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
15968
14728
  "image/jpeg",
15969
14729
  "image/png",
@@ -15987,11 +14747,11 @@ function assertSupportedMime(mimeType) {
15987
14747
  }
15988
14748
  async function writeAttachment(scope, filename, mimeType, sizeBytes, buffer) {
15989
14749
  const attachmentId = randomUUID6();
15990
- const dir = resolve15(ATTACHMENTS_ROOT, scope, attachmentId);
14750
+ const dir = resolve12(ATTACHMENTS_ROOT, scope, attachmentId);
15991
14751
  await mkdir2(dir, { recursive: true });
15992
14752
  const ext = extname(filename) || "";
15993
- const storagePath = resolve15(dir, `${attachmentId}${ext}`);
15994
- const metaPath = resolve15(dir, `${attachmentId}.meta.json`);
14753
+ const storagePath = resolve12(dir, `${attachmentId}${ext}`);
14754
+ const metaPath = resolve12(dir, `${attachmentId}.meta.json`);
15995
14755
  const meta = {
15996
14756
  attachmentId,
15997
14757
  scope,
@@ -16066,7 +14826,7 @@ async function storeGeneratedFile(accountId, filePath) {
16066
14826
  // app/lib/stt/voice-note.ts
16067
14827
  import { writeFile as writeFile3, mkdtemp, rm } from "fs/promises";
16068
14828
  import { tmpdir } from "os";
16069
- import { join as join8 } from "path";
14829
+ import { join as join7 } from "path";
16070
14830
  var TAG14 = "[voice]";
16071
14831
  var AUDIO_MIME_TYPES = /* @__PURE__ */ new Set([
16072
14832
  "audio/ogg",
@@ -16104,9 +14864,9 @@ async function transcribeVoiceNote(file, source) {
16104
14864
  let tempDir;
16105
14865
  let tempPath;
16106
14866
  try {
16107
- tempDir = await mkdtemp(join8(tmpdir(), "voice-"));
14867
+ tempDir = await mkdtemp(join7(tmpdir(), "voice-"));
16108
14868
  const ext = audioExtension(mimeType);
16109
- tempPath = join8(tempDir, `recording${ext}`);
14869
+ tempPath = join7(tempDir, `recording${ext}`);
16110
14870
  const buffer = Buffer.from(await file.arrayBuffer());
16111
14871
  await writeFile3(tempPath, buffer);
16112
14872
  } catch (err) {
@@ -16668,16 +15428,16 @@ var group_default = app4;
16668
15428
 
16669
15429
  // app/lib/access-gate.ts
16670
15430
  import neo4j2 from "neo4j-driver";
16671
- import { readFileSync as readFileSync14 } from "fs";
16672
- import { resolve as resolve16 } from "path";
15431
+ import { readFileSync as readFileSync12 } from "fs";
15432
+ import { resolve as resolve13 } from "path";
16673
15433
  import { randomUUID as randomUUID7, randomInt } from "crypto";
16674
- var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
15434
+ var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
16675
15435
  var driver2 = null;
16676
15436
  function readPassword2() {
16677
15437
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
16678
- const passwordFile = resolve16(PLATFORM_ROOT6, "config/.neo4j-password");
15438
+ const passwordFile = resolve13(PLATFORM_ROOT5, "config/.neo4j-password");
16679
15439
  try {
16680
- return readFileSync14(passwordFile, "utf-8").trim();
15440
+ return readFileSync12(passwordFile, "utf-8").trim();
16681
15441
  } catch {
16682
15442
  throw new Error(
16683
15443
  `Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
@@ -16710,13 +15470,13 @@ process.on("SIGINT", async () => {
16710
15470
  driver2 = null;
16711
15471
  }
16712
15472
  });
16713
- var rateLimitMap2 = /* @__PURE__ */ new Map();
15473
+ var rateLimitMap = /* @__PURE__ */ new Map();
16714
15474
  var ACCESS_MAX_ATTEMPTS = 5;
16715
15475
  var ACCESS_LOCKOUT_MS = 15 * 60 * 1e3;
16716
15476
  var ACCESS_WINDOW_MS = 15 * 60 * 1e3;
16717
15477
  function checkAccessRateLimit(ip, agentSlug) {
16718
15478
  const key = `${ip}:${agentSlug}`;
16719
- const entry = rateLimitMap2.get(key);
15479
+ const entry = rateLimitMap.get(key);
16720
15480
  if (!entry) return null;
16721
15481
  const now = Date.now();
16722
15482
  if (entry.lockedUntil && now < entry.lockedUntil) {
@@ -16725,7 +15485,7 @@ function checkAccessRateLimit(ip, agentSlug) {
16725
15485
  return `Too many attempts. Try again in ${remainingS}s`;
16726
15486
  }
16727
15487
  if (now - entry.firstAttempt > ACCESS_WINDOW_MS) {
16728
- rateLimitMap2.delete(key);
15488
+ rateLimitMap.delete(key);
16729
15489
  return null;
16730
15490
  }
16731
15491
  return null;
@@ -16733,9 +15493,9 @@ function checkAccessRateLimit(ip, agentSlug) {
16733
15493
  function recordAccessFailedAttempt(ip, agentSlug) {
16734
15494
  const key = `${ip}:${agentSlug}`;
16735
15495
  const now = Date.now();
16736
- const entry = rateLimitMap2.get(key);
15496
+ const entry = rateLimitMap.get(key);
16737
15497
  if (!entry || now - entry.firstAttempt > ACCESS_WINDOW_MS) {
16738
- rateLimitMap2.set(key, { attempts: 1, firstAttempt: now });
15498
+ rateLimitMap.set(key, { attempts: 1, firstAttempt: now });
16739
15499
  return;
16740
15500
  }
16741
15501
  entry.attempts++;
@@ -16744,7 +15504,7 @@ function recordAccessFailedAttempt(ip, agentSlug) {
16744
15504
  }
16745
15505
  }
16746
15506
  function clearAccessRateLimit(ip, agentSlug) {
16747
- rateLimitMap2.delete(`${ip}:${agentSlug}`);
15507
+ rateLimitMap.delete(`${ip}:${agentSlug}`);
16748
15508
  }
16749
15509
  var forgotPwdMap = /* @__PURE__ */ new Map();
16750
15510
  var FORGOT_PWD_MAX = 3;
@@ -16988,19 +15748,19 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
16988
15748
  }
16989
15749
 
16990
15750
  // app/lib/brevo-sms.ts
16991
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync12, mkdirSync as mkdirSync11, existsSync as existsSync14, chmodSync } from "fs";
15751
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, existsSync as existsSync12, chmodSync } from "fs";
16992
15752
  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");
15753
+ import { resolve as resolve14 } from "path";
15754
+ var BREVO_API_KEY_FILE = resolve14(MAXY_DIR, ".brevo-api-key");
16995
15755
  var BREVO_API_URL = "https://api.brevo.com/v3/transactionalSMS/sms";
16996
15756
  var BREVO_TIMEOUT_MS = 1e4;
16997
15757
  var BREVO_SENDER = "Maxy";
16998
- var platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
16999
- if (platformRoot2) {
15758
+ var platformRoot = process.env.MAXY_PLATFORM_ROOT;
15759
+ if (platformRoot) {
17000
15760
  try {
17001
- const brandPath = resolve17(platformRoot2, "config", "brand.json");
17002
- if (existsSync14(brandPath)) {
17003
- const brand = JSON.parse(readFileSync15(brandPath, "utf-8"));
15761
+ const brandPath = resolve14(platformRoot, "config", "brand.json");
15762
+ if (existsSync12(brandPath)) {
15763
+ const brand = JSON.parse(readFileSync13(brandPath, "utf-8"));
17004
15764
  if (brand.productName) BREVO_SENDER = brand.productName;
17005
15765
  }
17006
15766
  } catch {
@@ -17008,7 +15768,7 @@ if (platformRoot2) {
17008
15768
  }
17009
15769
  function readBrevoApiKey() {
17010
15770
  try {
17011
- const key = readFileSync15(BREVO_API_KEY_FILE, "utf-8").trim();
15771
+ const key = readFileSync13(BREVO_API_KEY_FILE, "utf-8").trim();
17012
15772
  if (!key) {
17013
15773
  throw new Error(`Brevo API key file is empty: ${BREVO_API_KEY_FILE}`);
17014
15774
  }
@@ -17023,7 +15783,7 @@ function readBrevoApiKey() {
17023
15783
  }
17024
15784
  }
17025
15785
  function hasBrevoApiKey() {
17026
- return existsSync14(BREVO_API_KEY_FILE);
15786
+ return existsSync12(BREVO_API_KEY_FILE);
17027
15787
  }
17028
15788
  async function sendSms(recipient, content, opts) {
17029
15789
  let apiKey;
@@ -17395,8 +16155,8 @@ app5.post("/forgot-password", async (c) => {
17395
16155
  app5.post("/send-otp", async (c) => {
17396
16156
  const socketAddr = c.env?.incoming?.socket?.remoteAddress;
17397
16157
  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) {
16158
+ const isLoopback = socketAddr === "127.0.0.1" || socketAddr === "::1" || socketAddr === "::ffff:127.0.0.1";
16159
+ if (!isLoopback || hasXff) {
17400
16160
  console.error(`[access-gate] send-otp rejected: socket=${socketAddr ?? "unknown"} xff=${hasXff}`);
17401
16161
  return c.json({ error: "Forbidden" }, 403);
17402
16162
  }
@@ -17439,8 +16199,8 @@ app5.post("/send-otp", async (c) => {
17439
16199
  var access_default = app5;
17440
16200
 
17441
16201
  // server/routes/telegram.ts
17442
- import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
17443
- import { timingSafeEqual as timingSafeEqual2 } from "crypto";
16202
+ import { existsSync as existsSync13, readFileSync as readFileSync14 } from "fs";
16203
+ import { timingSafeEqual } from "crypto";
17444
16204
 
17445
16205
  // app/lib/telegram/access-control.ts
17446
16206
  function checkTelegramAccess(params) {
@@ -17476,8 +16236,8 @@ var TELEGRAM_API = "https://api.telegram.org";
17476
16236
  function getWebhookSecret(botType) {
17477
16237
  const filePath = botType === "admin" ? TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE : TELEGRAM_WEBHOOK_SECRET_FILE;
17478
16238
  try {
17479
- if (!existsSync15(filePath)) return null;
17480
- const secret = readFileSync16(filePath, "utf-8").trim();
16239
+ if (!existsSync13(filePath)) return null;
16240
+ const secret = readFileSync14(filePath, "utf-8").trim();
17481
16241
  return secret || null;
17482
16242
  } catch {
17483
16243
  return null;
@@ -17487,7 +16247,7 @@ function verifyWebhookSecret(headerValue, storedSecret) {
17487
16247
  const a = Buffer.from(headerValue);
17488
16248
  const b = Buffer.from(storedSecret);
17489
16249
  if (a.length !== b.length) return false;
17490
- return timingSafeEqual2(a, b);
16250
+ return timingSafeEqual(a, b);
17491
16251
  }
17492
16252
  async function handleInbound(params) {
17493
16253
  const { chatId, senderId, text, botType, botToken, accountId, agentType } = params;
@@ -17635,9 +16395,9 @@ app6.post("/webhook", async (c) => {
17635
16395
  var telegram_default = app6;
17636
16396
 
17637
16397
  // server/routes/whatsapp.ts
17638
- import { join as join9, resolve as resolve18, basename as basename4 } from "path";
16398
+ import { join as join8, resolve as resolve15, basename as basename4 } from "path";
17639
16399
  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";
16400
+ import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
17641
16401
 
17642
16402
  // app/lib/whatsapp/login.ts
17643
16403
  import { randomUUID as randomUUID8 } from "crypto";
@@ -17743,8 +16503,8 @@ async function startLogin(opts) {
17743
16503
  resetActiveLogin(accountId);
17744
16504
  let resolveQr = null;
17745
16505
  let rejectQr = null;
17746
- const qrPromise = new Promise((resolve31, reject) => {
17747
- resolveQr = resolve31;
16506
+ const qrPromise = new Promise((resolve28, reject) => {
16507
+ resolveQr = resolve28;
17748
16508
  rejectQr = reject;
17749
16509
  });
17750
16510
  const qrTimer = setTimeout(
@@ -17960,7 +16720,7 @@ function serializeWhatsAppSchema() {
17960
16720
 
17961
16721
  // server/routes/whatsapp.ts
17962
16722
  var TAG18 = "[whatsapp:api]";
17963
- var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
16723
+ var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT || "";
17964
16724
  var app7 = new Hono2();
17965
16725
  app7.get("/status", (c) => {
17966
16726
  try {
@@ -17978,7 +16738,7 @@ app7.post("/login/start", async (c) => {
17978
16738
  const body = await c.req.json().catch(() => ({}));
17979
16739
  const accountId = validateAccountId(body.accountId);
17980
16740
  const force = body.force ?? false;
17981
- const authDir = join9(MAXY_DIR, "credentials", "whatsapp", accountId);
16741
+ const authDir = join8(MAXY_DIR, "credentials", "whatsapp", accountId);
17982
16742
  const result = await startLogin({ accountId, authDir, force });
17983
16743
  console.error(`${TAG18} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
17984
16744
  return c.json(result);
@@ -18115,17 +16875,17 @@ app7.post("/config", async (c) => {
18115
16875
  return c.json({ ok: true, slug: currentSlug });
18116
16876
  }
18117
16877
  case "list-public-agents": {
18118
- const agentsDir = resolve18(account.accountDir, "agents");
16878
+ const agentsDir = resolve15(account.accountDir, "agents");
18119
16879
  const agents = [];
18120
- if (existsSync16(agentsDir)) {
16880
+ if (existsSync14(agentsDir)) {
18121
16881
  try {
18122
16882
  const entries = readdirSync4(agentsDir, { withFileTypes: true });
18123
16883
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
18124
16884
  if (!entry.isDirectory() || entry.name === "admin") continue;
18125
- const configPath2 = resolve18(agentsDir, entry.name, "config.json");
18126
- if (!existsSync16(configPath2)) continue;
16885
+ const configPath2 = resolve15(agentsDir, entry.name, "config.json");
16886
+ if (!existsSync14(configPath2)) continue;
18127
16887
  try {
18128
- const config = JSON.parse(readFileSync17(configPath2, "utf-8"));
16888
+ const config = JSON.parse(readFileSync15(configPath2, "utf-8"));
18129
16889
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
18130
16890
  } catch {
18131
16891
  console.error(`${TAG18} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
@@ -18197,10 +16957,10 @@ app7.post("/send-document", async (c) => {
18197
16957
  if (!to || !filePath) {
18198
16958
  return c.json({ error: "Missing required fields: to, filePath" }, 400);
18199
16959
  }
18200
- if (!maxyAccountId || !PLATFORM_ROOT7) {
16960
+ if (!maxyAccountId || !PLATFORM_ROOT6) {
18201
16961
  return c.json({ error: "Cannot validate file path: missing account or platform context" }, 400);
18202
16962
  }
18203
- const accountDir = resolve18(PLATFORM_ROOT7, "..", "data/accounts", maxyAccountId);
16963
+ const accountDir = resolve15(PLATFORM_ROOT6, "..", "data/accounts", maxyAccountId);
18204
16964
  let resolvedPath;
18205
16965
  try {
18206
16966
  resolvedPath = realpathSync2(filePath);
@@ -18337,16 +17097,16 @@ var whatsapp_default = app7;
18337
17097
 
18338
17098
  // server/routes/onboarding.ts
18339
17099
  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";
17100
+ import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync16, unlinkSync as unlinkSync4 } from "fs";
17101
+ import { resolve as resolve16, dirname as dirname7 } from "path";
18342
17102
  import { createHash, randomUUID as randomUUID9 } from "crypto";
18343
- var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
17103
+ var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
18344
17104
  function hashPin(pin) {
18345
17105
  return createHash("sha256").update(pin).digest("hex");
18346
17106
  }
18347
17107
  function readUsersFile() {
18348
- if (!existsSync17(USERS_FILE)) return null;
18349
- const raw2 = readFileSync18(USERS_FILE, "utf-8").trim();
17108
+ if (!existsSync15(USERS_FILE)) return null;
17109
+ const raw2 = readFileSync16(USERS_FILE, "utf-8").trim();
18350
17110
  if (!raw2) return [];
18351
17111
  return JSON.parse(raw2);
18352
17112
  }
@@ -18412,7 +17172,7 @@ app8.post("/claude-auth", async (c) => {
18412
17172
  if (!vncReady) return c.json({ error: "VNC display failed to start" }, 500);
18413
17173
  }
18414
17174
  await ensureCdp(transport);
18415
- writeFileSync13(logPath("claude-auth"), "");
17175
+ writeFileSync12(logPath("claude-auth"), "");
18416
17176
  const chromiumWrapper = writeChromiumWrapper();
18417
17177
  const x11Env = buildX11Env(chromiumWrapper, transport);
18418
17178
  vncLog("claude-auth", { action: "start", transport });
@@ -18451,17 +17211,17 @@ app8.post("/set-pin", async (c) => {
18451
17211
  }
18452
17212
  const hash = hashPin(body.pin);
18453
17213
  const userId = randomUUID9();
18454
- mkdirSync12(dirname7(USERS_FILE), { recursive: true });
18455
- writeFileSync13(USERS_FILE, JSON.stringify([{ userId, name: "Owner", pin: hash }]), { mode: 384 });
17214
+ mkdirSync10(dirname7(USERS_FILE), { recursive: true });
17215
+ writeFileSync12(USERS_FILE, JSON.stringify([{ userId, name: "Owner", pin: hash }]), { mode: 384 });
18456
17216
  console.log(`[set-pin] created users.json: userId=${userId.slice(0, 8)}\u2026 hash=${hash.slice(0, 8)}\u2026`);
18457
17217
  const account = resolveAccount();
18458
17218
  if (account) {
18459
17219
  try {
18460
- const config = JSON.parse(readFileSync18(`${account.accountDir}/account.json`, "utf-8"));
17220
+ const config = JSON.parse(readFileSync16(`${account.accountDir}/account.json`, "utf-8"));
18461
17221
  if (!config.admins) config.admins = [];
18462
17222
  if (!config.admins.some((a) => a.userId === userId)) {
18463
17223
  config.admins.push({ userId, role: "owner" });
18464
- writeFileSync13(`${account.accountDir}/account.json`, JSON.stringify(config, null, 2) + "\n");
17224
+ writeFileSync12(`${account.accountDir}/account.json`, JSON.stringify(config, null, 2) + "\n");
18465
17225
  console.log(`[set-pin] added userId=${userId.slice(0, 8)}\u2026 to account.json admins`);
18466
17226
  }
18467
17227
  } catch (err) {
@@ -18499,7 +17259,7 @@ app8.delete("/set-pin", async (c) => {
18499
17259
  unlinkSync4(USERS_FILE);
18500
17260
  console.log(`[set-pin] cleared users.json (last entry removed): userId=${matchedUser.userId.slice(0, 8)}\u2026`);
18501
17261
  } else {
18502
- writeFileSync13(USERS_FILE, JSON.stringify(remaining), { mode: 384 });
17262
+ writeFileSync12(USERS_FILE, JSON.stringify(remaining), { mode: 384 });
18503
17263
  console.log(`[set-pin] removed entry from users.json: userId=${matchedUser.userId.slice(0, 8)}\u2026 remaining=${remaining.length}`);
18504
17264
  }
18505
17265
  return c.json({ ok: true });
@@ -18512,19 +17272,19 @@ app8.post("/skip", async (c) => {
18512
17272
  }
18513
17273
  const { accountId, accountDir } = account;
18514
17274
  let agentName = "Maxy";
18515
- const brandPath = PLATFORM_ROOT8 ? resolve19(PLATFORM_ROOT8, "config", "brand.json") : "";
18516
- if (brandPath && existsSync17(brandPath)) {
17275
+ const brandPath = PLATFORM_ROOT7 ? resolve16(PLATFORM_ROOT7, "config", "brand.json") : "";
17276
+ if (brandPath && existsSync15(brandPath)) {
18517
17277
  try {
18518
- const brand = JSON.parse(readFileSync18(brandPath, "utf-8"));
17278
+ const brand = JSON.parse(readFileSync16(brandPath, "utf-8"));
18519
17279
  if (brand.productName) agentName = brand.productName;
18520
17280
  } catch (err) {
18521
17281
  console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
18522
17282
  }
18523
17283
  }
18524
- const soulPath = resolve19(accountDir, "agents", "admin", "SOUL.md");
17284
+ const soulPath = resolve16(accountDir, "agents", "admin", "SOUL.md");
18525
17285
  try {
18526
- mkdirSync12(dirname7(soulPath), { recursive: true });
18527
- writeFileSync13(soulPath, `You are ${agentName}, an AI operations manager.
17286
+ mkdirSync10(dirname7(soulPath), { recursive: true });
17287
+ writeFileSync12(soulPath, `You are ${agentName}, an AI operations manager.
18528
17288
  `);
18529
17289
  console.log(`[onboarding-skip] wrote SOUL.md: ${soulPath}`);
18530
17290
  } catch (err) {
@@ -18562,9 +17322,9 @@ app8.post("/skip", async (c) => {
18562
17322
  var onboarding_default = app8;
18563
17323
 
18564
17324
  // 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");
17325
+ import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as renameSync5, statSync as statSync7 } from "fs";
17326
+ import { join as join9 } from "path";
17327
+ var CLIENT_ERRORS_LOG = join9(LOG_DIR, "client-errors.log");
18568
17328
  var MAX_LOG_SIZE = 10 * 1024 * 1024;
18569
17329
  var MAX_BODY_SIZE = 8 * 1024;
18570
17330
  var MAX_STACK_LEN = 2e3;
@@ -18616,7 +17376,7 @@ function stackHead(stack) {
18616
17376
  }
18617
17377
  function rotateIfNeeded() {
18618
17378
  try {
18619
- if (!existsSync18(CLIENT_ERRORS_LOG)) return;
17379
+ if (!existsSync16(CLIENT_ERRORS_LOG)) return;
18620
17380
  const stats = statSync7(CLIENT_ERRORS_LOG);
18621
17381
  if (stats.size < MAX_LOG_SIZE) return;
18622
17382
  renameSync5(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
@@ -18699,7 +17459,7 @@ app9.post("/", async (c) => {
18699
17459
  tag: typeof body.tag === "string" ? truncate2(body.tag, 32) : void 0,
18700
17460
  status: typeof body.status === "number" ? body.status : void 0
18701
17461
  };
18702
- appendFileSync5(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
17462
+ appendFileSync3(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
18703
17463
  } catch (err) {
18704
17464
  console.error(`[client-error] append failed: ${err instanceof Error ? err.message : String(err)}`);
18705
17465
  }
@@ -18709,14 +17469,14 @@ app9.post("/", async (c) => {
18709
17469
  var client_error_default = app9;
18710
17470
 
18711
17471
  // server/routes/admin/session.ts
18712
- import { readFileSync as readFileSync19, existsSync as existsSync19 } from "fs";
17472
+ import { readFileSync as readFileSync17, existsSync as existsSync17 } from "fs";
18713
17473
  import { createHash as createHash2 } from "crypto";
18714
17474
  function hashPin2(pin) {
18715
17475
  return createHash2("sha256").update(pin).digest("hex");
18716
17476
  }
18717
17477
  function readUsersFile2() {
18718
- if (!existsSync19(USERS_FILE)) return null;
18719
- const raw2 = readFileSync19(USERS_FILE, "utf-8").trim();
17478
+ if (!existsSync17(USERS_FILE)) return null;
17479
+ const raw2 = readFileSync17(USERS_FILE, "utf-8").trim();
18720
17480
  if (!raw2) return [];
18721
17481
  return JSON.parse(raw2);
18722
17482
  }
@@ -18846,11 +17606,11 @@ app10.post("/", async (c) => {
18846
17606
  var session_default2 = app10;
18847
17607
 
18848
17608
  // server/routes/admin/chat.ts
18849
- import { resolve as resolve20 } from "path";
17609
+ import { resolve as resolve17 } from "path";
18850
17610
 
18851
17611
  // app/lib/script-stream-tailer.ts
18852
17612
  import * as childProcess from "child_process";
18853
- import { appendFileSync as appendFileSync6, createReadStream as createReadStream2, mkdirSync as mkdirSync13, statSync as statSync8 } from "fs";
17613
+ import { appendFileSync as appendFileSync4, createReadStream as createReadStream2, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
18854
17614
  import { dirname as dirname8 } from "path";
18855
17615
  import { StringDecoder as StringDecoder2 } from "string_decoder";
18856
17616
  var SCRIPT_STREAM_RE = /^\[([^\]]+)\] \[script:([a-z][a-z0-9-]*)((?::[a-z0-9:_-]+)?)\] (.*)$/;
@@ -18960,8 +17720,8 @@ function writeRouteMilestone(streamLogPath, scope, line) {
18960
17720
  }
18961
17721
  const ts = (/* @__PURE__ */ new Date()).toISOString();
18962
17722
  try {
18963
- mkdirSync13(dirname8(streamLogPath), { recursive: true });
18964
- appendFileSync6(streamLogPath, `[${ts}] [script:${scope}] ${line}
17723
+ mkdirSync11(dirname8(streamLogPath), { recursive: true });
17724
+ appendFileSync4(streamLogPath, `[${ts}] [script:${scope}] ${line}
18965
17725
  `);
18966
17726
  } catch (err) {
18967
17727
  console.error(
@@ -19291,7 +18051,7 @@ app11.post("/", requireAdminSession, async (c) => {
19291
18051
  try {
19292
18052
  registerAdminSSE(sseEntry);
19293
18053
  if (sseConvId) {
19294
- const streamLogPath = resolve20(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
18054
+ const streamLogPath = resolve17(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
19295
18055
  tailer = startScriptStreamTailer({
19296
18056
  path: streamLogPath,
19297
18057
  onEvent: (event) => {
@@ -19416,8 +18176,8 @@ app12.post("/", requireAdminSession, async (c) => {
19416
18176
  var compact_default = app12;
19417
18177
 
19418
18178
  // 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";
18179
+ import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync18, statSync as statSync9 } from "fs";
18180
+ import { resolve as resolve18, basename as basename5 } from "path";
19421
18181
  var TAIL_BYTES = 8192;
19422
18182
  var app13 = new Hono2();
19423
18183
  app13.get("/", async (c) => {
@@ -19426,16 +18186,16 @@ app13.get("/", async (c) => {
19426
18186
  const conversationIdParam = c.req.query("conversationId");
19427
18187
  const download = c.req.query("download") === "1";
19428
18188
  const account = resolveAccount();
19429
- const accountLogDir2 = account ? resolve21(account.accountDir, "logs") : null;
18189
+ const accountLogDir2 = account ? resolve18(account.accountDir, "logs") : null;
19430
18190
  if (fileParam) {
19431
18191
  const safe = basename5(fileParam);
19432
18192
  const searched = [];
19433
18193
  for (const dir of [accountLogDir2, LOG_DIR]) {
19434
18194
  if (!dir) continue;
19435
- const filePath = resolve21(dir, safe);
18195
+ const filePath = resolve18(dir, safe);
19436
18196
  searched.push(filePath);
19437
18197
  try {
19438
- const content = readFileSync20(filePath, "utf-8");
18198
+ const content = readFileSync18(filePath, "utf-8");
19439
18199
  const headers = { "Content-Type": "text/plain; charset=utf-8" };
19440
18200
  if (download) headers["Content-Disposition"] = `attachment; filename="${safe}"`;
19441
18201
  return new Response(content, { headers });
@@ -19474,10 +18234,10 @@ app13.get("/", async (c) => {
19474
18234
  const searched = [];
19475
18235
  for (const dir of [accountLogDir2, LOG_DIR]) {
19476
18236
  if (!dir) continue;
19477
- const filePath = resolve21(dir, fileName);
18237
+ const filePath = resolve18(dir, fileName);
19478
18238
  searched.push(filePath);
19479
18239
  try {
19480
- const content = readFileSync20(filePath, "utf-8");
18240
+ const content = readFileSync18(filePath, "utf-8");
19481
18241
  const headers = { "Content-Type": "text/plain; charset=utf-8" };
19482
18242
  if (download) headers["Content-Disposition"] = `attachment; filename="${fileName}"`;
19483
18243
  return new Response(content, { headers });
@@ -19492,7 +18252,7 @@ app13.get("/", async (c) => {
19492
18252
  const seen = /* @__PURE__ */ new Set();
19493
18253
  const logs = {};
19494
18254
  for (const dir of [accountLogDir2, LOG_DIR]) {
19495
- if (!dir || !existsSync20(dir)) continue;
18255
+ if (!dir || !existsSync18(dir)) continue;
19496
18256
  let files;
19497
18257
  try {
19498
18258
  files = readdirSync5(dir).filter((f) => f.endsWith(".log"));
@@ -19501,10 +18261,10 @@ app13.get("/", async (c) => {
19501
18261
  console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
19502
18262
  continue;
19503
18263
  }
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 }) => {
18264
+ files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve18(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
19505
18265
  seen.add(name);
19506
18266
  try {
19507
- const content = readFileSync20(resolve21(dir, name));
18267
+ const content = readFileSync18(resolve18(dir, name));
19508
18268
  const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
19509
18269
  logs[name] = tail.trim() || "(empty)";
19510
18270
  } catch (err) {
@@ -19544,8 +18304,8 @@ var claude_info_default = app14;
19544
18304
 
19545
18305
  // server/routes/admin/attachment.ts
19546
18306
  import { readFile as readFile3, readdir } from "fs/promises";
19547
- import { existsSync as existsSync21 } from "fs";
19548
- import { resolve as resolve22 } from "path";
18307
+ import { existsSync as existsSync19 } from "fs";
18308
+ import { resolve as resolve19 } from "path";
19549
18309
  var app15 = new Hono2();
19550
18310
  app15.get("/:attachmentId", requireAdminSession, async (c) => {
19551
18311
  const attachmentId = c.req.param("attachmentId");
@@ -19557,12 +18317,12 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
19557
18317
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
19558
18318
  return new Response("Not found", { status: 404 });
19559
18319
  }
19560
- const dir = resolve22(ATTACHMENTS_ROOT, accountId, attachmentId);
19561
- if (!existsSync21(dir)) {
18320
+ const dir = resolve19(ATTACHMENTS_ROOT, accountId, attachmentId);
18321
+ if (!existsSync19(dir)) {
19562
18322
  return new Response("Not found", { status: 404 });
19563
18323
  }
19564
- const metaPath = resolve22(dir, `${attachmentId}.meta.json`);
19565
- if (!existsSync21(metaPath)) {
18324
+ const metaPath = resolve19(dir, `${attachmentId}.meta.json`);
18325
+ if (!existsSync19(metaPath)) {
19566
18326
  return new Response("Not found", { status: 404 });
19567
18327
  }
19568
18328
  let meta;
@@ -19576,7 +18336,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
19576
18336
  if (!dataFile) {
19577
18337
  return new Response("Not found", { status: 404 });
19578
18338
  }
19579
- const filePath = resolve22(dir, dataFile);
18339
+ const filePath = resolve19(dir, dataFile);
19580
18340
  const buffer = await readFile3(filePath);
19581
18341
  return new Response(new Uint8Array(buffer), {
19582
18342
  headers: {
@@ -19589,8 +18349,8 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
19589
18349
  var attachment_default = app15;
19590
18350
 
19591
18351
  // server/routes/admin/account.ts
19592
- import { readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
19593
- import { resolve as resolve23 } from "path";
18352
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
18353
+ import { resolve as resolve20 } from "path";
19594
18354
  var VALID_CONTEXT_MODES = ["managed", "claude-code"];
19595
18355
  var app16 = new Hono2();
19596
18356
  app16.patch("/", requireAdminSession, async (c) => {
@@ -19606,12 +18366,12 @@ app16.patch("/", requireAdminSession, async (c) => {
19606
18366
  }
19607
18367
  const account = resolveAccount();
19608
18368
  if (!account) return c.json({ error: "No account configured" }, 500);
19609
- const configPath2 = resolve23(account.accountDir, "account.json");
18369
+ const configPath2 = resolve20(account.accountDir, "account.json");
19610
18370
  try {
19611
- const raw2 = readFileSync21(configPath2, "utf-8");
18371
+ const raw2 = readFileSync19(configPath2, "utf-8");
19612
18372
  const config = JSON.parse(raw2);
19613
18373
  config.contextMode = contextMode;
19614
- writeFileSync14(configPath2, JSON.stringify(config, null, 2) + "\n", "utf-8");
18374
+ writeFileSync13(configPath2, JSON.stringify(config, null, 2) + "\n", "utf-8");
19615
18375
  console.error(`[account-update] contextMode=${contextMode}`);
19616
18376
  return c.json({ ok: true, contextMode });
19617
18377
  } catch (err) {
@@ -19622,24 +18382,24 @@ app16.patch("/", requireAdminSession, async (c) => {
19622
18382
  var account_default = app16;
19623
18383
 
19624
18384
  // 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";
18385
+ import { resolve as resolve21 } from "path";
18386
+ import { readdirSync as readdirSync6, readFileSync as readFileSync20, existsSync as existsSync20, rmSync as rmSync3 } from "fs";
19627
18387
  var app17 = new Hono2();
19628
18388
  app17.get("/", (c) => {
19629
18389
  const account = resolveAccount();
19630
18390
  if (!account) return c.json({ agents: [] });
19631
- const agentsDir = resolve24(account.accountDir, "agents");
19632
- if (!existsSync22(agentsDir)) return c.json({ agents: [] });
18391
+ const agentsDir = resolve21(account.accountDir, "agents");
18392
+ if (!existsSync20(agentsDir)) return c.json({ agents: [] });
19633
18393
  const agents = [];
19634
18394
  try {
19635
18395
  const entries = readdirSync6(agentsDir, { withFileTypes: true });
19636
18396
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
19637
18397
  if (!entry.isDirectory()) continue;
19638
18398
  if (entry.name === "admin") continue;
19639
- const configPath2 = resolve24(agentsDir, entry.name, "config.json");
19640
- if (!existsSync22(configPath2)) continue;
18399
+ const configPath2 = resolve21(agentsDir, entry.name, "config.json");
18400
+ if (!existsSync20(configPath2)) continue;
19641
18401
  try {
19642
- const config = JSON.parse(readFileSync22(configPath2, "utf-8"));
18402
+ const config = JSON.parse(readFileSync20(configPath2, "utf-8"));
19643
18403
  agents.push({
19644
18404
  slug: entry.name,
19645
18405
  displayName: config.displayName ?? entry.name,
@@ -19665,8 +18425,8 @@ app17.delete("/:slug", (c) => {
19665
18425
  if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
19666
18426
  return c.json({ error: "Invalid agent slug" }, 400);
19667
18427
  }
19668
- const agentDir = resolve24(account.accountDir, "agents", slug);
19669
- if (!existsSync22(agentDir)) {
18428
+ const agentDir = resolve21(account.accountDir, "agents", slug);
18429
+ if (!existsSync20(agentDir)) {
19670
18430
  return c.json({ error: "Agent not found" }, 404);
19671
18431
  }
19672
18432
  try {
@@ -19681,27 +18441,27 @@ app17.delete("/:slug", (c) => {
19681
18441
  var agents_default = app17;
19682
18442
 
19683
18443
  // 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(), "..");
18444
+ import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
18445
+ import { resolve as resolve22, join as join10 } from "path";
18446
+ var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
19687
18447
  var brandHostname = "maxy";
19688
18448
  var brandNpmPackage = "@rubytech/create-maxy";
19689
- var brandJsonPath = join11(PLATFORM_ROOT9, "config", "brand.json");
19690
- if (existsSync23(brandJsonPath)) {
18449
+ var brandJsonPath = join10(PLATFORM_ROOT8, "config", "brand.json");
18450
+ if (existsSync21(brandJsonPath)) {
19691
18451
  try {
19692
- const brand = JSON.parse(readFileSync23(brandJsonPath, "utf-8"));
18452
+ const brand = JSON.parse(readFileSync21(brandJsonPath, "utf-8"));
19693
18453
  if (brand.hostname) brandHostname = brand.hostname;
19694
18454
  if (brand.npm?.packageName) brandNpmPackage = brand.npm.packageName;
19695
18455
  } catch {
19696
18456
  }
19697
18457
  }
19698
- var VERSION_FILE = resolve25(PLATFORM_ROOT9, `config/.${brandHostname}-version`);
18458
+ var VERSION_FILE = resolve22(PLATFORM_ROOT8, `config/.${brandHostname}-version`);
19699
18459
  var NPM_PACKAGE = brandNpmPackage;
19700
18460
  var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
19701
18461
  var FETCH_TIMEOUT_MS = 5e3;
19702
18462
  function readInstalled() {
19703
- if (!existsSync23(VERSION_FILE)) return "unknown";
19704
- const content = readFileSync23(VERSION_FILE, "utf-8").trim();
18463
+ if (!existsSync21(VERSION_FILE)) return "unknown";
18464
+ const content = readFileSync21(VERSION_FILE, "utf-8").trim();
19705
18465
  return content || "unknown";
19706
18466
  }
19707
18467
  async function fetchLatest() {
@@ -20194,9 +18954,9 @@ app23.post("/", async (c) => {
20194
18954
  var events_default = app23;
20195
18955
 
20196
18956
  // 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";
18957
+ import { homedir as homedir3 } from "os";
18958
+ import { resolve as resolve24 } from "path";
18959
+ import { readFileSync as readFileSync23 } from "fs";
20200
18960
 
20201
18961
  // app/lib/dns-label.ts
20202
18962
  var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
@@ -20212,14 +18972,14 @@ function isValidDomain(value) {
20212
18972
  }
20213
18973
 
20214
18974
  // app/lib/alias-domains.ts
20215
- import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync24, writeFileSync as writeFileSync15 } from "fs";
18975
+ import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
20216
18976
  import { dirname as dirname9 } from "path";
20217
- import { resolve as resolve26 } from "path";
20218
- var ALIAS_DOMAINS_PATH = resolve26(MAXY_DIR, "alias-domains.json");
18977
+ import { resolve as resolve23 } from "path";
18978
+ var ALIAS_DOMAINS_PATH = resolve23(MAXY_DIR, "alias-domains.json");
20219
18979
  function readExisting() {
20220
- if (!existsSync24(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
18980
+ if (!existsSync22(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
20221
18981
  try {
20222
- const parsed = JSON.parse(readFileSync24(ALIAS_DOMAINS_PATH, "utf-8"));
18982
+ const parsed = JSON.parse(readFileSync22(ALIAS_DOMAINS_PATH, "utf-8"));
20223
18983
  if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
20224
18984
  return new Set(parsed.filter((h) => typeof h === "string"));
20225
18985
  } catch {
@@ -20230,18 +18990,18 @@ function addAliasDomain(hostname2) {
20230
18990
  const existing = readExisting();
20231
18991
  if (existing.has(hostname2)) return;
20232
18992
  existing.add(hostname2);
20233
- mkdirSync14(dirname9(ALIAS_DOMAINS_PATH), { recursive: true });
20234
- writeFileSync15(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
18993
+ mkdirSync12(dirname9(ALIAS_DOMAINS_PATH), { recursive: true });
18994
+ writeFileSync14(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
20235
18995
  }
20236
18996
 
20237
18997
  // server/routes/admin/cloudflare.ts
20238
18998
  var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
20239
18999
  var DOMAINS_TIMEOUT_MS = 40 * 1e3;
20240
19000
  function loadBrandInfo() {
20241
- const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve27(process.cwd(), "..");
20242
- const brandPath = resolve27(platformRoot3, "config", "brand.json");
19001
+ const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
19002
+ const brandPath = resolve24(platformRoot2, "config", "brand.json");
20243
19003
  try {
20244
- const parsed = JSON.parse(readFileSync25(brandPath, "utf-8"));
19004
+ const parsed = JSON.parse(readFileSync23(brandPath, "utf-8"));
20245
19005
  const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
20246
19006
  const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
20247
19007
  return { hostname: hostname2, configDir: configDir2 };
@@ -20344,7 +19104,7 @@ app24.get("/domains", requireAdminSession, async (c) => {
20344
19104
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
20345
19105
  log2(`phase=stream-log-resolved path=${streamLogPath}`);
20346
19106
  const brand = loadBrandInfo();
20347
- const scriptPath = resolve27(homedir4(), "list-cf-domains.sh");
19107
+ const scriptPath = resolve24(homedir3(), "list-cf-domains.sh");
20348
19108
  const result = await runFormSpawn({
20349
19109
  scriptPath,
20350
19110
  args: [brand.hostname],
@@ -20469,7 +19229,7 @@ app24.post("/setup", requireAdminSession, async (c) => {
20469
19229
  }
20470
19230
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
20471
19231
  log2(`phase=stream-log-resolved path=${streamLogPath}`);
20472
- const scriptPath = resolve27(homedir4(), "setup-tunnel.sh");
19232
+ const scriptPath = resolve24(homedir3(), "setup-tunnel.sh");
20473
19233
  const args = [brand.hostname, String(port2), adminFqdn];
20474
19234
  if (publicFqdn) args.push(publicFqdn);
20475
19235
  if (apex) args.push(apex);
@@ -20538,17 +19298,17 @@ var cloudflare_default = app24;
20538
19298
  import { createReadStream as createReadStream3 } from "fs";
20539
19299
  import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
20540
19300
  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";
19301
+ import { basename as basename6, dirname as dirname10, join as join11, resolve as resolve26, sep as sep2 } from "path";
20542
19302
  import { Readable as Readable3 } from "stream";
20543
19303
 
20544
19304
  // app/lib/data-path.ts
20545
19305
  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");
19306
+ import { resolve as resolve25, normalize, sep, relative } from "path";
19307
+ var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "../platform");
19308
+ var DATA_ROOT = resolve25(PLATFORM_ROOT9, "..", "data");
20549
19309
  function resolveDataPath(raw2) {
20550
19310
  const cleaned = normalize("/" + (raw2 ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
20551
- const absolute = resolve28(DATA_ROOT, cleaned);
19311
+ const absolute = resolve25(DATA_ROOT, cleaned);
20552
19312
  let dataRootReal;
20553
19313
  try {
20554
19314
  dataRootReal = realpathSync3(DATA_ROOT);
@@ -20815,7 +19575,7 @@ async function cascadeDeleteDocument(params) {
20815
19575
  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
19576
  async function readMeta(absDir, baseName) {
20817
19577
  try {
20818
- const raw2 = await readFile4(join12(absDir, `${baseName}.meta.json`), "utf8");
19578
+ const raw2 = await readFile4(join11(absDir, `${baseName}.meta.json`), "utf8");
20819
19579
  const parsed = JSON.parse(raw2);
20820
19580
  if (typeof parsed?.filename === "string") {
20821
19581
  return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
@@ -20826,7 +19586,7 @@ async function readMeta(absDir, baseName) {
20826
19586
  }
20827
19587
  async function readAccountNames() {
20828
19588
  const map = /* @__PURE__ */ new Map();
20829
- const accountsDir = resolve29(DATA_ROOT, "accounts");
19589
+ const accountsDir = resolve26(DATA_ROOT, "accounts");
20830
19590
  let names;
20831
19591
  try {
20832
19592
  names = await readdir2(accountsDir);
@@ -20835,7 +19595,7 @@ async function readAccountNames() {
20835
19595
  }
20836
19596
  for (const name of names) {
20837
19597
  if (!UUID_RE3.test(name)) continue;
20838
- const configPath2 = resolve29(accountsDir, name, "account.json");
19598
+ const configPath2 = resolve26(accountsDir, name, "account.json");
20839
19599
  try {
20840
19600
  const raw2 = await readFile4(configPath2, "utf8");
20841
19601
  const parsed = JSON.parse(raw2);
@@ -20853,7 +19613,7 @@ async function readAccountNames() {
20853
19613
  }
20854
19614
  async function enrich(absolute, entry, accountNames) {
20855
19615
  if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
20856
- const meta = await readMeta(join12(absolute, entry.name), entry.name);
19616
+ const meta = await readMeta(join11(absolute, entry.name), entry.name);
20857
19617
  if (meta?.filename) {
20858
19618
  entry.displayName = meta.filename;
20859
19619
  entry.mimeType = meta.mimeType;
@@ -20912,7 +19672,7 @@ app25.get("/", requireAdminSession, async (c) => {
20912
19672
  continue;
20913
19673
  }
20914
19674
  try {
20915
- const entryPath = join12(absolute, name);
19675
+ const entryPath = join11(absolute, name);
20916
19676
  const s = await stat4(entryPath);
20917
19677
  entries.push({
20918
19678
  name,
@@ -21026,8 +19786,8 @@ app25.post("/upload", requireAdminSession, async (c) => {
21026
19786
  }
21027
19787
  const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
21028
19788
  const finalName = `${Date.now()}-${safeName}`;
21029
- const destDir = resolve29(DATA_ROOT, "uploads", accountId);
21030
- const destPath = resolve29(destDir, finalName);
19789
+ const destDir = resolve26(DATA_ROOT, "uploads", accountId);
19790
+ const destPath = resolve26(destDir, finalName);
21031
19791
  try {
21032
19792
  await mkdir3(destDir, { recursive: true });
21033
19793
  const dataRootReal = realpathSync4(DATA_ROOT);
@@ -21085,7 +19845,7 @@ app25.delete("/", requireAdminSession, async (c) => {
21085
19845
  }
21086
19846
  const dot = base.lastIndexOf(".");
21087
19847
  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;
19848
+ const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join11(dirname10(absolute), `${stem}.meta.json`) : null;
21089
19849
  await unlink2(absolute);
21090
19850
  if (sidecarPath) {
21091
19851
  try {
@@ -21961,15 +20721,15 @@ app34.route("/adherence", adherence_default);
21961
20721
  var admin_default = app34;
21962
20722
 
21963
20723
  // 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") : "";
20724
+ var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT || "";
20725
+ var BRAND_JSON_PATH = PLATFORM_ROOT10 ? join12(PLATFORM_ROOT10, "config", "brand.json") : "";
21966
20726
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
21967
- if (BRAND_JSON_PATH && !existsSync25(BRAND_JSON_PATH)) {
20727
+ if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
21968
20728
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
21969
20729
  }
21970
- if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
20730
+ if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
21971
20731
  try {
21972
- const parsed = JSON.parse(readFileSync26(BRAND_JSON_PATH, "utf-8"));
20732
+ const parsed = JSON.parse(readFileSync24(BRAND_JSON_PATH, "utf-8"));
21973
20733
  BRAND = { ...BRAND, ...parsed };
21974
20734
  } catch (err) {
21975
20735
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -21988,11 +20748,11 @@ var brandLoginOpts = {
21988
20748
  bodyFont: BRAND.defaultFonts?.body,
21989
20749
  logoContainsName: !!BRAND.logoContainsName
21990
20750
  };
21991
- var ALIAS_DOMAINS_PATH2 = join13(homedir5(), BRAND.configDir, "alias-domains.json");
20751
+ var ALIAS_DOMAINS_PATH2 = join12(homedir4(), BRAND.configDir, "alias-domains.json");
21992
20752
  function loadAliasDomains() {
21993
20753
  try {
21994
- if (!existsSync25(ALIAS_DOMAINS_PATH2)) return null;
21995
- const parsed = JSON.parse(readFileSync26(ALIAS_DOMAINS_PATH2, "utf-8"));
20754
+ if (!existsSync23(ALIAS_DOMAINS_PATH2)) return null;
20755
+ const parsed = JSON.parse(readFileSync24(ALIAS_DOMAINS_PATH2, "utf-8"));
21996
20756
  if (!Array.isArray(parsed)) {
21997
20757
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
21998
20758
  return null;
@@ -22332,20 +21092,20 @@ app35.get("/agent-assets/:slug/:filename", (c) => {
22332
21092
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
22333
21093
  return c.text("Not found", 404);
22334
21094
  }
22335
- const filePath = resolve30(account.accountDir, "agents", slug, "assets", filename);
22336
- const expectedDir = resolve30(account.accountDir, "agents", slug, "assets");
21095
+ const filePath = resolve27(account.accountDir, "agents", slug, "assets", filename);
21096
+ const expectedDir = resolve27(account.accountDir, "agents", slug, "assets");
22337
21097
  if (!filePath.startsWith(expectedDir + "/")) {
22338
21098
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
22339
21099
  return c.text("Forbidden", 403);
22340
21100
  }
22341
- if (!existsSync25(filePath)) {
21101
+ if (!existsSync23(filePath)) {
22342
21102
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
22343
21103
  return c.text("Not found", 404);
22344
21104
  }
22345
21105
  const ext = "." + filename.split(".").pop()?.toLowerCase();
22346
21106
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
22347
21107
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
22348
- const body = readFileSync26(filePath);
21108
+ const body = readFileSync24(filePath);
22349
21109
  return c.body(body, 200, {
22350
21110
  "Content-Type": contentType,
22351
21111
  "Cache-Control": "public, max-age=3600"
@@ -22362,20 +21122,20 @@ app35.get("/generated/:filename", (c) => {
22362
21122
  console.error(`[generated] serve file=${filename} status=404`);
22363
21123
  return c.text("Not found", 404);
22364
21124
  }
22365
- const filePath = resolve30(account.accountDir, "generated", filename);
22366
- const expectedDir = resolve30(account.accountDir, "generated");
21125
+ const filePath = resolve27(account.accountDir, "generated", filename);
21126
+ const expectedDir = resolve27(account.accountDir, "generated");
22367
21127
  if (!filePath.startsWith(expectedDir + "/")) {
22368
21128
  console.error(`[generated] serve file=${filename} status=403`);
22369
21129
  return c.text("Forbidden", 403);
22370
21130
  }
22371
- if (!existsSync25(filePath)) {
21131
+ if (!existsSync23(filePath)) {
22372
21132
  console.error(`[generated] serve file=${filename} status=404`);
22373
21133
  return c.text("Not found", 404);
22374
21134
  }
22375
21135
  const ext = "." + filename.split(".").pop()?.toLowerCase();
22376
21136
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
22377
21137
  console.log(`[generated] serve file=${filename} status=200`);
22378
- const body = readFileSync26(filePath);
21138
+ const body = readFileSync24(filePath);
22379
21139
  return c.body(body, 200, {
22380
21140
  "Content-Type": contentType,
22381
21141
  "Cache-Control": "public, max-age=86400"
@@ -22384,9 +21144,9 @@ app35.get("/generated/:filename", (c) => {
22384
21144
  var htmlCache = /* @__PURE__ */ new Map();
22385
21145
  var brandLogoPath = "/brand/maxy-monochrome.png";
22386
21146
  var brandIconPath = "/brand/maxy-monochrome.png";
22387
- if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
21147
+ if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
22388
21148
  try {
22389
- const fullBrand = JSON.parse(readFileSync26(BRAND_JSON_PATH, "utf-8"));
21149
+ const fullBrand = JSON.parse(readFileSync24(BRAND_JSON_PATH, "utf-8"));
22390
21150
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
22391
21151
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
22392
21152
  } catch {
@@ -22402,10 +21162,10 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
22402
21162
  })}</script>`;
22403
21163
  function readInstalledVersion() {
22404
21164
  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();
21165
+ if (!PLATFORM_ROOT10) return "unknown";
21166
+ const versionFile = join12(PLATFORM_ROOT10, "config", `.${BRAND.hostname}-version`);
21167
+ if (!existsSync23(versionFile)) return "unknown";
21168
+ const content = readFileSync24(versionFile, "utf-8").trim();
22409
21169
  return content || "unknown";
22410
21170
  } catch {
22411
21171
  return "unknown";
@@ -22446,9 +21206,9 @@ var clientErrorReporterScript = `<script>
22446
21206
  function cachedHtml(file) {
22447
21207
  let html = htmlCache.get(file);
22448
21208
  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)}"`);
21209
+ html = readFileSync24(resolve27(process.cwd(), "public", file), "utf-8");
21210
+ html = html.replace("<title>Maxy</title>", `<title>${escapeHtml(BRAND.productName)}</title>`);
21211
+ html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
22452
21212
  const headInjection = file === "index.html" ? `${brandScript}
22453
21213
  ${versionScript}
22454
21214
  ${clientErrorReporterScript}
@@ -22461,26 +21221,26 @@ ${clientErrorReporterScript}
22461
21221
  }
22462
21222
  var brandedHtmlCache = /* @__PURE__ */ new Map();
22463
21223
  function loadBrandingCache(agentSlug) {
22464
- const configDir2 = join13(homedir5(), BRAND.configDir);
21224
+ const configDir2 = join12(homedir4(), BRAND.configDir);
22465
21225
  try {
22466
- const accountJsonPath = join13(configDir2, "account.json");
22467
- if (!existsSync25(accountJsonPath)) return null;
22468
- const account = JSON.parse(readFileSync26(accountJsonPath, "utf-8"));
21226
+ const accountJsonPath = join12(configDir2, "account.json");
21227
+ if (!existsSync23(accountJsonPath)) return null;
21228
+ const account = JSON.parse(readFileSync24(accountJsonPath, "utf-8"));
22469
21229
  const accountId = account.accountId;
22470
21230
  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"));
21231
+ const cachePath = join12(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
21232
+ if (!existsSync23(cachePath)) return null;
21233
+ return JSON.parse(readFileSync24(cachePath, "utf-8"));
22474
21234
  } catch {
22475
21235
  return null;
22476
21236
  }
22477
21237
  }
22478
21238
  function resolveDefaultSlug() {
22479
21239
  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"));
21240
+ const configDir2 = join12(homedir4(), BRAND.configDir);
21241
+ const accountJsonPath = join12(configDir2, "account.json");
21242
+ if (!existsSync23(accountJsonPath)) return null;
21243
+ const account = JSON.parse(readFileSync24(accountJsonPath, "utf-8"));
22484
21244
  return account.defaultAgent || null;
22485
21245
  } catch {
22486
21246
  return null;
@@ -22494,26 +21254,26 @@ function brandedPublicHtml(agentSlug) {
22494
21254
  if (!branding) return baseHtml;
22495
21255
  const brandHash = JSON.stringify(branding).length;
22496
21256
  if (cached && cached.mtime === brandHash) return cached.html;
22497
- const title = escapeHtml2(branding.name);
22498
- const description = branding.tagline ? escapeHtml2(branding.tagline) : "";
21257
+ const title = escapeHtml(branding.name);
21258
+ const description = branding.tagline ? escapeHtml(branding.tagline) : "";
22499
21259
  const themeColor = branding.primaryColor || "";
22500
21260
  const ogImage = branding.logoUrl || "";
22501
21261
  const favicon = branding.faviconUrl || "";
22502
21262
  const metaTags = [
22503
21263
  ` <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)}">` : "",
21264
+ favicon ? ` <link rel="icon" href="${escapeHtml(favicon)}">` : ` <link rel="icon" href="${escapeHtml(brandFaviconPath)}">`,
21265
+ themeColor ? ` <meta name="theme-color" content="${escapeHtml(themeColor)}">` : "",
22506
21266
  ` <meta property="og:title" content="${title}">`,
22507
21267
  description ? ` <meta property="og:description" content="${description}">` : "",
22508
- ogImage ? ` <meta property="og:image" content="${escapeHtml2(ogImage)}">` : "",
21268
+ ogImage ? ` <meta property="og:image" content="${escapeHtml(ogImage)}">` : "",
22509
21269
  ' <meta property="og:type" content="website">'
22510
21270
  ].filter(Boolean).join("\n");
22511
- const html = baseHtml.replace(` <title>${escapeHtml2(BRAND.productName)}</title>
22512
- <link rel="icon" href="${escapeHtml2(brandFaviconPath)}">`, metaTags);
21271
+ const html = baseHtml.replace(` <title>${escapeHtml(BRAND.productName)}</title>
21272
+ <link rel="icon" href="${escapeHtml(brandFaviconPath)}">`, metaTags);
22513
21273
  brandedHtmlCache.set(agentSlug, { html, mtime: brandHash });
22514
21274
  return html;
22515
21275
  }
22516
- function escapeHtml2(s) {
21276
+ function escapeHtml(s) {
22517
21277
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
22518
21278
  }
22519
21279
  app35.get("/", (c) => {
@@ -22553,11 +21313,11 @@ app35.use("/vnc-popout.html", logViewerFetch);
22553
21313
  app35.get("/vnc-popout.html", (c) => {
22554
21314
  let html = htmlCache.get("vnc-popout.html");
22555
21315
  if (!html) {
22556
- html = readFileSync26(resolve30(process.cwd(), "public", "vnc-popout.html"), "utf-8");
22557
- const name = escapeHtml2(BRAND.productName);
21316
+ html = readFileSync24(resolve27(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21317
+ const name = escapeHtml(BRAND.productName);
22558
21318
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
22559
21319
  html = html.replace("</head>", ` ${brandScript}
22560
- <link rel="icon" href="${escapeHtml2(brandFaviconPath)}">
21320
+ <link rel="icon" href="${escapeHtml(brandFaviconPath)}">
22561
21321
  </head>`);
22562
21322
  htmlCache.set("vnc-popout.html", html);
22563
21323
  }
@@ -22607,36 +21367,10 @@ app35.get("/:slug", async (c, next) => {
22607
21367
  await next();
22608
21368
  });
22609
21369
  app35.use("/*", serveStatic({ root: "./public" }));
22610
- var port = parseInt(process.env.PORT ?? "19200", 10);
22611
- var hostname = process.env.HOSTNAME ?? "0.0.0.0";
21370
+ var port = parseInt(process.env.PORT ?? "19199", 10);
21371
+ var hostname = process.env.HOSTNAME ?? "127.0.0.1";
22612
21372
  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
21373
  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
21374
  var SUBAPP_MANIFEST = [
22641
21375
  { prefix: "/api/health", file: "server/routes/health.ts", subapp: health_default },
22642
21376
  { prefix: "/api/session", file: "server/routes/session.ts", subapp: session_default },
@@ -22669,8 +21403,8 @@ try {
22669
21403
  (async () => {
22670
21404
  try {
22671
21405
  let userId = "";
22672
- if (existsSync25(USERS_FILE)) {
22673
- const users = JSON.parse(readFileSync26(USERS_FILE, "utf-8").trim() || "[]");
21406
+ if (existsSync23(USERS_FILE)) {
21407
+ const users = JSON.parse(readFileSync24(USERS_FILE, "utf-8").trim() || "[]");
22674
21408
  userId = users[0]?.userId ?? "";
22675
21409
  }
22676
21410
  await backfillNullUserIdConversations(userId);
@@ -22696,7 +21430,7 @@ if (bootAccountConfig?.whatsapp) {
22696
21430
  }
22697
21431
  init({
22698
21432
  configDir: configDirForWhatsApp,
22699
- platformRoot: resolve30(process.env.MAXY_PLATFORM_ROOT ?? join13(__dirname, "..")),
21433
+ platformRoot: resolve27(process.env.MAXY_PLATFORM_ROOT ?? join12(__dirname, "..")),
22700
21434
  accountConfig: bootAccountConfig,
22701
21435
  onMessage: async (msg) => {
22702
21436
  try {