@rubytech/create-maxy 1.0.477 → 1.0.478

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.
@@ -2877,6 +2877,8 @@ if (platformRoot) {
2877
2877
  }
2878
2878
  var MAXY_DIR = resolve(homedir(), configDirName);
2879
2879
  var PIN_FILE = resolve(MAXY_DIR, ".admin-pin");
2880
+ var PLATFORM_ROOT = process.env.MAXY_PLATFORM_ROOT ?? resolve(process.cwd(), "..");
2881
+ var USERS_FILE = resolve(PLATFORM_ROOT, "config", "users.json");
2880
2882
  var LOG_DIR = resolve(MAXY_DIR, "logs");
2881
2883
  var BIN_DIR = resolve(MAXY_DIR, "bin");
2882
2884
  var API_KEY_FILE = resolve(MAXY_DIR, ".anthropic-api-key");
@@ -3144,8 +3146,8 @@ import { spawnSync, execFileSync } from "child_process";
3144
3146
  import { createConnection as createConnection2 } from "net";
3145
3147
  import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
3146
3148
  import { resolve as resolve2 } from "path";
3147
- var PLATFORM_ROOT = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "..");
3148
- var VNC_SCRIPT = resolve2(PLATFORM_ROOT, "scripts/vnc.sh");
3149
+ var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "..");
3150
+ var VNC_SCRIPT = resolve2(PLATFORM_ROOT2, "scripts/vnc.sh");
3149
3151
  function sleep(ms) {
3150
3152
  return new Promise((r) => setTimeout(r, ms));
3151
3153
  }
@@ -3277,11 +3279,11 @@ import { randomUUID } from "crypto";
3277
3279
  import { spawn } from "child_process";
3278
3280
  import { readFileSync as readFileSync4 } from "fs";
3279
3281
  import { resolve as resolve3 } from "path";
3280
- var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
3282
+ var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
3281
3283
  var driver = null;
3282
3284
  function readPassword() {
3283
3285
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
3284
- const passwordFile = resolve3(PLATFORM_ROOT2, "config/.neo4j-password");
3286
+ const passwordFile = resolve3(PLATFORM_ROOT3, "config/.neo4j-password");
3285
3287
  try {
3286
3288
  return readFileSync4(passwordFile, "utf-8").trim();
3287
3289
  } catch {
@@ -4270,11 +4272,11 @@ function agentLogStream(name, accountDir) {
4270
4272
  }
4271
4273
  return createWriteStream(resolve4(logDir, `${name}-${date5}.log`), { flags: "a" });
4272
4274
  }
4273
- var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
4274
- var ACCOUNTS_DIR = resolve4(PLATFORM_ROOT3, "..", "data/accounts");
4275
- if (!existsSync5(PLATFORM_ROOT3)) {
4275
+ var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
4276
+ var ACCOUNTS_DIR = resolve4(PLATFORM_ROOT4, "..", "data/accounts");
4277
+ if (!existsSync5(PLATFORM_ROOT4)) {
4276
4278
  throw new Error(
4277
- `PLATFORM_ROOT does not exist: ${PLATFORM_ROOT3}
4279
+ `PLATFORM_ROOT does not exist: ${PLATFORM_ROOT4}
4278
4280
  Set the MAXY_PLATFORM_ROOT environment variable to the absolute path of the platform directory.`
4279
4281
  );
4280
4282
  }
@@ -4452,7 +4454,7 @@ function resolveAgentConfig(accountDir, agentName) {
4452
4454
  return { model, plugins, status, displayName, image, imageShape, showAgentName, knowledge, knowledgeBaked, liveMemory, knowledgeKeywords, budget, accessMode };
4453
4455
  }
4454
4456
  function parsePluginFrontmatter(pluginDir) {
4455
- const pluginPath = resolve4(PLATFORM_ROOT3, "plugins", pluginDir, "PLUGIN.md");
4457
+ const pluginPath = resolve4(PLATFORM_ROOT4, "plugins", pluginDir, "PLUGIN.md");
4456
4458
  if (!existsSync5(pluginPath)) return null;
4457
4459
  let raw2;
4458
4460
  try {
@@ -4513,7 +4515,7 @@ function parsePluginFrontmatter(pluginDir) {
4513
4515
  };
4514
4516
  }
4515
4517
  function assemblePublicPluginContent(pluginDir) {
4516
- const pluginRoot = resolve4(PLATFORM_ROOT3, "plugins", pluginDir);
4518
+ const pluginRoot = resolve4(PLATFORM_ROOT4, "plugins", pluginDir);
4517
4519
  const pluginPath = resolve4(pluginRoot, "PLUGIN.md");
4518
4520
  let raw2;
4519
4521
  try {
@@ -4607,7 +4609,7 @@ function assemblePublicPluginContent(pluginDir) {
4607
4609
  return { body: parts.join("\n"), skillCount, refCount };
4608
4610
  }
4609
4611
  function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
4610
- const pluginsDir = resolve4(PLATFORM_ROOT3, "plugins");
4612
+ const pluginsDir = resolve4(PLATFORM_ROOT4, "plugins");
4611
4613
  let dirs;
4612
4614
  try {
4613
4615
  dirs = readdirSync(pluginsDir);
@@ -4687,14 +4689,14 @@ var mcpToolsCache = /* @__PURE__ */ new Map();
4687
4689
  function fetchMcpToolsList(pluginDir) {
4688
4690
  const cached2 = mcpToolsCache.get(pluginDir);
4689
4691
  if (cached2) return Promise.resolve(cached2);
4690
- const serverPath = resolve4(PLATFORM_ROOT3, "plugins", pluginDir, "mcp/dist/index.js");
4692
+ const serverPath = resolve4(PLATFORM_ROOT4, "plugins", pluginDir, "mcp/dist/index.js");
4691
4693
  if (!existsSync5(serverPath)) return Promise.resolve([]);
4692
4694
  const startMs = Date.now();
4693
4695
  return new Promise((resolvePromise) => {
4694
4696
  const proc = spawn2(process.execPath, [serverPath], {
4695
4697
  env: {
4696
4698
  ...process.env,
4697
- PLATFORM_ROOT: PLATFORM_ROOT3,
4699
+ PLATFORM_ROOT: PLATFORM_ROOT4,
4698
4700
  ACCOUNT_ID: "__toolslist__",
4699
4701
  PLATFORM_PORT: process.env.PORT ?? "19200"
4700
4702
  }
@@ -4778,7 +4780,7 @@ function fetchMcpToolsList(pluginDir) {
4778
4780
  });
4779
4781
  }
4780
4782
  async function buildPluginManifest(enabledPlugins) {
4781
- const pluginsDir = resolve4(PLATFORM_ROOT3, "plugins");
4783
+ const pluginsDir = resolve4(PLATFORM_ROOT4, "plugins");
4782
4784
  let dirs;
4783
4785
  try {
4784
4786
  dirs = readdirSync(pluginsDir);
@@ -4849,7 +4851,7 @@ async function buildPluginManifest(enabledPlugins) {
4849
4851
  toolLines.push(desc ? ` ${tool.name} \u2014 ${desc}` : ` ${tool.name}`);
4850
4852
  }
4851
4853
  } else if (parsed.tools.length > 0) {
4852
- const serverPath = resolve4(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
4854
+ const serverPath = resolve4(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
4853
4855
  if (existsSync5(serverPath)) {
4854
4856
  fallbackSourced++;
4855
4857
  console.error(`[plugin-manifest] ${dir}: tools/list empty \u2014 fallback to frontmatter (${parsed.tools.length} tools)`);
@@ -4924,10 +4926,37 @@ function validateComponentData(componentName, data) {
4924
4926
  function getDefaultAccountId() {
4925
4927
  return resolveAccount()?.accountId ?? null;
4926
4928
  }
4929
+ function resolveUserAccounts(userId) {
4930
+ if (!existsSync5(ACCOUNTS_DIR)) return [];
4931
+ const results = [];
4932
+ const entries = readdirSync(ACCOUNTS_DIR, { withFileTypes: true });
4933
+ for (const entry of entries) {
4934
+ if (!entry.isDirectory()) continue;
4935
+ const configPath2 = resolve4(ACCOUNTS_DIR, entry.name, "account.json");
4936
+ if (!existsSync5(configPath2)) continue;
4937
+ let config2;
4938
+ try {
4939
+ config2 = JSON.parse(readFileSync5(configPath2, "utf-8"));
4940
+ } catch {
4941
+ console.error(`[session] account.json corrupt at ${configPath2} \u2014 skipping`);
4942
+ continue;
4943
+ }
4944
+ const adminEntry = config2.admins?.find((a) => a.userId === userId);
4945
+ if (adminEntry) {
4946
+ results.push({
4947
+ accountId: config2.accountId,
4948
+ accountDir: resolve4(ACCOUNTS_DIR, entry.name),
4949
+ config: config2,
4950
+ role: adminEntry.role
4951
+ });
4952
+ }
4953
+ }
4954
+ return results;
4955
+ }
4927
4956
  var sessionStore = /* @__PURE__ */ new Map();
4928
4957
  setSessionStoreRef(sessionStore);
4929
- function registerSession(sessionKey, agentType, accountId, agentName) {
4930
- sessionStore.set(sessionKey, { createdAt: Date.now(), agentType, accountId, agentName });
4958
+ function registerSession(sessionKey, agentType, accountId, agentName, userId, userName) {
4959
+ sessionStore.set(sessionKey, { createdAt: Date.now(), agentType, accountId, agentName, userId, userName });
4931
4960
  }
4932
4961
  function registerResumedSession(sessionKey, accountId, agentName, conversationId, messages) {
4933
4962
  const messageHistory = messages.map((m) => ({
@@ -5076,38 +5105,38 @@ function getMcpServers(accountId, enabledPlugins) {
5076
5105
  const servers = {
5077
5106
  "memory": {
5078
5107
  command: "node",
5079
- args: [resolve4(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js")],
5080
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT3 }
5108
+ args: [resolve4(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js")],
5109
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
5081
5110
  },
5082
5111
  "contacts": {
5083
5112
  command: "node",
5084
- args: [resolve4(PLATFORM_ROOT3, "plugins/contacts/mcp/dist/index.js")],
5085
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT3 }
5113
+ args: [resolve4(PLATFORM_ROOT4, "plugins/contacts/mcp/dist/index.js")],
5114
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
5086
5115
  },
5087
5116
  "whatsapp": {
5088
5117
  command: "node",
5089
- args: [resolve4(PLATFORM_ROOT3, "plugins/whatsapp/mcp/dist/index.js")],
5090
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT3, PLATFORM_PORT: process.env.PORT ?? "19200" }
5118
+ args: [resolve4(PLATFORM_ROOT4, "plugins/whatsapp/mcp/dist/index.js")],
5119
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4, PLATFORM_PORT: process.env.PORT ?? "19200" }
5091
5120
  },
5092
5121
  "admin": {
5093
5122
  command: "node",
5094
- args: [resolve4(PLATFORM_ROOT3, "plugins/admin/mcp/dist/index.js")],
5095
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT3, PLATFORM_PORT: process.env.PORT ?? "19200" }
5123
+ args: [resolve4(PLATFORM_ROOT4, "plugins/admin/mcp/dist/index.js")],
5124
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4, PLATFORM_PORT: process.env.PORT ?? "19200" }
5096
5125
  },
5097
5126
  "scheduling": {
5098
5127
  command: "node",
5099
- args: [resolve4(PLATFORM_ROOT3, "plugins/scheduling/mcp/dist/index.js")],
5100
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT3 }
5128
+ args: [resolve4(PLATFORM_ROOT4, "plugins/scheduling/mcp/dist/index.js")],
5129
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
5101
5130
  },
5102
5131
  "tasks": {
5103
5132
  command: "node",
5104
- args: [resolve4(PLATFORM_ROOT3, "plugins/tasks/mcp/dist/index.js")],
5105
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT3 }
5133
+ args: [resolve4(PLATFORM_ROOT4, "plugins/tasks/mcp/dist/index.js")],
5134
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
5106
5135
  },
5107
5136
  "email": {
5108
5137
  command: "node",
5109
- args: [resolve4(PLATFORM_ROOT3, "plugins/email/mcp/dist/index.js")],
5110
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT3 }
5138
+ args: [resolve4(PLATFORM_ROOT4, "plugins/email/mcp/dist/index.js")],
5139
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
5111
5140
  },
5112
5141
  // Playwright MCP server — browser automation for browser-specialist.
5113
5142
  // Key matches Claude Code's plugin naming: plugin_{plugin}_{server} so tools
@@ -5123,8 +5152,8 @@ function getMcpServers(accountId, enabledPlugins) {
5123
5152
  if (process.env.TELEGRAM_PUBLIC_BOT_TOKEN) {
5124
5153
  servers["telegram"] = {
5125
5154
  command: "node",
5126
- args: [resolve4(PLATFORM_ROOT3, "plugins/telegram/mcp/dist/index.js")],
5127
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT3 }
5155
+ args: [resolve4(PLATFORM_ROOT4, "plugins/telegram/mcp/dist/index.js")],
5156
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
5128
5157
  };
5129
5158
  } else {
5130
5159
  console.error("[plugins] telegram MCP: skipped (no TELEGRAM_PUBLIC_BOT_TOKEN)");
@@ -5141,14 +5170,14 @@ function getMcpServers(accountId, enabledPlugins) {
5141
5170
  if (!tunnelConfigured) {
5142
5171
  servers["cloudflare"] = {
5143
5172
  command: "node",
5144
- args: [resolve4(PLATFORM_ROOT3, "plugins/cloudflare/mcp/dist/index.js")],
5145
- env: { PLATFORM_ROOT: PLATFORM_ROOT3 }
5173
+ args: [resolve4(PLATFORM_ROOT4, "plugins/cloudflare/mcp/dist/index.js")],
5174
+ env: { PLATFORM_ROOT: PLATFORM_ROOT4 }
5146
5175
  };
5147
5176
  } else {
5148
5177
  console.error("[plugins] cloudflare MCP: skipped (tunnel already configured)");
5149
5178
  }
5150
5179
  if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
5151
- const pluginsDir = resolve4(PLATFORM_ROOT3, "plugins");
5180
+ const pluginsDir = resolve4(PLATFORM_ROOT4, "plugins");
5152
5181
  let dirs;
5153
5182
  try {
5154
5183
  dirs = readdirSync(pluginsDir);
@@ -5171,12 +5200,12 @@ function getMcpServers(accountId, enabledPlugins) {
5171
5200
  continue;
5172
5201
  }
5173
5202
  }
5174
- const mcpEntry = resolve4(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
5203
+ const mcpEntry = resolve4(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
5175
5204
  if (!existsSync5(mcpEntry)) continue;
5176
5205
  servers[dir] = {
5177
5206
  command: "node",
5178
5207
  args: [mcpEntry],
5179
- env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT3 }
5208
+ env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
5180
5209
  };
5181
5210
  console.log(`[plugins] optional MCP server started: ${dir}`);
5182
5211
  }
@@ -5265,7 +5294,7 @@ var ADMIN_CORE_TOOLS = [
5265
5294
  function getAdminAllowedTools(enabledPlugins) {
5266
5295
  const tools = [...ADMIN_CORE_TOOLS];
5267
5296
  if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
5268
- const pluginsDir = resolve4(PLATFORM_ROOT3, "plugins");
5297
+ const pluginsDir = resolve4(PLATFORM_ROOT4, "plugins");
5269
5298
  let dirs;
5270
5299
  try {
5271
5300
  dirs = readdirSync(pluginsDir);
@@ -5358,7 +5387,7 @@ ${message.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`
5358
5387
  }
5359
5388
  }
5360
5389
  async function fetchMemoryContext(accountId, query, sessionKey, options) {
5361
- const serverPath = resolve4(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
5390
+ const serverPath = resolve4(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
5362
5391
  if (!existsSync5(serverPath)) {
5363
5392
  console.error(`[fetchMemoryContext] MCP server not found: ${serverPath}`);
5364
5393
  return null;
@@ -5369,7 +5398,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
5369
5398
  env: {
5370
5399
  ...process.env,
5371
5400
  ACCOUNT_ID: accountId,
5372
- PLATFORM_ROOT: PLATFORM_ROOT3,
5401
+ PLATFORM_ROOT: PLATFORM_ROOT4,
5373
5402
  READ_ONLY: "true",
5374
5403
  ALLOWED_SCOPES: "public,shared",
5375
5404
  ...sessionKey ? { SESSION_ID: sessionKey } : {},
@@ -5456,7 +5485,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
5456
5485
  }
5457
5486
  async function compactTrimmedMessages(accountId, trimmedMessages) {
5458
5487
  if (trimmedMessages.length === 0) return true;
5459
- const serverPath = resolve4(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
5488
+ const serverPath = resolve4(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
5460
5489
  if (!existsSync5(serverPath)) return false;
5461
5490
  const briefing = trimmedMessages.map((m) => `[${m.role.toUpperCase()}] ${m.content}`).join("\n\n");
5462
5491
  return new Promise((resolvePromise) => {
@@ -5888,7 +5917,7 @@ async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSes
5888
5917
  const proc = spawn2("claude", args, {
5889
5918
  cwd: accountDir,
5890
5919
  stdio: ["ignore", "pipe", "pipe"],
5891
- env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT3, ACCOUNT_DIR: accountDir }
5920
+ env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT4, ACCOUNT_DIR: accountDir }
5892
5921
  });
5893
5922
  const stderrLog = agentLogStream("claude-agent-compaction-stderr", accountDir);
5894
5923
  proc.stderr?.pipe(stderrLog);
@@ -6529,7 +6558,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
6529
6558
  const proc = spawn2("claude", args, {
6530
6559
  cwd: accountDir,
6531
6560
  stdio: ["ignore", "pipe", "pipe"],
6532
- env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT3, ACCOUNT_DIR: accountDir }
6561
+ env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT4, ACCOUNT_DIR: accountDir }
6533
6562
  });
6534
6563
  const stderrLog = agentLogStream("claude-agent-stderr", accountDir);
6535
6564
  proc.stderr?.pipe(stderrLog);
@@ -6778,7 +6807,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
6778
6807
  const proc = spawn2("claude", args, {
6779
6808
  cwd: accountDir,
6780
6809
  stdio: ["ignore", "pipe", "pipe"],
6781
- env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT3, ACCOUNT_DIR: accountDir }
6810
+ env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT4, ACCOUNT_DIR: accountDir }
6782
6811
  });
6783
6812
  const stderrLog = agentLogStream("claude-agent-stderr", accountDir);
6784
6813
  proc.stderr?.pipe(stderrLog);
@@ -7606,8 +7635,8 @@ import { randomUUID as randomUUID2 } from "crypto";
7606
7635
  import { mkdir, readFile, stat, writeFile } from "fs/promises";
7607
7636
  import { realpathSync } from "fs";
7608
7637
  import { resolve as resolve6, extname, basename } from "path";
7609
- var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "../platform");
7610
- var ATTACHMENTS_ROOT = resolve6(PLATFORM_ROOT4, "..", "data/uploads");
7638
+ var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "../platform");
7639
+ var ATTACHMENTS_ROOT = resolve6(PLATFORM_ROOT5, "..", "data/uploads");
7611
7640
  var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
7612
7641
  "image/jpeg",
7613
7642
  "image/png",
@@ -8463,11 +8492,11 @@ import neo4j2 from "neo4j-driver";
8463
8492
  import { readFileSync as readFileSync7 } from "fs";
8464
8493
  import { resolve as resolve7 } from "path";
8465
8494
  import { randomUUID as randomUUID3, randomInt } from "crypto";
8466
- var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve7(process.cwd(), "..");
8495
+ var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve7(process.cwd(), "..");
8467
8496
  var driver2 = null;
8468
8497
  function readPassword2() {
8469
8498
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
8470
- const passwordFile = resolve7(PLATFORM_ROOT5, "config/.neo4j-password");
8499
+ const passwordFile = resolve7(PLATFORM_ROOT6, "config/.neo4j-password");
8471
8500
  try {
8472
8501
  return readFileSync7(passwordFile, "utf-8").trim();
8473
8502
  } catch {
@@ -25619,7 +25648,7 @@ import { readFile as readFile2, stat as stat2 } from "fs/promises";
25619
25648
  import { realpathSync as realpathSync2 } from "fs";
25620
25649
  import { resolve as resolve11, basename as basename2 } from "path";
25621
25650
  var TAG11 = "[whatsapp:api]";
25622
- var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT || "";
25651
+ var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
25623
25652
  async function POST15(req) {
25624
25653
  try {
25625
25654
  const body = await req.json();
@@ -25628,13 +25657,13 @@ async function POST15(req) {
25628
25657
  if (!to || !filePath) {
25629
25658
  return Response.json({ error: "Missing required fields: to, filePath" }, { status: 400 });
25630
25659
  }
25631
- if (!maxyAccountId || !PLATFORM_ROOT6) {
25660
+ if (!maxyAccountId || !PLATFORM_ROOT7) {
25632
25661
  return Response.json(
25633
25662
  { error: "Cannot validate file path: missing account or platform context" },
25634
25663
  { status: 400 }
25635
25664
  );
25636
25665
  }
25637
- const accountDir = resolve11(PLATFORM_ROOT6, "..", "data/accounts", maxyAccountId);
25666
+ const accountDir = resolve11(PLATFORM_ROOT7, "..", "data/accounts", maxyAccountId);
25638
25667
  let resolvedPath;
25639
25668
  try {
25640
25669
  resolvedPath = realpathSync2(filePath);
@@ -25877,35 +25906,93 @@ function getStoredPinHash() {
25877
25906
  }
25878
25907
  return null;
25879
25908
  }
25909
+ function readUsersFile() {
25910
+ if (!existsSync13(USERS_FILE)) return null;
25911
+ const raw2 = readFileSync13(USERS_FILE, "utf-8").trim();
25912
+ if (!raw2) return [];
25913
+ return JSON.parse(raw2);
25914
+ }
25880
25915
  async function POST19(req) {
25881
- const storedHash = getStoredPinHash();
25882
- if (!storedHash) {
25883
- return Response.json(
25884
- { error: "PIN not configured. Complete onboarding first." },
25885
- { status: 503 }
25886
- );
25887
- }
25888
25916
  let body;
25889
25917
  try {
25890
25918
  body = await req.json();
25891
25919
  } catch {
25892
25920
  return Response.json({ error: "Invalid request" }, { status: 400 });
25893
25921
  }
25922
+ if (!body.pin) {
25923
+ return Response.json({ error: "Invalid request" }, { status: 400 });
25924
+ }
25894
25925
  const inputHash = hashPin2(body.pin);
25895
- if (!body.pin || inputHash !== storedHash) {
25926
+ let users = null;
25927
+ try {
25928
+ users = readUsersFile();
25929
+ } catch (err) {
25930
+ console.error(`[session] users.json corrupt: ${err instanceof Error ? err.message : String(err)}`);
25931
+ return Response.json(
25932
+ { error: "User configuration is corrupt. Re-run the seed script." },
25933
+ { status: 503 }
25934
+ );
25935
+ }
25936
+ if (users !== null) {
25937
+ const matchedUser = users.find((u) => u.pin === inputHash);
25938
+ if (!matchedUser) {
25939
+ console.log(`[session] PIN auth failed: no matching user`);
25940
+ return Response.json({ error: "Invalid PIN" }, { status: 401 });
25941
+ }
25942
+ const { userId, name: userName } = matchedUser;
25943
+ const accounts = resolveUserAccounts(userId);
25944
+ if (accounts.length === 0) {
25945
+ console.log(`[session] user has no accounts: userId=${userId}`);
25946
+ return Response.json(
25947
+ { error: "No account access for this user." },
25948
+ { status: 403 }
25949
+ );
25950
+ }
25951
+ if (accounts.length > 1 && !body.accountId) {
25952
+ console.log(`[session] multi-account user: userId=${userId} accounts=${accounts.length}`);
25953
+ const accountList = await Promise.all(
25954
+ accounts.map(async (a) => {
25955
+ let businessName;
25956
+ try {
25957
+ const branding = await fetchBranding(a.accountId);
25958
+ businessName = branding?.name || void 0;
25959
+ } catch {
25960
+ }
25961
+ return { accountId: a.accountId, businessName, role: a.role };
25962
+ })
25963
+ );
25964
+ return Response.json({ accounts: accountList, userId, userName });
25965
+ }
25966
+ const selected = body.accountId ? accounts.find((a) => a.accountId === body.accountId) : accounts[0];
25967
+ if (!selected) {
25968
+ console.log(`[session] account selection invalid: userId=${userId} requested=${body.accountId}`);
25969
+ return Response.json({ error: "Invalid account selection." }, { status: 403 });
25970
+ }
25971
+ return createAdminSession(selected.accountId, selected.config.thinkingView, userId, userName);
25972
+ }
25973
+ const storedHash = getStoredPinHash();
25974
+ if (!storedHash) {
25975
+ return Response.json(
25976
+ { error: "PIN not configured. Complete onboarding first." },
25977
+ { status: 503 }
25978
+ );
25979
+ }
25980
+ if (inputHash !== storedHash) {
25896
25981
  console.log(`[session] PIN mismatch \u2014 stored=${storedHash.slice(0, 8)}\u2026 input=${inputHash.slice(0, 8)}\u2026 file=${PIN_FILE}`);
25897
25982
  return Response.json({ error: "Invalid PIN" }, { status: 401 });
25898
25983
  }
25899
25984
  const accountId = getDefaultAccountId() ?? "default";
25900
- const sessionKey = crypto.randomUUID();
25901
- registerSession(sessionKey, "admin", accountId);
25985
+ return createAdminSession(accountId);
25986
+ }
25987
+ async function createAdminSession(accountId, thinkingView, userId, userName) {
25902
25988
  const account = resolveAccount();
25903
- const thinkingView = account?.config.thinkingView ?? "default";
25989
+ const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
25990
+ const sessionKey = crypto.randomUUID();
25991
+ registerSession(sessionKey, "admin", accountId, void 0, userId, userName);
25904
25992
  let onboardingComplete = true;
25905
25993
  try {
25906
25994
  const step = await loadOnboardingStep(accountId);
25907
25995
  onboardingComplete = step === null || step >= 7;
25908
- console.log(`[session] accountId=${accountId} onboardingComplete=${onboardingComplete}`);
25909
25996
  } catch (err) {
25910
25997
  console.error(`[session] onboarding query failed: ${err instanceof Error ? err.message : String(err)}`);
25911
25998
  }
@@ -25915,11 +26002,13 @@ async function POST19(req) {
25915
26002
  businessName = branding?.name || void 0;
25916
26003
  } catch {
25917
26004
  }
25918
- console.log(`[session] businessName=${businessName ?? "\u2013"}`);
26005
+ console.log(`[session] admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId}`);
25919
26006
  return Response.json({
25920
26007
  session_key: sessionKey,
25921
26008
  agent_id: "admin",
25922
- thinkingView,
26009
+ userId,
26010
+ userName,
26011
+ thinkingView: effectiveThinkingView,
25923
26012
  onboardingComplete,
25924
26013
  businessName
25925
26014
  });
@@ -26433,10 +26522,10 @@ async function GET8() {
26433
26522
  // app/api/admin/version/route.ts
26434
26523
  import { readFileSync as readFileSync17, existsSync as existsSync17 } from "fs";
26435
26524
  import { resolve as resolve16, join as join7 } from "path";
26436
- var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
26525
+ var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
26437
26526
  var brandHostname = "maxy";
26438
26527
  var brandNpmPackage = "@rubytech/create-maxy";
26439
- var brandJsonPath = join7(PLATFORM_ROOT7, "config", "brand.json");
26528
+ var brandJsonPath = join7(PLATFORM_ROOT8, "config", "brand.json");
26440
26529
  if (existsSync17(brandJsonPath)) {
26441
26530
  try {
26442
26531
  const brand = JSON.parse(readFileSync17(brandJsonPath, "utf-8"));
@@ -26445,7 +26534,7 @@ if (existsSync17(brandJsonPath)) {
26445
26534
  } catch {
26446
26535
  }
26447
26536
  }
26448
- var VERSION_FILE = resolve16(PLATFORM_ROOT7, `config/.${brandHostname}-version`);
26537
+ var VERSION_FILE = resolve16(PLATFORM_ROOT8, `config/.${brandHostname}-version`);
26449
26538
  var NPM_PACKAGE = brandNpmPackage;
26450
26539
  var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
26451
26540
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -26509,10 +26598,10 @@ async function GET9() {
26509
26598
  import { spawn as spawn4 } from "child_process";
26510
26599
  import { existsSync as existsSync18, statSync as statSync4, writeFileSync as writeFileSync10, readFileSync as readFileSync18, openSync as openSync2, closeSync as closeSync2 } from "fs";
26511
26600
  import { resolve as resolve17, join as join8 } from "path";
26512
- var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
26601
+ var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
26513
26602
  var upgradePkg = "@rubytech/create-maxy";
26514
26603
  var upgradeHostname = "maxy";
26515
- var brandPath = join8(PLATFORM_ROOT8, "config", "brand.json");
26604
+ var brandPath = join8(PLATFORM_ROOT9, "config", "brand.json");
26516
26605
  if (existsSync18(brandPath)) {
26517
26606
  try {
26518
26607
  const brand = JSON.parse(readFileSync18(brandPath, "utf-8"));
@@ -26678,8 +26767,8 @@ async function POST23() {
26678
26767
  }
26679
26768
 
26680
26769
  // server/index.ts
26681
- var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
26682
- var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join9(PLATFORM_ROOT9, "config", "brand.json") : "";
26770
+ var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT || "";
26771
+ var BRAND_JSON_PATH = PLATFORM_ROOT10 ? join9(PLATFORM_ROOT10, "config", "brand.json") : "";
26683
26772
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
26684
26773
  if (BRAND_JSON_PATH && !existsSync19(BRAND_JSON_PATH)) {
26685
26774
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);