@rubytech/create-maxy 1.0.887 → 1.0.889

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.
@@ -16,7 +16,7 @@ import {
16
16
  streamActionEvents,
17
17
  vncLog,
18
18
  websockifyLog
19
- } from "./chunk-MOAY7KG2.js";
19
+ } from "./chunk-S65GGO53.js";
20
20
  import {
21
21
  LOG_DIR,
22
22
  WEBSOCKIFY_PORT
@@ -42,7 +42,6 @@ import {
42
42
  load,
43
43
  logPath,
44
44
  pickComponentBytes,
45
- readBundleSubPlugins,
46
45
  reconcileEnabledPlugins,
47
46
  recordFailedAttempt,
48
47
  render,
@@ -53,7 +52,6 @@ import {
53
52
  resolveBrowserTransport,
54
53
  resolveClientIp,
55
54
  resolveDefaultAgentSlug,
56
- resolveEntitlement,
57
55
  resolveUserAccounts,
58
56
  safeJson,
59
57
  sanitizeClientCorrId,
@@ -75,8 +73,9 @@ import {
75
73
  verifyRemotePassword,
76
74
  vncLog,
77
75
  waitForExit,
76
+ walkPremiumBundles,
78
77
  writeChromiumWrapper
79
- } from "./chunk-MOAY7KG2.js";
78
+ } from "./chunk-S65GGO53.js";
80
79
  import {
81
80
  CDP_PORT,
82
81
  COMMERCIAL_MODE,
@@ -664,8 +663,8 @@ var serveStatic = (options = { root: "" }) => {
664
663
  };
665
664
 
666
665
  // server/index.ts
667
- import { readFileSync as readFileSync18, existsSync as existsSync24, watchFile } from "fs";
668
- import { resolve as resolve21, join as join12, basename as basename4 } from "path";
666
+ import { readFileSync as readFileSync19, existsSync as existsSync25, watchFile } from "fs";
667
+ import { resolve as resolve22, join as join12, basename as basename4 } from "path";
669
668
  import { homedir as homedir3 } from "os";
670
669
 
671
670
  // app/lib/agent-slug-pattern.ts
@@ -1309,7 +1308,7 @@ var credsSaveQueue = Promise.resolve();
1309
1308
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
1310
1309
  console.error(`${TAG3} draining credential save queue\u2026`);
1311
1310
  const timer2 = new Promise(
1312
- (resolve22) => setTimeout(() => resolve22("timeout"), timeoutMs)
1311
+ (resolve23) => setTimeout(() => resolve23("timeout"), timeoutMs)
1313
1312
  );
1314
1313
  const result = await Promise.race([
1315
1314
  credsSaveQueue.then(() => "drained"),
@@ -1437,11 +1436,11 @@ async function createWaSocket(opts) {
1437
1436
  return sock;
1438
1437
  }
1439
1438
  async function waitForConnection(sock) {
1440
- return new Promise((resolve22, reject) => {
1439
+ return new Promise((resolve23, reject) => {
1441
1440
  const handler = (update) => {
1442
1441
  if (update.connection === "open") {
1443
1442
  sock.ev.off("connection.update", handler);
1444
- resolve22();
1443
+ resolve23();
1445
1444
  }
1446
1445
  if (update.connection === "close") {
1447
1446
  sock.ev.off("connection.update", handler);
@@ -1555,14 +1554,14 @@ ${inspected}`;
1555
1554
  return inspect2(err, INSPECT_OPTS2);
1556
1555
  }
1557
1556
  function withTimeout(label, promise, timeoutMs) {
1558
- return new Promise((resolve22, reject) => {
1557
+ return new Promise((resolve23, reject) => {
1559
1558
  const timer2 = setTimeout(() => {
1560
1559
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
1561
1560
  }, timeoutMs);
1562
1561
  promise.then(
1563
1562
  (value) => {
1564
1563
  clearTimeout(timer2);
1565
- resolve22(value);
1564
+ resolve23(value);
1566
1565
  },
1567
1566
  (err) => {
1568
1567
  clearTimeout(timer2);
@@ -2097,8 +2096,8 @@ async function persistWhatsAppMessage(input) {
2097
2096
  const { givenName, familyName } = splitName(input.pushName);
2098
2097
  const prev = sessionWriteLocks.get(input.cacheKey);
2099
2098
  let release;
2100
- const mine = new Promise((resolve22) => {
2101
- release = resolve22;
2099
+ const mine = new Promise((resolve23) => {
2100
+ release = resolve23;
2102
2101
  });
2103
2102
  const chained = (prev ?? Promise.resolve()).then(() => mine);
2104
2103
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -3135,11 +3134,11 @@ async function connectWithReconnect(conn) {
3135
3134
  console.error(
3136
3135
  `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
3137
3136
  );
3138
- await new Promise((resolve22) => {
3139
- const timer2 = setTimeout(resolve22, delay);
3137
+ await new Promise((resolve23) => {
3138
+ const timer2 = setTimeout(resolve23, delay);
3140
3139
  conn.abortController.signal.addEventListener("abort", () => {
3141
3140
  clearTimeout(timer2);
3142
- resolve22();
3141
+ resolve23();
3143
3142
  }, { once: true });
3144
3143
  });
3145
3144
  }
@@ -3147,16 +3146,16 @@ async function connectWithReconnect(conn) {
3147
3146
  }
3148
3147
  }
3149
3148
  function waitForDisconnectEvent(conn) {
3150
- return new Promise((resolve22) => {
3149
+ return new Promise((resolve23) => {
3151
3150
  if (!conn.sock) {
3152
- resolve22();
3151
+ resolve23();
3153
3152
  return;
3154
3153
  }
3155
3154
  const sock = conn.sock;
3156
3155
  const handler = (update) => {
3157
3156
  if (update.connection === "close") {
3158
3157
  sock.ev.off("connection.update", handler);
3159
- resolve22();
3158
+ resolve23();
3160
3159
  }
3161
3160
  };
3162
3161
  sock.ev.on("connection.update", handler);
@@ -3418,8 +3417,8 @@ async function handleInboundMessage(conn, msg) {
3418
3417
  const conversationKey = isGroup ? remoteJid : senderPhone;
3419
3418
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
3420
3419
  let resolvePending;
3421
- const sttPending = new Promise((resolve22) => {
3422
- resolvePending = resolve22;
3420
+ const sttPending = new Promise((resolve23) => {
3421
+ resolvePending = resolve23;
3423
3422
  });
3424
3423
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
3425
3424
  try {
@@ -3540,20 +3539,20 @@ async function probeApiKey() {
3540
3539
  return result.status;
3541
3540
  }
3542
3541
  function checkPort(port2, timeoutMs = 500) {
3543
- return new Promise((resolve22) => {
3542
+ return new Promise((resolve23) => {
3544
3543
  const socket = createConnection(port2, "127.0.0.1");
3545
3544
  socket.setTimeout(timeoutMs);
3546
3545
  socket.once("connect", () => {
3547
3546
  socket.destroy();
3548
- resolve22(true);
3547
+ resolve23(true);
3549
3548
  });
3550
3549
  socket.once("error", () => {
3551
3550
  socket.destroy();
3552
- resolve22(false);
3551
+ resolve23(false);
3553
3552
  });
3554
3553
  socket.once("timeout", () => {
3555
3554
  socket.destroy();
3556
- resolve22(false);
3555
+ resolve23(false);
3557
3556
  });
3558
3557
  });
3559
3558
  }
@@ -5644,8 +5643,8 @@ async function startLogin(opts) {
5644
5643
  resetActiveLogin(accountId);
5645
5644
  let resolveQr = null;
5646
5645
  let rejectQr = null;
5647
- const qrPromise = new Promise((resolve22, reject) => {
5648
- resolveQr = resolve22;
5646
+ const qrPromise = new Promise((resolve23, reject) => {
5647
+ resolveQr = resolve23;
5649
5648
  rejectQr = reject;
5650
5649
  });
5651
5650
  const qrTimer = setTimeout(
@@ -9557,8 +9556,8 @@ app23.get("/tunnels", requireAdminSession, async (c) => {
9557
9556
  if (!correlationId) return err("session", "No active conversation for session \u2014 refresh chat.");
9558
9557
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
9559
9558
  const certPath = resolve13(homedir2(), brand.configDir, "cloudflared", "cert.pem");
9560
- const { existsSync: existsSync25 } = await import("fs");
9561
- if (!existsSync25(certPath)) {
9559
+ const { existsSync: existsSync26 } = await import("fs");
9560
+ if (!existsSync26(certPath)) {
9562
9561
  return err("cert", `Cloudflare origin certificate is not on disk yet (${certPath}). Complete the Cloudflare login first by submitting the form once \u2014 the OAuth flow writes cert.pem.`);
9563
9562
  }
9564
9563
  const result = await runFormSpawn({
@@ -12935,8 +12934,232 @@ function startGraphHealthTimer() {
12935
12934
  if (typeof timer.unref === "function") timer.unref();
12936
12935
  }
12937
12936
 
12937
+ // ../lib/entitlement/src/index.ts
12938
+ import { createPublicKey, createHash as createHash3, verify as cryptoVerify } from "crypto";
12939
+ import { existsSync as existsSync24, readFileSync as readFileSync18, statSync as statSync8 } from "fs";
12940
+ import { resolve as resolve21 } from "path";
12941
+
12942
+ // ../lib/entitlement/src/canonicalize.ts
12943
+ function canonicalize(value) {
12944
+ if (value === null) return "null";
12945
+ if (typeof value === "boolean") return value ? "true" : "false";
12946
+ if (typeof value === "string") return JSON.stringify(value);
12947
+ if (typeof value === "number") {
12948
+ if (!Number.isFinite(value)) {
12949
+ throw new Error(`canonicalize: non-finite number rejected (${value})`);
12950
+ }
12951
+ if (Object.is(value, -0)) {
12952
+ throw new Error("canonicalize: negative zero rejected");
12953
+ }
12954
+ return JSON.stringify(value);
12955
+ }
12956
+ if (Array.isArray(value)) {
12957
+ return "[" + value.map(canonicalize).join(",") + "]";
12958
+ }
12959
+ if (typeof value === "object") {
12960
+ const keys = Object.keys(value).sort();
12961
+ const parts = [];
12962
+ for (const k of keys) {
12963
+ parts.push(JSON.stringify(k) + ":" + canonicalize(value[k]));
12964
+ }
12965
+ return "{" + parts.join(",") + "}";
12966
+ }
12967
+ throw new Error(`canonicalize: unsupported value type ${typeof value}`);
12968
+ }
12969
+
12970
+ // ../lib/entitlement/src/index.ts
12971
+ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d99db79";
12972
+ var GRACE_DAYS = 7;
12973
+ var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
12974
+ function pubkeyPath(brand) {
12975
+ return resolve21(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
12976
+ }
12977
+ var memo = null;
12978
+ function memoKey(mtimeMs, account) {
12979
+ const bucket = Math.floor(Date.now() / 6e4);
12980
+ return `${mtimeMs}:${bucket}:${account.accountId}:${account.customerEmail ?? ""}`;
12981
+ }
12982
+ var VALID_TIERS = /* @__PURE__ */ new Set(["solo", "family", "pro"]);
12983
+ function resolveEntitlement(brand, account) {
12984
+ if (brand.commercialMode !== true) {
12985
+ return logResolved(implicitTrust(account), null);
12986
+ }
12987
+ const entitlementPath = resolve21(brand.configDir, "entitlement.json");
12988
+ if (!existsSync24(entitlementPath)) {
12989
+ return logResolved(anonymousFallback("missing"), { reason: "missing" });
12990
+ }
12991
+ const stat6 = statSync8(entitlementPath);
12992
+ const key = memoKey(stat6.mtimeMs, account);
12993
+ if (memo && memo.key === key) {
12994
+ return memo.result;
12995
+ }
12996
+ const result = verifyAndResolve(brand, entitlementPath, account);
12997
+ memo = { key, result };
12998
+ return result;
12999
+ }
13000
+ function verifyAndResolve(brand, entitlementPath, account) {
13001
+ let pubkeyPem;
13002
+ try {
13003
+ pubkeyPem = readFileSync18(pubkeyPath(brand), "utf-8");
13004
+ } catch (err) {
13005
+ return logResolved(anonymousFallback("pubkey-missing"), {
13006
+ reason: "pubkey-missing"
13007
+ });
13008
+ }
13009
+ const pubkeyHash = createHash3("sha256").update(pubkeyPem).digest("hex");
13010
+ if (pubkeyHash !== PUBKEY_SHA256) {
13011
+ return logResolved(anonymousFallback("pubkey-tamper"), {
13012
+ reason: "pubkey-tamper"
13013
+ });
13014
+ }
13015
+ let envelope;
13016
+ try {
13017
+ envelope = JSON.parse(readFileSync18(entitlementPath, "utf-8"));
13018
+ } catch {
13019
+ return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
13020
+ }
13021
+ if (typeof envelope !== "object" || envelope === null || typeof envelope.signature !== "string" || typeof envelope.payload !== "object" || envelope.payload === null) {
13022
+ return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
13023
+ }
13024
+ const payload = envelope.payload;
13025
+ const signatureB64 = envelope.signature;
13026
+ let pubkey;
13027
+ try {
13028
+ pubkey = createPublicKey(pubkeyPem);
13029
+ } catch {
13030
+ return logResolved(anonymousFallback("pubkey-malformed"), {
13031
+ reason: "pubkey-malformed"
13032
+ });
13033
+ }
13034
+ let canonical;
13035
+ try {
13036
+ canonical = canonicalize(payload);
13037
+ } catch {
13038
+ return logResolved(anonymousFallback("payload-shape"), {
13039
+ reason: "payload-shape"
13040
+ });
13041
+ }
13042
+ let sigBuf;
13043
+ try {
13044
+ sigBuf = Buffer.from(signatureB64, "base64");
13045
+ } catch {
13046
+ return logResolved(anonymousFallback("signature-encoding"), {
13047
+ reason: "signature-encoding"
13048
+ });
13049
+ }
13050
+ const ok = cryptoVerify(null, Buffer.from(canonical, "utf-8"), pubkey, sigBuf);
13051
+ if (!ok) {
13052
+ return logResolved(anonymousFallback("signature"), { reason: "signature" });
13053
+ }
13054
+ if (typeof payload.accountId !== "string" || payload.accountId !== account.accountId) {
13055
+ return logResolved(anonymousFallback("binding-account"), {
13056
+ reason: "binding-account"
13057
+ });
13058
+ }
13059
+ const signedEmail = typeof payload.customerEmail === "string" ? payload.customerEmail : void 0;
13060
+ if (signedEmail !== void 0 || account.customerEmail !== void 0) {
13061
+ if (signedEmail !== account.customerEmail) {
13062
+ return logResolved(anonymousFallback("binding-email"), {
13063
+ reason: "binding-email"
13064
+ });
13065
+ }
13066
+ }
13067
+ const issuedRaw = payload.issued;
13068
+ const expiresRaw = payload.expires;
13069
+ if (typeof issuedRaw !== "string" || typeof expiresRaw !== "string") {
13070
+ return logResolved(anonymousFallback("temporal-shape"), {
13071
+ reason: "temporal-shape"
13072
+ });
13073
+ }
13074
+ const issued = Date.parse(issuedRaw);
13075
+ const expires = Date.parse(expiresRaw);
13076
+ const now = Date.now();
13077
+ if (Number.isNaN(issued) || Number.isNaN(expires)) {
13078
+ return logResolved(anonymousFallback("temporal-shape"), {
13079
+ reason: "temporal-shape"
13080
+ });
13081
+ }
13082
+ if (issued > now) {
13083
+ return logResolved(anonymousFallback("clock-skew"), {
13084
+ reason: "clock-skew"
13085
+ });
13086
+ }
13087
+ const tier = typeof payload.tier === "string" ? payload.tier : "";
13088
+ if (!VALID_TIERS.has(tier)) {
13089
+ return logResolved(anonymousFallback("tier-shape"), { reason: "tier-shape" });
13090
+ }
13091
+ const purchased = Array.isArray(payload.purchasedPlugins) ? payload.purchasedPlugins.filter((s) => typeof s === "string") : [];
13092
+ if (now <= expires) {
13093
+ return logResolved(
13094
+ {
13095
+ tier,
13096
+ purchasedPlugins: purchased,
13097
+ source: "signed",
13098
+ issued: issuedRaw,
13099
+ expires: expiresRaw
13100
+ },
13101
+ maybeTamperLine(account, tier)
13102
+ );
13103
+ }
13104
+ if (now <= expires + GRACE_MS) {
13105
+ return logResolved(
13106
+ {
13107
+ tier,
13108
+ purchasedPlugins: purchased,
13109
+ source: "signed-grace",
13110
+ issued: issuedRaw,
13111
+ expires: expiresRaw
13112
+ },
13113
+ maybeTamperLine(account, tier)
13114
+ );
13115
+ }
13116
+ return logResolved(anonymousFallback("expired"), { reason: "expired" });
13117
+ }
13118
+ function implicitTrust(account) {
13119
+ const tier = typeof account.tier === "string" ? account.tier : "";
13120
+ const purchased = Array.isArray(account.purchasedPlugins) ? account.purchasedPlugins : [];
13121
+ return {
13122
+ tier,
13123
+ purchasedPlugins: purchased,
13124
+ source: "implicit-trust",
13125
+ issued: null,
13126
+ expires: null
13127
+ };
13128
+ }
13129
+ function anonymousFallback(_reason) {
13130
+ return {
13131
+ tier: "solo",
13132
+ purchasedPlugins: [],
13133
+ source: "anonymous-fallback",
13134
+ issued: null,
13135
+ expires: null
13136
+ };
13137
+ }
13138
+ function maybeTamperLine(account, signedTier) {
13139
+ if (typeof account.tier === "string" && account.tier !== signedTier) {
13140
+ return { reason: "tampered", diskTier: account.tier, signedTier };
13141
+ }
13142
+ return null;
13143
+ }
13144
+ function logResolved(result, detail) {
13145
+ console.error(
13146
+ `[entitlement] resolved: tier=${result.tier} plugins=${result.purchasedPlugins.length} source=${result.source} issued=${result.issued ?? "-"} expires=${result.expires ?? "-"}`
13147
+ );
13148
+ if (detail && "diskTier" in detail) {
13149
+ console.error(
13150
+ `[entitlement] tampered: tier-on-disk=${detail.diskTier} signed=${detail.signedTier} action=using-signed`
13151
+ );
13152
+ } else if (detail && result.source === "anonymous-fallback") {
13153
+ const action = detail.reason === "expired" || detail.reason === "missing" ? "lock" : "degrade";
13154
+ console.error(
13155
+ `[entitlement] verify-failed: reason=${detail.reason} action=${action}`
13156
+ );
13157
+ }
13158
+ return result;
13159
+ }
13160
+
12938
13161
  // server/index.ts
12939
- import { existsSync as existsSyncBoot } from "fs";
13162
+ import { existsSync as existsSyncBoot, readFileSync as readFileSyncBoot, writeFileSync as writeFileSyncBoot } from "fs";
12940
13163
  import { resolve as resolveBoot } from "path";
12941
13164
  function requestIsTlsTerminated(c) {
12942
13165
  const remote = c.env?.incoming?.socket?.remoteAddress ?? "";
@@ -12954,12 +13177,12 @@ function clientFrom(c) {
12954
13177
  var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
12955
13178
  var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join12(PLATFORM_ROOT7, "config", "brand.json") : "";
12956
13179
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
12957
- if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
13180
+ if (BRAND_JSON_PATH && !existsSync25(BRAND_JSON_PATH)) {
12958
13181
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
12959
13182
  }
12960
- if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
13183
+ if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
12961
13184
  try {
12962
- const parsed = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
13185
+ const parsed = JSON.parse(readFileSync19(BRAND_JSON_PATH, "utf-8"));
12963
13186
  BRAND = { ...BRAND, ...parsed };
12964
13187
  } catch (err) {
12965
13188
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -12981,8 +13204,8 @@ var brandLoginOpts = {
12981
13204
  var ALIAS_DOMAINS_PATH2 = join12(homedir3(), BRAND.configDir, "alias-domains.json");
12982
13205
  function loadAliasDomains() {
12983
13206
  try {
12984
- if (!existsSync24(ALIAS_DOMAINS_PATH2)) return null;
12985
- const parsed = JSON.parse(readFileSync18(ALIAS_DOMAINS_PATH2, "utf-8"));
13207
+ if (!existsSync25(ALIAS_DOMAINS_PATH2)) return null;
13208
+ const parsed = JSON.parse(readFileSync19(ALIAS_DOMAINS_PATH2, "utf-8"));
12986
13209
  if (!Array.isArray(parsed)) {
12987
13210
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
12988
13211
  return null;
@@ -13356,20 +13579,20 @@ app39.get("/agent-assets/:slug/:filename", (c) => {
13356
13579
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
13357
13580
  return c.text("Not found", 404);
13358
13581
  }
13359
- const filePath = resolve21(account.accountDir, "agents", slug, "assets", filename);
13360
- const expectedDir = resolve21(account.accountDir, "agents", slug, "assets");
13582
+ const filePath = resolve22(account.accountDir, "agents", slug, "assets", filename);
13583
+ const expectedDir = resolve22(account.accountDir, "agents", slug, "assets");
13361
13584
  if (!filePath.startsWith(expectedDir + "/")) {
13362
13585
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
13363
13586
  return c.text("Forbidden", 403);
13364
13587
  }
13365
- if (!existsSync24(filePath)) {
13588
+ if (!existsSync25(filePath)) {
13366
13589
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
13367
13590
  return c.text("Not found", 404);
13368
13591
  }
13369
13592
  const ext = "." + filename.split(".").pop()?.toLowerCase();
13370
13593
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
13371
13594
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
13372
- const body = readFileSync18(filePath);
13595
+ const body = readFileSync19(filePath);
13373
13596
  return c.body(body, 200, {
13374
13597
  "Content-Type": contentType,
13375
13598
  "Cache-Control": "public, max-age=3600"
@@ -13386,20 +13609,20 @@ app39.get("/generated/:filename", (c) => {
13386
13609
  console.error(`[generated] serve file=${filename} status=404`);
13387
13610
  return c.text("Not found", 404);
13388
13611
  }
13389
- const filePath = resolve21(account.accountDir, "generated", filename);
13390
- const expectedDir = resolve21(account.accountDir, "generated");
13612
+ const filePath = resolve22(account.accountDir, "generated", filename);
13613
+ const expectedDir = resolve22(account.accountDir, "generated");
13391
13614
  if (!filePath.startsWith(expectedDir + "/")) {
13392
13615
  console.error(`[generated] serve file=${filename} status=403`);
13393
13616
  return c.text("Forbidden", 403);
13394
13617
  }
13395
- if (!existsSync24(filePath)) {
13618
+ if (!existsSync25(filePath)) {
13396
13619
  console.error(`[generated] serve file=${filename} status=404`);
13397
13620
  return c.text("Not found", 404);
13398
13621
  }
13399
13622
  const ext = "." + filename.split(".").pop()?.toLowerCase();
13400
13623
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
13401
13624
  console.log(`[generated] serve file=${filename} status=200`);
13402
- const body = readFileSync18(filePath);
13625
+ const body = readFileSync19(filePath);
13403
13626
  return c.body(body, 200, {
13404
13627
  "Content-Type": contentType,
13405
13628
  "Cache-Control": "public, max-age=86400"
@@ -13409,9 +13632,9 @@ app39.route("/sites", sites_default);
13409
13632
  var htmlCache = /* @__PURE__ */ new Map();
13410
13633
  var brandLogoPath = "/brand/maxy-monochrome.png";
13411
13634
  var brandIconPath = "/brand/maxy-monochrome.png";
13412
- if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
13635
+ if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
13413
13636
  try {
13414
- const fullBrand = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
13637
+ const fullBrand = JSON.parse(readFileSync19(BRAND_JSON_PATH, "utf-8"));
13415
13638
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
13416
13639
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
13417
13640
  } catch {
@@ -13429,8 +13652,8 @@ function readInstalledVersion() {
13429
13652
  try {
13430
13653
  if (!PLATFORM_ROOT7) return "unknown";
13431
13654
  const versionFile = join12(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
13432
- if (!existsSync24(versionFile)) return "unknown";
13433
- const content = readFileSync18(versionFile, "utf-8").trim();
13655
+ if (!existsSync25(versionFile)) return "unknown";
13656
+ const content = readFileSync19(versionFile, "utf-8").trim();
13434
13657
  return content || "unknown";
13435
13658
  } catch {
13436
13659
  return "unknown";
@@ -13471,7 +13694,7 @@ var clientErrorReporterScript = `<script>
13471
13694
  function cachedHtml(file) {
13472
13695
  let html = htmlCache.get(file);
13473
13696
  if (!html) {
13474
- html = readFileSync18(resolve21(process.cwd(), "public", file), "utf-8");
13697
+ html = readFileSync19(resolve22(process.cwd(), "public", file), "utf-8");
13475
13698
  const productNameEsc = escapeHtml(BRAND.productName);
13476
13699
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
13477
13700
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -13490,13 +13713,13 @@ function loadBrandingCache(agentSlug) {
13490
13713
  const configDir2 = join12(homedir3(), BRAND.configDir);
13491
13714
  try {
13492
13715
  const accountJsonPath = join12(configDir2, "account.json");
13493
- if (!existsSync24(accountJsonPath)) return null;
13494
- const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
13716
+ if (!existsSync25(accountJsonPath)) return null;
13717
+ const account = JSON.parse(readFileSync19(accountJsonPath, "utf-8"));
13495
13718
  const accountId = account.accountId;
13496
13719
  if (!accountId) return null;
13497
13720
  const cachePath = join12(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
13498
- if (!existsSync24(cachePath)) return null;
13499
- return JSON.parse(readFileSync18(cachePath, "utf-8"));
13721
+ if (!existsSync25(cachePath)) return null;
13722
+ return JSON.parse(readFileSync19(cachePath, "utf-8"));
13500
13723
  } catch {
13501
13724
  return null;
13502
13725
  }
@@ -13505,8 +13728,8 @@ function resolveDefaultSlug() {
13505
13728
  try {
13506
13729
  const configDir2 = join12(homedir3(), BRAND.configDir);
13507
13730
  const accountJsonPath = join12(configDir2, "account.json");
13508
- if (!existsSync24(accountJsonPath)) return null;
13509
- const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
13731
+ if (!existsSync25(accountJsonPath)) return null;
13732
+ const account = JSON.parse(readFileSync19(accountJsonPath, "utf-8"));
13510
13733
  return account.defaultAgent || null;
13511
13734
  } catch {
13512
13735
  return null;
@@ -13579,7 +13802,7 @@ app39.use("/vnc-popout.html", logViewerFetch);
13579
13802
  app39.get("/vnc-popout.html", (c) => {
13580
13803
  let html = htmlCache.get("vnc-popout.html");
13581
13804
  if (!html) {
13582
- html = readFileSync18(resolve21(process.cwd(), "public", "vnc-popout.html"), "utf-8");
13805
+ html = readFileSync19(resolve22(process.cwd(), "public", "vnc-popout.html"), "utf-8");
13583
13806
  const name = escapeHtml(BRAND.productName);
13584
13807
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
13585
13808
  html = html.replace("</head>", ` ${brandScript}
@@ -13685,8 +13908,8 @@ try {
13685
13908
  (async () => {
13686
13909
  try {
13687
13910
  let userId = "";
13688
- if (existsSync24(USERS_FILE)) {
13689
- const users = JSON.parse(readFileSync18(USERS_FILE, "utf-8").trim() || "[]");
13911
+ if (existsSync25(USERS_FILE)) {
13912
+ const users = JSON.parse(readFileSync19(USERS_FILE, "utf-8").trim() || "[]");
13690
13913
  userId = users[0]?.userId ?? "";
13691
13914
  }
13692
13915
  await backfillNullUserIdConversations(userId);
@@ -13703,8 +13926,8 @@ try {
13703
13926
  })();
13704
13927
  (async () => {
13705
13928
  try {
13706
- if (!existsSync24(USERS_FILE)) return;
13707
- const usersRaw = readFileSync18(USERS_FILE, "utf-8").trim();
13929
+ if (!existsSync25(USERS_FILE)) return;
13930
+ const usersRaw = readFileSync19(USERS_FILE, "utf-8").trim();
13708
13931
  if (!usersRaw) return;
13709
13932
  const users = JSON.parse(usersRaw);
13710
13933
  const userId = users[0]?.userId;
@@ -13742,25 +13965,31 @@ var configDirForWhatsApp = basename4(MAXY_DIR) || ".maxy";
13742
13965
  var bootAccount = resolveAccount();
13743
13966
  var bootAccountConfig = bootAccount?.config;
13744
13967
  var bootPublicAgent = bootAccount ? resolvePublicAgent(bootAccount.accountDir, { accountId: bootAccount.accountId })?.slug ?? null : null;
13968
+ if (bootAccount) {
13969
+ const accountJsonPath = resolveBoot(bootAccount.accountDir, "account.json");
13970
+ if (existsSyncBoot(accountJsonPath)) {
13971
+ try {
13972
+ const parsed = JSON.parse(readFileSyncBoot(accountJsonPath, "utf-8"));
13973
+ if ("purchasedPlugins" in parsed) {
13974
+ delete parsed.purchasedPlugins;
13975
+ writeFileSyncBoot(accountJsonPath, JSON.stringify(parsed, null, 2) + "\n");
13976
+ console.log(`[account-scrub] removed-purchasedPlugins=true accountId=${bootAccount.accountId}`);
13977
+ }
13978
+ } catch (err) {
13979
+ console.error(`[account-scrub] read/write failed accountId=${bootAccount.accountId} \u2014 ${err instanceof Error ? err.message : String(err)}`);
13980
+ }
13981
+ }
13982
+ }
13745
13983
  var bootEntitlement = bootAccountConfig ? resolveEntitlement(
13746
13984
  { configDir: MAXY_DIR, platformRoot: PLATFORM_ROOT, commercialMode: COMMERCIAL_MODE },
13747
13985
  {
13748
13986
  accountId: typeof bootAccountConfig.accountId === "string" ? bootAccountConfig.accountId : "",
13749
13987
  customerEmail: typeof bootAccountConfig.customerEmail === "string" ? bootAccountConfig.customerEmail : void 0,
13750
- tier: typeof bootAccountConfig.tier === "string" ? bootAccountConfig.tier : void 0,
13751
- purchasedPlugins: Array.isArray(bootAccountConfig.purchasedPlugins) ? bootAccountConfig.purchasedPlugins : void 0
13988
+ tier: typeof bootAccountConfig.tier === "string" ? bootAccountConfig.tier : void 0
13752
13989
  }
13753
13990
  ) : null;
13754
- autoDeliverPremiumPlugins(
13755
- bootEntitlement?.purchasedPlugins ?? void 0,
13756
- bootAccount?.accountDir,
13757
- bootAccount?.config
13758
- );
13759
- reconcileEnabledPlugins(
13760
- bootAccount?.accountDir,
13761
- bootAccount?.config,
13762
- bootEntitlement?.purchasedPlugins ?? void 0
13763
- );
13991
+ autoDeliverPremiumPlugins();
13992
+ reconcileEnabledPlugins(bootAccount?.accountDir, bootAccount?.config);
13764
13993
  (async () => {
13765
13994
  if (!bootAccount) return;
13766
13995
  try {
@@ -13806,11 +14035,11 @@ for (const dir of bootEnabled) {
13806
14035
  bootDelivered.push(dir);
13807
14036
  }
13808
14037
  var bootEnabledNotDelivered = [];
13809
- if (bootEntitlement?.purchasedPlugins?.length) {
14038
+ var bootBundles = walkPremiumBundles();
14039
+ if (bootBundles.length > 0) {
13810
14040
  const enabledSet = new Set(bootEnabled);
13811
- for (const purchased of bootEntitlement.purchasedPlugins) {
13812
- const bundlePath = resolveBoot(PLATFORM_ROOT, "..", "premium-plugins", purchased, "BUNDLE.md");
13813
- for (const sub of readBundleSubPlugins(bundlePath)) {
14041
+ for (const { subs } of bootBundles) {
14042
+ for (const sub of subs) {
13814
14043
  if (!enabledSet.has(sub)) bootEnabledNotDelivered.push(sub);
13815
14044
  }
13816
14045
  }
@@ -13824,7 +14053,7 @@ if (bootAccountConfig?.whatsapp) {
13824
14053
  }
13825
14054
  init({
13826
14055
  configDir: configDirForWhatsApp,
13827
- platformRoot: resolve21(process.env.MAXY_PLATFORM_ROOT ?? join12(__dirname, "..")),
14056
+ platformRoot: resolve22(process.env.MAXY_PLATFORM_ROOT ?? join12(__dirname, "..")),
13828
14057
  accountConfig: bootAccountConfig,
13829
14058
  onMessage: async (msg) => {
13830
14059
  try {