openmausbot 0.1.76 → 0.1.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/assets/{index-C6blGSq5.js → index-DeK-z9ld.js} +1 -1
  2. package/dist/assets/index-XdHJ-9CF.css +1 -0
  3. package/dist/assets/index-lhqE1umn.js +305 -0
  4. package/dist/index.html +2 -2
  5. package/dist-server/container-mcp.js +5 -1
  6. package/dist-server/drivers/agents-proxy.js +52 -1
  7. package/dist-server/enterprise/server/index.js +387 -29
  8. package/dist-server/index.js +1377 -459
  9. package/dist-server/openmausbot.js +725 -522
  10. package/dist-server/pair-cli.js +725 -522
  11. package/dist-server/server/agent-tool-policy.js +1 -0
  12. package/dist-server/server/browser-live.js +3 -0
  13. package/dist-server/server/config.js +16 -0
  14. package/dist-server/server/drivers/acp/core.js +3 -1
  15. package/dist-server/server/drivers/agents-proxy.js +58 -2
  16. package/dist-server/server/drivers/boxagent.js +1 -1
  17. package/dist-server/server/drivers/claude.js +3 -2
  18. package/dist-server/server/drivers/codex.js +3 -1
  19. package/dist-server/server/drivers/pi.js +4 -0
  20. package/dist-server/server/enterprise.js +38 -0
  21. package/dist-server/server/environment.js +3 -0
  22. package/dist-server/server/fleet-agent.js +57 -13
  23. package/dist-server/server/fleet-cli.js +248 -31
  24. package/dist-server/server/fleet.js +88 -14
  25. package/dist-server/server/hosted-contract.js +16 -0
  26. package/dist-server/server/index.js +671 -100
  27. package/dist-server/server/message-file.js +4 -0
  28. package/dist-server/server/provider-key-check.js +18 -1
  29. package/dist-server/server/request-auth.js +14 -5
  30. package/dist-server/server/sessions.js +115 -8
  31. package/dist-server/server/shared-computer-control.js +24 -0
  32. package/dist-server/server/shared-computers.js +131 -0
  33. package/dist-server/server/steer-queue.js +2 -0
  34. package/dist-server/server/store.js +20 -7
  35. package/dist-server/server/team-computers.js +108 -0
  36. package/dist-server/server/tool-summary.js +53 -1
  37. package/dist-server/server/workspace-backup.js +1 -0
  38. package/dist-server/shared/approval-mode.js +4 -1
  39. package/dist-server/shared/team-computer.js +1 -0
  40. package/dist-server/vps-container-mcp.js +5 -1
  41. package/enterprise/README.md +21 -3
  42. package/enterprise/server/index.js +387 -29
  43. package/package.json +1 -1
  44. package/dist/assets/index-BtT047dk.css +0 -1
  45. package/dist/assets/index-fKXFpB_J.js +0 -305
@@ -17619,9 +17619,9 @@ var init_schema = __esm({
17619
17619
  });
17620
17620
 
17621
17621
  // server/config.ts
17622
- import { readFileSync as readFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync5, renameSync as renameSync3 } from "node:fs";
17622
+ import { readFileSync as readFileSync3, mkdirSync as mkdirSync6, existsSync as existsSync5, renameSync as renameSync3 } from "node:fs";
17623
17623
  import { homedir as homedir4 } from "node:os";
17624
- import { join as join7 } from "node:path";
17624
+ import { join as join6 } from "node:path";
17625
17625
  function isValidSshAlias(value) {
17626
17626
  return typeof value === "string" && SSH_ALIAS.test(value);
17627
17627
  }
@@ -17754,7 +17754,7 @@ function parseStoredConfig(value) {
17754
17754
  function loadConfig() {
17755
17755
  let cfg = {};
17756
17756
  try {
17757
- cfg = parseStoredConfig(parseJson(readFileSync4(join7(DATA_DIR, "config.json"), "utf8")));
17757
+ cfg = parseStoredConfig(parseJson(readFileSync3(join6(DATA_DIR, "config.json"), "utf8")));
17758
17758
  } catch {
17759
17759
  }
17760
17760
  cfg.xai = { ...cfg.xai };
@@ -17790,10 +17790,10 @@ function stripWorkspaceCredentialEnv(env) {
17790
17790
  for (const key of WORKSPACE_CREDENTIAL_ENV) delete env[key];
17791
17791
  }
17792
17792
  function saveConfig(patch, options = {}) {
17793
- const p = join7(DATA_DIR, "config.json");
17793
+ const p = join6(DATA_DIR, "config.json");
17794
17794
  let disk = {};
17795
17795
  try {
17796
- const parsed = jsonObjectSchema.safeParse(parseJson(readFileSync4(p, "utf8")));
17796
+ const parsed = jsonObjectSchema.safeParse(parseJson(readFileSync3(p, "utf8")));
17797
17797
  if (parsed.success) disk = parsed.data;
17798
17798
  } catch {
17799
17799
  }
@@ -17995,7 +17995,11 @@ var init_config = __esm({
17995
17995
  showToolCalls: external_exports.boolean().optional(),
17996
17996
  /** Experimental built-in browser. Off until explicitly enabled; each bot
17997
17997
  * also has its own switch. */
17998
- browser: external_exports.boolean().optional()
17998
+ browser: external_exports.boolean().optional(),
17999
+ /** Opt-in computer sharing (a desktop lending folders, a terminal or
18000
+ * computer control to a workspace). Off until explicitly enabled; there is
18001
+ * no Settings toggle — see sharedComputersEnabled. */
18002
+ sharedComputers: external_exports.boolean().optional()
17999
18003
  });
18000
18004
  onboardingConfigSchema = external_exports.object({
18001
18005
  /** ISO timestamp of finishing (or skipping to the end of) the welcome flow. */
@@ -18112,10 +18116,10 @@ var init_config = __esm({
18112
18116
  });
18113
18117
  appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true, customDomain: true });
18114
18118
  jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.json());
18115
- DATA_DIR = process.env.OMB_DATA_DIR ?? join7(homedir4(), ".openmausbot");
18116
- LEGACY_DATA_DIR = join7(homedir4(), ".opengrokbot");
18117
- EVENTS_DIR = join7(DATA_DIR, "events");
18118
- NATIVE_DIR = join7(DATA_DIR, "native");
18119
+ DATA_DIR = process.env.OMB_DATA_DIR ?? join6(homedir4(), ".openmausbot");
18120
+ LEGACY_DATA_DIR = join6(homedir4(), ".opengrokbot");
18121
+ EVENTS_DIR = join6(DATA_DIR, "events");
18122
+ NATIVE_DIR = join6(DATA_DIR, "native");
18119
18123
  WORKSPACE_CREDENTIAL_ENV = [
18120
18124
  "XAI_API_KEY",
18121
18125
  "OMB_ANTHROPIC_API_KEY",
@@ -18153,11 +18157,11 @@ var init_config = __esm({
18153
18157
  });
18154
18158
 
18155
18159
  // electron/data-dir-lease.mjs
18156
- import { execFileSync } from "node:child_process";
18160
+ import { execFileSync as execFileSync2 } from "node:child_process";
18157
18161
  import { createHash as createHash2, randomUUID as randomUUID5 } from "node:crypto";
18158
- import { existsSync as existsSync6, linkSync, mkdirSync as mkdirSync7, readFileSync as readFileSync5, renameSync as renameSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
18162
+ import { existsSync as existsSync6, linkSync, mkdirSync as mkdirSync7, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
18159
18163
  import { hostname as hostname4, uptime } from "node:os";
18160
- import { join as join8 } from "node:path";
18164
+ import { join as join7 } from "node:path";
18161
18165
  function leaseError(message2, cause) {
18162
18166
  return Object.assign(new DataDirLeaseError(message2), { cause });
18163
18167
  }
@@ -18167,10 +18171,10 @@ function isPid(value) {
18167
18171
  function readBootSession() {
18168
18172
  try {
18169
18173
  if (process.platform === "linux") {
18170
- return normalizeBootId(readFileSync5("/proc/sys/kernel/random/boot_id", "utf8"));
18174
+ return normalizeBootId(readFileSync4("/proc/sys/kernel/random/boot_id", "utf8"));
18171
18175
  }
18172
18176
  if (process.platform === "darwin") {
18173
- return normalizeBootId(execFileSync("/usr/sbin/sysctl", ["-n", "kern.bootsessionuuid"], {
18177
+ return normalizeBootId(execFileSync2("/usr/sbin/sysctl", ["-n", "kern.bootsessionuuid"], {
18174
18178
  encoding: "utf8",
18175
18179
  timeout: 5e3,
18176
18180
  stdio: ["ignore", "pipe", "ignore"]
@@ -18214,7 +18218,7 @@ function isReaperOwner(value, targetToken) {
18214
18218
  function parseRecord(path3, invalidMessage, validate) {
18215
18219
  let raw;
18216
18220
  try {
18217
- raw = readFileSync5(path3, "utf8");
18221
+ raw = readFileSync4(path3, "utf8");
18218
18222
  } catch (error51) {
18219
18223
  if (error51?.code === "ENOENT") return null;
18220
18224
  throw leaseError("OpenMausBot cannot read the data-directory lease; refusing to start to protect its state.", error51);
@@ -18262,7 +18266,7 @@ function unlinkExact(path3, message2) {
18262
18266
  function publishRecord(path3, record3, prepareMessage, acquireMessage) {
18263
18267
  const candidatePath = `${path3}.candidate-${record3.pid}-${record3.token}`;
18264
18268
  try {
18265
- writeFileSync5(candidatePath, `${JSON.stringify(record3)}
18269
+ writeFileSync4(candidatePath, `${JSON.stringify(record3)}
18266
18270
  `, { flag: "wx", mode: 384, flush: true });
18267
18271
  } catch (error51) {
18268
18272
  throw leaseError(prepareMessage, error51);
@@ -18349,10 +18353,10 @@ function prepareDataDir(dataDir, legacyDataDir) {
18349
18353
  } catch (error51) {
18350
18354
  throw leaseError("OpenMausBot cannot create its data directory.", error51);
18351
18355
  }
18352
- return join8(dataDir, LEASE_NAME);
18356
+ return join7(dataDir, LEASE_NAME);
18353
18357
  }
18354
18358
  function assertNoLiveDelegatedChild(dataDir) {
18355
- const childLeasePath = join8(dataDir, DELEGATED_CHILD_DIR, LEASE_NAME);
18359
+ const childLeasePath = join7(dataDir, DELEGATED_CHILD_DIR, LEASE_NAME);
18356
18360
  const child = readOwner(childLeasePath);
18357
18361
  if (!child) return;
18358
18362
  if (child.host !== hostname4()) {
@@ -18383,7 +18387,7 @@ function acquireDataDirLeaseInternal(dataDir, options = {}) {
18383
18387
  };
18384
18388
  const candidatePath = `${leasePath}.candidate-${owner.pid}-${owner.token}`;
18385
18389
  try {
18386
- writeFileSync5(candidatePath, `${JSON.stringify(owner)}
18390
+ writeFileSync4(candidatePath, `${JSON.stringify(owner)}
18387
18391
  `, { flag: "wx", mode: 384, flush: true });
18388
18392
  } catch (error51) {
18389
18393
  throw leaseError("OpenMausBot cannot prepare its data-directory lease.", error51);
@@ -18475,13 +18479,13 @@ var init_data_dir_lease2 = __esm({
18475
18479
 
18476
18480
  // server/env-path.ts
18477
18481
  import { execFile as execFile2 } from "node:child_process";
18478
- import { closeSync as closeSync2, existsSync as existsSync7, openSync as openSync2, readFileSync as readFileSync6, readSync, statSync as statSync2, readdirSync } from "node:fs";
18482
+ import { closeSync as closeSync3, existsSync as existsSync7, openSync as openSync3, readFileSync as readFileSync5, readSync, statSync as statSync2, readdirSync } from "node:fs";
18479
18483
  import { homedir as homedir5 } from "node:os";
18480
- import { basename as basename2, delimiter as delimiter3, dirname as dirname4, extname, join as join9 } from "node:path";
18484
+ import { basename as basename2, delimiter as delimiter3, dirname as dirname4, extname, join as join8 } from "node:path";
18481
18485
  function nvmBinDirs() {
18482
18486
  try {
18483
- const base2 = join9(homedir5(), ".nvm", "versions", "node");
18484
- return readdirSync(base2).filter((v) => v.startsWith("v")).sort((a2, b) => b.localeCompare(a2, void 0, { numeric: true })).map((v) => join9(base2, v, "bin"));
18487
+ const base2 = join8(homedir5(), ".nvm", "versions", "node");
18488
+ return readdirSync(base2).filter((v) => v.startsWith("v")).sort((a2, b) => b.localeCompare(a2, void 0, { numeric: true })).map((v) => join8(base2, v, "bin"));
18485
18489
  } catch {
18486
18490
  return [];
18487
18491
  }
@@ -18489,49 +18493,49 @@ function nvmBinDirs() {
18489
18493
  function knownDirs() {
18490
18494
  const home = homedir5();
18491
18495
  return [
18492
- join9(home, ".local", "bin"),
18496
+ join8(home, ".local", "bin"),
18493
18497
  // claude installer default
18494
- join9(home, ".npm-global", "bin"),
18498
+ join8(home, ".npm-global", "bin"),
18495
18499
  // npm prefix ~/.npm-global (claude, opencode)
18496
- join9(home, ".kimi-code", "bin"),
18500
+ join8(home, ".kimi-code", "bin"),
18497
18501
  // kimi-code installer
18498
- join9(home, ".grok", "bin"),
18502
+ join8(home, ".grok", "bin"),
18499
18503
  // x.ai installer
18500
- join9(home, ".opencode", "bin"),
18504
+ join8(home, ".opencode", "bin"),
18501
18505
  // opencode installer
18502
- join9(home, ".claude", "local"),
18506
+ join8(home, ".claude", "local"),
18503
18507
  // claude "local install"
18504
18508
  "/opt/homebrew/bin",
18505
18509
  // brew, Apple silicon
18506
18510
  "/usr/local/bin",
18507
18511
  // brew Intel / classic installs
18508
- join9(home, ".volta", "bin"),
18509
- join9(home, ".bun", "bin"),
18510
- join9(home, ".asdf", "shims"),
18511
- join9(home, ".deno", "bin"),
18512
- join9(home, "bin"),
18512
+ join8(home, ".volta", "bin"),
18513
+ join8(home, ".bun", "bin"),
18514
+ join8(home, ".asdf", "shims"),
18515
+ join8(home, ".deno", "bin"),
18516
+ join8(home, "bin"),
18513
18517
  ...nvmBinDirs()
18514
18518
  ];
18515
18519
  }
18516
18520
  function windowsKnownDirs() {
18517
18521
  const home = homedir5();
18518
- const appData = process.env.APPDATA ?? join9(home, "AppData", "Roaming");
18519
- const localAppData = process.env.LOCALAPPDATA ?? join9(home, "AppData", "Local");
18522
+ const appData = process.env.APPDATA ?? join8(home, "AppData", "Roaming");
18523
+ const localAppData = process.env.LOCALAPPDATA ?? join8(home, "AppData", "Local");
18520
18524
  return [
18521
- join9(appData, "npm"),
18525
+ join8(appData, "npm"),
18522
18526
  // npm -g shims: claude, codex
18523
- join9(home, ".grok", "bin"),
18527
+ join8(home, ".grok", "bin"),
18524
18528
  // x.ai installer
18525
- join9(localAppData, "agy", "bin"),
18529
+ join8(localAppData, "agy", "bin"),
18526
18530
  // Antigravity installer
18527
- join9(home, ".local", "bin"),
18531
+ join8(home, ".local", "bin"),
18528
18532
  // claude native installer
18529
- join9(home, ".claude", "local"),
18530
- join9(home, "bin"),
18533
+ join8(home, ".claude", "local"),
18534
+ join8(home, "bin"),
18531
18535
  // Factory droid installer (%USERPROFILE%\bin)
18532
- join9(home, ".bun", "bin"),
18533
- join9(home, ".deno", "bin"),
18534
- join9(home, "go", "bin")
18536
+ join8(home, ".bun", "bin"),
18537
+ join8(home, ".deno", "bin"),
18538
+ join8(home, "go", "bin")
18535
18539
  ];
18536
18540
  }
18537
18541
  function registerPathDir(dir) {
@@ -18591,7 +18595,7 @@ function findCliCandidates(name) {
18591
18595
  for (const dir of augmentedPath().split(delimiter3)) {
18592
18596
  if (!dir) continue;
18593
18597
  for (const ext of exts) {
18594
- const p = join9(dir, name + ext);
18598
+ const p = join8(dir, name + ext);
18595
18599
  if (existsSync7(p)) {
18596
18600
  out.push(p);
18597
18601
  break;
@@ -18637,13 +18641,13 @@ function whichWin(cli, env) {
18637
18641
  const path3 = env ? env.PATH ?? env.Path ?? "" : augmentedPath();
18638
18642
  for (const dir of path3.split(delimiter3)) {
18639
18643
  if (!dir) continue;
18640
- const hit = probe(join9(dir, cli));
18644
+ const hit = probe(join8(dir, cli));
18641
18645
  if (hit) return hit;
18642
18646
  }
18643
18647
  return null;
18644
18648
  }
18645
18649
  function nodeExe(near, env) {
18646
- const local = join9(near, "node.exe");
18650
+ const local = join8(near, "node.exe");
18647
18651
  if (isFile(local)) return local;
18648
18652
  const onPath2 = whichWin("node.exe", env);
18649
18653
  if (onPath2 && extname(onPath2).toLowerCase() === ".exe") return onPath2;
@@ -18652,12 +18656,12 @@ function nodeExe(near, env) {
18652
18656
  function parseCmdShim(shim, env) {
18653
18657
  let text4;
18654
18658
  try {
18655
- text4 = readFileSync6(shim, "utf8");
18659
+ text4 = readFileSync5(shim, "utf8");
18656
18660
  } catch {
18657
18661
  return null;
18658
18662
  }
18659
18663
  const dir = dirname4(shim);
18660
- const targets = [...text4.matchAll(/"%~?dp0%?\\?([^"]+)"/g)].map((m) => join9(dir, m[1])).filter((p) => isFile(p) && basename2(p).toLowerCase() !== "node.exe");
18664
+ const targets = [...text4.matchAll(/"%~?dp0%?\\?([^"]+)"/g)].map((m) => join8(dir, m[1])).filter((p) => isFile(p) && basename2(p).toLowerCase() !== "node.exe");
18661
18665
  const script = targets.find((p) => /\.[cm]?js$/i.test(p));
18662
18666
  if (script) {
18663
18667
  const node = nodeExe(dir, env);
@@ -18670,7 +18674,7 @@ function parseNodeShebang(file2, env) {
18670
18674
  let head = "";
18671
18675
  let fd = null;
18672
18676
  try {
18673
- fd = openSync2(file2, "r");
18677
+ fd = openSync3(file2, "r");
18674
18678
  const buf = Buffer.alloc(128);
18675
18679
  const n3 = readSync(fd, buf, 0, buf.length, 0);
18676
18680
  head = buf.subarray(0, n3).toString("utf8").split("\n", 1)[0];
@@ -18679,7 +18683,7 @@ function parseNodeShebang(file2, env) {
18679
18683
  } finally {
18680
18684
  if (fd !== null) {
18681
18685
  try {
18682
- closeSync2(fd);
18686
+ closeSync3(fd);
18683
18687
  } catch {
18684
18688
  }
18685
18689
  }
@@ -18728,7 +18732,7 @@ import {
18728
18732
  spawn as spawn5,
18729
18733
  execFile as execFile3
18730
18734
  } from "node:child_process";
18731
- import { join as join10 } from "node:path";
18735
+ import { join as join9 } from "node:path";
18732
18736
  function resolveCli(cli, args = [], env) {
18733
18737
  return resolveCliSpawn(cli, args, env);
18734
18738
  }
@@ -18872,7 +18876,7 @@ async function stopCliTree(child, pid, timeoutMs) {
18872
18876
  });
18873
18877
  }
18874
18878
  function brokerSocketPath(dataDir, tag) {
18875
- return process.platform === "win32" ? `\\\\.\\pipe\\openmausbot-perm-${process.pid}-${tag}` : join10(dataDir, `perm-${tag}.sock`);
18879
+ return process.platform === "win32" ? `\\\\.\\pipe\\openmausbot-perm-${process.pid}-${tag}` : join9(dataDir, `perm-${tag}.sock`);
18876
18880
  }
18877
18881
  var WINDOWS_SAFE_COMMAND_LINE_CHARS, cliGroups, stopping;
18878
18882
  var init_procs = __esm({
@@ -18887,13 +18891,13 @@ var init_procs = __esm({
18887
18891
 
18888
18892
  // server/engine-install.ts
18889
18893
  import { existsSync as existsSync8, mkdirSync as mkdirSync8 } from "node:fs";
18890
- import { join as join11 } from "node:path";
18894
+ import { join as join10 } from "node:path";
18891
18895
  import { stripVTControlCharacters as stripVTControlCharacters3 } from "node:util";
18892
18896
  function enginesPrefix(baseDir = DATA_DIR) {
18893
- return join11(baseDir, "tools", "npm");
18897
+ return join10(baseDir, "tools", "npm");
18894
18898
  }
18895
18899
  function enginesBinDir(baseDir = DATA_DIR, platform = process.platform) {
18896
- return platform === "win32" ? enginesPrefix(baseDir) : join11(enginesPrefix(baseDir), "bin");
18900
+ return platform === "win32" ? enginesPrefix(baseDir) : join10(enginesPrefix(baseDir), "bin");
18897
18901
  }
18898
18902
  function npmPackageOf(install) {
18899
18903
  const line = install?.command?.linux ?? install?.command?.darwin ?? install?.command?.win32;
@@ -18943,9 +18947,9 @@ async function installOnce(pkg, options) {
18943
18947
  }
18944
18948
  }
18945
18949
  function tail(output) {
18946
- const clean3 = stripVTControlCharacters3(output).trim();
18947
- if (!clean3) return "";
18948
- const lines = clean3.split(/\r?\n/).filter((line) => line.trim()).slice(-6);
18950
+ const clean = stripVTControlCharacters3(output).trim();
18951
+ if (!clean) return "";
18952
+ const lines = clean.split(/\r?\n/).filter((line) => line.trim()).slice(-6);
18949
18953
  return `
18950
18954
  ${lines.join("\n").slice(-600)}`;
18951
18955
  }
@@ -19239,9 +19243,9 @@ var init_registry = __esm({
19239
19243
  });
19240
19244
 
19241
19245
  // server/drivers/local-inject.ts
19242
- import { readFileSync as readFileSync7 } from "node:fs";
19246
+ import { readFileSync as readFileSync6 } from "node:fs";
19243
19247
  import { homedir as homedir6 } from "node:os";
19244
- import { join as join12 } from "node:path";
19248
+ import { join as join11 } from "node:path";
19245
19249
  function contextWindowsFromPs(extra) {
19246
19250
  const out = /* @__PURE__ */ new Map();
19247
19251
  const rec = extra && typeof extra === "object" ? extra : null;
@@ -19326,7 +19330,7 @@ function firstUnslothToken(row) {
19326
19330
  function readUnslothKey(env) {
19327
19331
  const home = env.HOME || env.USERPROFILE || homedir6();
19328
19332
  try {
19329
- const raw = JSON.parse(readFileSync7(join12(home, ".unsloth", "studio", "auth", "agent_api_key.json"), "utf8"));
19333
+ const raw = JSON.parse(readFileSync6(join11(home, ".unsloth", "studio", "auth", "agent_api_key.json"), "utf8"));
19330
19334
  if (raw.servers && typeof raw.servers === "object") {
19331
19335
  const servers = raw.servers;
19332
19336
  for (const url2 of ["http://127.0.0.1:8888", "http://localhost:8888"]) {
@@ -19376,7 +19380,7 @@ async function timedJson(url2, env, host, fetchImpl) {
19376
19380
  function loadedIdsFromPayloads(_host, catalog, extra) {
19377
19381
  const loaded = /* @__PURE__ */ new Set();
19378
19382
  const catalogIds = new Set(idsFromModelsPayload(catalog));
19379
- const add2 = (id) => {
19383
+ const add = (id) => {
19380
19384
  const base2 = id.split(":")[0];
19381
19385
  if (catalogIds.size && !catalogIds.has(id) && !catalogIds.has(base2)) return;
19382
19386
  if (!MODEL_ID.test(id)) return;
@@ -19389,10 +19393,10 @@ function loadedIdsFromPayloads(_host, catalog, extra) {
19389
19393
  const hasLoadedFlags = running2.some(
19390
19394
  (row) => row && typeof row === "object" && ("loaded" in row || "state" in row)
19391
19395
  );
19392
- if (!hasLoadedFlags && typeof rec.default_model === "string") add2(rec.default_model);
19396
+ if (!hasLoadedFlags && typeof rec.default_model === "string") add(rec.default_model);
19393
19397
  for (const row of running2) {
19394
19398
  if (typeof row === "string") {
19395
- if (!hasLoadedFlags) add2(row);
19399
+ if (!hasLoadedFlags) add(row);
19396
19400
  continue;
19397
19401
  }
19398
19402
  if (!row || typeof row !== "object") continue;
@@ -19402,20 +19406,20 @@ function loadedIdsFromPayloads(_host, catalog, extra) {
19402
19406
  const state = typeof item.state === "string" ? item.state.toLowerCase() : "";
19403
19407
  if (item.loaded === false || state === "not-loaded" || state === "unloaded") continue;
19404
19408
  if (item.loaded === true || state === "loaded" || state === "idle" || !hasLoadedFlags) {
19405
- add2(id);
19409
+ add(id);
19406
19410
  }
19407
19411
  }
19408
19412
  }
19409
19413
  if (!loaded.size && catalog && typeof catalog === "object") {
19410
19414
  const rec = catalog;
19411
- if (typeof rec.default_model === "string") add2(rec.default_model);
19415
+ if (typeof rec.default_model === "string") add(rec.default_model);
19412
19416
  const records = Array.isArray(rec.data) ? rec.data : [];
19413
19417
  for (const row of records) {
19414
19418
  if (!row || typeof row !== "object") continue;
19415
19419
  const item = row;
19416
19420
  if (typeof item.id !== "string") continue;
19417
19421
  const state = typeof item.state === "string" ? item.state.toLowerCase() : "";
19418
- if (item.loaded === true || state === "loaded") add2(item.id);
19422
+ if (item.loaded === true || state === "loaded") add(item.id);
19419
19423
  }
19420
19424
  }
19421
19425
  return loaded;
@@ -19528,11 +19532,11 @@ var init_local_inject = __esm({
19528
19532
 
19529
19533
  // server/proxy-paths.ts
19530
19534
  import { existsSync as existsSync9 } from "node:fs";
19531
- import { dirname as dirname5, join as join13 } from "node:path";
19535
+ import { dirname as dirname5, join as join12 } from "node:path";
19532
19536
  import { fileURLToPath as fileURLToPath2 } from "node:url";
19533
19537
  function resolveProxy(relative2) {
19534
- const source = join13(SERVER_ROOT, `${relative2}.ts`);
19535
- return existsSync9(source) ? source : join13(SERVER_ROOT, `${relative2}.js`);
19538
+ const source = join12(SERVER_ROOT, `${relative2}.ts`);
19539
+ return existsSync9(source) ? source : join12(SERVER_ROOT, `${relative2}.js`);
19536
19540
  }
19537
19541
  var SERVER_ROOT, SPAWNED_PROXIES;
19538
19542
  var init_proxy_paths = __esm({
@@ -19561,7 +19565,7 @@ var init_proxy_paths = __esm({
19561
19565
 
19562
19566
  // server/container-computer.ts
19563
19567
  import { execFile as execFile4 } from "node:child_process";
19564
- import { join as join14, resolve as resolve3 } from "node:path";
19568
+ import { join as join13, resolve as resolve3 } from "node:path";
19565
19569
  import { promisify } from "node:util";
19566
19570
  function computerProxyEnv(computer) {
19567
19571
  return {
@@ -19585,7 +19589,7 @@ var init_container_computer = __esm({
19585
19589
  IMAGE_REPOSITORY = "localhost/openmausbot/cua-local-vm";
19586
19590
  IMAGE_LAYER_VERSION = "5";
19587
19591
  IMAGE = `${IMAGE_REPOSITORY}:driver-${CUA_DRIVER_VERSION}-v${IMAGE_LAYER_VERSION}`;
19588
- VM_WORKSPACE_DIR = join14(DATA_DIR, "vm-home");
19592
+ VM_WORKSPACE_DIR = join13(DATA_DIR, "vm-home");
19589
19593
  MEMORY_BYTES = 4 * 1024 * 1024 * 1024;
19590
19594
  SHM_BYTES = 512 * 1024 * 1024;
19591
19595
  containerMcpPath = SPAWNED_PROXIES.containerMcp;
@@ -19689,11 +19693,11 @@ var init_redact = __esm({
19689
19693
 
19690
19694
  // server/drivers/native.ts
19691
19695
  import { appendFileSync as appendFileSync3 } from "node:fs";
19692
- import { join as join15 } from "node:path";
19696
+ import { join as join14 } from "node:path";
19693
19697
  function appendNative(threadId, entry) {
19694
19698
  try {
19695
19699
  appendFileSync3(
19696
- join15(NATIVE_DIR, `${threadId}.ndjson`),
19700
+ join14(NATIVE_DIR, `${threadId}.ndjson`),
19697
19701
  JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), ...entry, msg: redactSecrets(entry.msg) }) + "\n",
19698
19702
  { mode: 384 }
19699
19703
  );
@@ -19709,6 +19713,41 @@ var init_native = __esm({
19709
19713
  });
19710
19714
 
19711
19715
  // server/tool-summary.ts
19716
+ function toolDetailPreview(value) {
19717
+ if (value === void 0) return void 0;
19718
+ let budget = 200;
19719
+ let textBudget = 256e3;
19720
+ const bounded = (item, depth = 0) => {
19721
+ if (--budget < 0 || depth > 6) return "[additional data omitted]";
19722
+ if (typeof item === "string") {
19723
+ if (item.length > textBudget) return "[large content omitted]";
19724
+ if (/^data:[^,\s]+;base64,/i.test(item)) return "[binary content omitted]";
19725
+ if (/^\s*[[{]/.test(item)) {
19726
+ try {
19727
+ return bounded(JSON.parse(item), depth + 1);
19728
+ } catch {
19729
+ }
19730
+ }
19731
+ textBudget -= item.length;
19732
+ return item;
19733
+ }
19734
+ if (typeof item === "bigint") return String(item);
19735
+ if (item === null || typeof item !== "object") return item;
19736
+ if (Array.isArray(item)) return item.slice(0, 40).map((child) => bounded(child, depth + 1)).concat(item.length > 40 ? ["[additional items omitted]"] : []);
19737
+ const entries = Object.entries(item);
19738
+ const result = /* @__PURE__ */ Object.create(null);
19739
+ for (const [key, child] of entries.slice(0, 40)) {
19740
+ result[key] = /token|secret|password|passwd|cookie|authorization|api.?key|(^|[_.-])keys?$/i.test(key) ? "[redacted]" : /^(data|base64|blob)$/i.test(key) && typeof child === "string" ? "[binary content omitted]" : bounded(child, depth + 1);
19741
+ }
19742
+ if (entries.length > 40) result["\u2026"] = "[additional fields omitted]";
19743
+ return result;
19744
+ };
19745
+ const safe = redactSecrets(bounded(value));
19746
+ const text4 = typeof safe === "string" ? safe : JSON.stringify(safe, null, 2);
19747
+ if (!text4?.trim() || text4 === "{}" || text4 === "[]") return void 0;
19748
+ return text4.length > 6e3 ? `${text4.slice(0, 6e3)}
19749
+ [\u2026 preview shortened]` : text4;
19750
+ }
19712
19751
  function fieldsOf(input) {
19713
19752
  if (input === null || typeof input !== "object" || Array.isArray(input)) return void 0;
19714
19753
  return input;
@@ -19838,12 +19877,12 @@ function createAcpDriver(support11) {
19838
19877
  support11.transformEnv?.(env, activeConfig, instanceId);
19839
19878
  return env;
19840
19879
  };
19841
- let models = support11.models;
19880
+ let models2 = support11.models;
19842
19881
  const refreshModels = async () => {
19843
19882
  if (!support11.resolveModels) return;
19844
19883
  try {
19845
19884
  const resolved = await support11.resolveModels(childEnv(), config2, instanceId);
19846
- if (resolved.options.length) models = resolved;
19885
+ if (resolved.options.length) models2 = resolved;
19847
19886
  } catch {
19848
19887
  }
19849
19888
  };
@@ -20214,7 +20253,8 @@ function createAcpDriver(support11) {
20214
20253
  itemType: "tool",
20215
20254
  itemId: u4.toolCallId,
20216
20255
  title: String(u4.rawInput?.command ?? u4.title ?? "tool").slice(0, 80),
20217
- summary: commandSummary(u4.rawInput)
20256
+ summary: commandSummary(u4.rawInput),
20257
+ input: toolDetailPreview(u4.rawInput)
20218
20258
  });
20219
20259
  break;
20220
20260
  }
@@ -20225,7 +20265,8 @@ function createAcpDriver(support11) {
20225
20265
  type: "item.completed",
20226
20266
  itemType: "tool",
20227
20267
  itemId: u4.toolCallId,
20228
- ok: u4.status !== "failed"
20268
+ ok: u4.status !== "failed",
20269
+ output: toolDetailPreview(u4.rawOutput ?? u4.content)
20229
20270
  });
20230
20271
  }
20231
20272
  break;
@@ -20466,7 +20507,7 @@ ${turn.text}` : turn.text;
20466
20507
  displayName: input.displayName,
20467
20508
  enabled: input.enabled,
20468
20509
  get models() {
20469
- return models;
20510
+ return models2;
20470
20511
  },
20471
20512
  refreshModels: support11.resolveModels ? refreshModels : void 0,
20472
20513
  snapshot,
@@ -20543,7 +20584,7 @@ import { createHash as createHash3, randomUUID as randomUUID6 } from "node:crypt
20543
20584
  import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm, stat as stat2 } from "node:fs/promises";
20544
20585
  import { request as httpRequest } from "node:http";
20545
20586
  import { tmpdir as tmpdir2 } from "node:os";
20546
- import { delimiter as delimiter4, join as join16, resolve as resolve5 } from "node:path";
20587
+ import { delimiter as delimiter4, join as join15, resolve as resolve5 } from "node:path";
20547
20588
  function quoteBrowserArgument(value) {
20548
20589
  return `'${value.replaceAll("'", `'"'"'`)}'`;
20549
20590
  }
@@ -20578,11 +20619,11 @@ function authorizationUrlFromLine(line) {
20578
20619
  }
20579
20620
  }
20580
20621
  function antigravityProfileDirectory(instanceId, baseDir = DATA_DIR) {
20581
- return join16(baseDir, "providers", "antigravity", createHash3("sha256").update(instanceId).digest("hex"));
20622
+ return join15(baseDir, "providers", "antigravity", createHash3("sha256").update(instanceId).digest("hex"));
20582
20623
  }
20583
20624
  async function prepareAntigravityProfile(input) {
20584
20625
  const directory = resolve5(input.profileDirectory ?? antigravityProfileDirectory(input.instanceId, input.baseDir));
20585
- const acpDirectory = join16(directory, "antigravity-acp");
20626
+ const acpDirectory = join15(directory, "antigravity-acp");
20586
20627
  await mkdir2(acpDirectory, { recursive: true, mode: 448 });
20587
20628
  if (process.platform !== "win32") {
20588
20629
  const { chmod: chmod2 } = await import("node:fs/promises");
@@ -20590,7 +20631,7 @@ async function prepareAntigravityProfile(input) {
20590
20631
  await chmod2(acpDirectory, 448);
20591
20632
  }
20592
20633
  writeFileAtomic(
20593
- join16(acpDirectory, "settings.json"),
20634
+ join15(acpDirectory, "settings.json"),
20594
20635
  `${JSON.stringify({ auth: { type: "oauth-personal" } })}
20595
20636
  `,
20596
20637
  { mode: 384 }
@@ -20613,7 +20654,7 @@ async function prepareAntigravityProfile(input) {
20613
20654
  PYTHONUNBUFFERED: "1",
20614
20655
  ELECTRON_RUN_AS_NODE: "1"
20615
20656
  });
20616
- return { directory, tokenPath: join16(acpDirectory, "acp_token.json"), environment };
20657
+ return { directory, tokenPath: join15(acpDirectory, "acp_token.json"), environment };
20617
20658
  }
20618
20659
  async function antigravityProfileAuthenticated(profile) {
20619
20660
  try {
@@ -20742,7 +20783,7 @@ function isValidAntigravityInitializeResult(initialized, expectedVersion) {
20742
20783
  return true;
20743
20784
  }
20744
20785
  async function validateAntigravityRuntime(runtime, expectedVersion) {
20745
- const profileDirectory = await mkdtemp(join16(tmpdir2(), "openmaus-antigravity-verify-"));
20786
+ const profileDirectory = await mkdtemp(join15(tmpdir2(), "openmaus-antigravity-verify-"));
20746
20787
  let client;
20747
20788
  let failed2 = false;
20748
20789
  let failure;
@@ -21132,7 +21173,7 @@ import {
21132
21173
  writeFile as writeFile2
21133
21174
  } from "node:fs/promises";
21134
21175
  import { constants } from "node:fs";
21135
- import { delimiter as delimiter5, dirname as dirname7, isAbsolute as isAbsolute2, join as join17, resolve as resolve6 } from "node:path";
21176
+ import { delimiter as delimiter5, dirname as dirname7, isAbsolute as isAbsolute2, join as join16, resolve as resolve6 } from "node:path";
21136
21177
  import { Transform } from "node:stream";
21137
21178
  import { pipeline } from "node:stream/promises";
21138
21179
  import { createInflateRaw } from "node:zlib";
@@ -21153,7 +21194,7 @@ async function retryOnWindowsFileLock(step, options = {}) {
21153
21194
  }
21154
21195
  }
21155
21196
  function runtimeRoot(baseDir = DATA_DIR) {
21156
- return join17(baseDir, "tools", "antigravity-acp", `${process.platform}-${process.arch}`);
21197
+ return join16(baseDir, "tools", "antigravity-acp", `${process.platform}-${process.arch}`);
21157
21198
  }
21158
21199
  function executableNames(platform = process.platform) {
21159
21200
  return platform === "win32" ? { executable: "agy_acp_server.exe", harness: "localharness_external.exe" } : { executable: "agy_acp_server.par", harness: "localharness_external" };
@@ -21170,10 +21211,10 @@ async function executableFile(path3, bytes) {
21170
21211
  }
21171
21212
  async function completedRelease(directory, asset) {
21172
21213
  try {
21173
- const record3 = JSON.parse(await readFile3(join17(directory, INSTALL_RECORD), "utf8"));
21214
+ const record3 = JSON.parse(await readFile3(join16(directory, INSTALL_RECORD), "utf8"));
21174
21215
  if (record3.sha256 !== asset.sha256 || record3.version !== asset.version || record3.executable.name !== asset.executable.name || record3.executable.bytes !== asset.executable.bytes || record3.harness.name !== asset.harness.name || record3.harness.bytes !== asset.harness.bytes) return null;
21175
- const executablePath = join17(directory, asset.executable.name);
21176
- const harnessPath = join17(directory, asset.harness.name);
21216
+ const executablePath = join16(directory, asset.executable.name);
21217
+ const harnessPath = join16(directory, asset.harness.name);
21177
21218
  if (!await executableFile(executablePath, asset.executable.bytes)) return null;
21178
21219
  if (!await executableFile(harnessPath, asset.harness.bytes)) return null;
21179
21220
  return { executablePath, harnessPath, source: "managed", version: asset.version };
@@ -21189,7 +21230,7 @@ function pathCandidates(binary2, env) {
21189
21230
  async function externalRuntime(candidate, source) {
21190
21231
  const names = executableNames();
21191
21232
  const executablePath = resolve6(candidate);
21192
- const harnessPath = join17(dirname7(executablePath), names.harness);
21233
+ const harnessPath = join16(dirname7(executablePath), names.harness);
21193
21234
  if (!await executableFile(executablePath) || !await executableFile(harnessPath)) return null;
21194
21235
  return { executablePath, harnessPath, source, version: null };
21195
21236
  }
@@ -21209,7 +21250,7 @@ async function resolveAntigravityRuntime(binaryPath, env = process.env, baseDir
21209
21250
  }
21210
21251
  const asset = resolveAntigravityReleaseAsset();
21211
21252
  if (asset) {
21212
- const managed = await completedRelease(join17(runtimeRoot(baseDir), "versions", asset.sha256), asset);
21253
+ const managed = await completedRelease(join16(runtimeRoot(baseDir), "versions", asset.sha256), asset);
21213
21254
  if (managed) return managed;
21214
21255
  }
21215
21256
  const names = executableNames();
@@ -21331,7 +21372,7 @@ async function extractPinnedArchive(archivePath, outputDirectory, asset) {
21331
21372
  start: dataOffset,
21332
21373
  end: dataOffset + entry.compressedBytes - 1
21333
21374
  });
21334
- const destination = createWriteStream(join17(outputDirectory, entry.name), { flags: "wx", mode: 448 });
21375
+ const destination = createWriteStream(join16(outputDirectory, entry.name), { flags: "wx", mode: 448 });
21335
21376
  if (entry.compression === 8) await pipeline(source, createInflateRaw(), counter, destination);
21336
21377
  else await pipeline(source, counter, destination);
21337
21378
  if (bytes !== expectedBytes) throw new Error("An extracted file was truncated.");
@@ -21342,8 +21383,8 @@ async function installOnce2(options) {
21342
21383
  const asset = options.asset === void 0 ? resolveAntigravityReleaseAsset() : options.asset;
21343
21384
  if (!asset) throw new Error(`Google does not publish an Antigravity runtime for ${process.platform}-${process.arch}.`);
21344
21385
  const root = runtimeRoot(options.baseDir);
21345
- const versions = join17(root, "versions");
21346
- const destination = join17(versions, asset.sha256);
21386
+ const versions = join16(root, "versions");
21387
+ const destination = join16(versions, asset.sha256);
21347
21388
  await mkdir3(versions, { recursive: true, mode: 448 });
21348
21389
  const existing = await completedRelease(destination, asset);
21349
21390
  if (existing) {
@@ -21359,11 +21400,11 @@ async function installOnce2(options) {
21359
21400
  } catch (error51) {
21360
21401
  if (error51 instanceof Error && error51.message.includes("Antigravity needs")) throw error51;
21361
21402
  }
21362
- const staging = await mkdtemp2(join17(versions, `${STAGING_PREFIX}${randomUUID7()}-`));
21403
+ const staging = await mkdtemp2(join16(versions, `${STAGING_PREFIX}${randomUUID7()}-`));
21363
21404
  let failed2 = false;
21364
21405
  try {
21365
- const archivePath = join17(staging, "download.zip");
21366
- const runtimeDirectory = join17(staging, "runtime");
21406
+ const archivePath = join16(staging, "download.zip");
21407
+ const runtimeDirectory = join16(staging, "runtime");
21367
21408
  await mkdir3(runtimeDirectory, { mode: 448 });
21368
21409
  const controller = new AbortController();
21369
21410
  const timeout = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS2);
@@ -21409,12 +21450,12 @@ async function installOnce2(options) {
21409
21450
  }
21410
21451
  await extractPinnedArchive(archivePath, runtimeDirectory, asset);
21411
21452
  if (process.platform !== "win32") {
21412
- await chmod(join17(runtimeDirectory, asset.executable.name), 493);
21413
- await chmod(join17(runtimeDirectory, asset.harness.name), 493);
21453
+ await chmod(join16(runtimeDirectory, asset.executable.name), 493);
21454
+ await chmod(join16(runtimeDirectory, asset.harness.name), 493);
21414
21455
  }
21415
21456
  const candidate = {
21416
- executablePath: join17(runtimeDirectory, asset.executable.name),
21417
- harnessPath: join17(runtimeDirectory, asset.harness.name),
21457
+ executablePath: join16(runtimeDirectory, asset.executable.name),
21458
+ harnessPath: join16(runtimeDirectory, asset.harness.name),
21418
21459
  source: "managed",
21419
21460
  version: asset.version
21420
21461
  };
@@ -21425,7 +21466,7 @@ async function installOnce2(options) {
21425
21466
  executable: asset.executable,
21426
21467
  harness: asset.harness
21427
21468
  };
21428
- await writeFile2(join17(runtimeDirectory, INSTALL_RECORD), `${JSON.stringify(record3)}
21469
+ await writeFile2(join16(runtimeDirectory, INSTALL_RECORD), `${JSON.stringify(record3)}
21429
21470
  `, { flag: "wx", mode: 384 });
21430
21471
  try {
21431
21472
  await retryOnWindowsFileLock(() => rename(runtimeDirectory, destination));
@@ -21706,7 +21747,7 @@ var init_boxagent = __esm({
21706
21747
  const model = turn.model || MODELS.default;
21707
21748
  const prompt = [
21708
21749
  turn.system,
21709
- "You are working on your own cloud computer \u2014 its desktop, Chrome, and shell are yours.",
21750
+ "You are working on the assigned cloud computer \u2014 use its desktop, Chrome, and shell within the access described above.",
21710
21751
  "",
21711
21752
  turn.text
21712
21753
  ].filter((s) => s !== void 0).join("\n");
@@ -21887,14 +21928,14 @@ var init_resume_recovery = __esm({
21887
21928
  import { randomUUID as randomUUID8 } from "node:crypto";
21888
21929
  import { realpathSync } from "node:fs";
21889
21930
  import { homedir as homedir8 } from "node:os";
21890
- import { basename as basename4, dirname as dirname8, isAbsolute as isAbsolute3, join as join18, resolve as resolve7 } from "node:path";
21931
+ import { basename as basename4, dirname as dirname8, isAbsolute as isAbsolute3, join as join17, resolve as resolve7 } from "node:path";
21891
21932
  import { stripVTControlCharacters as stripVTControlCharacters4 } from "node:util";
21892
21933
  function canonicalPath(path3) {
21893
21934
  try {
21894
21935
  return realpathSync(path3);
21895
21936
  } catch {
21896
21937
  const parent = dirname8(path3);
21897
- return parent === path3 ? path3 : join18(canonicalPath(parent), basename4(path3));
21938
+ return parent === path3 ? path3 : join17(canonicalPath(parent), basename4(path3));
21898
21939
  }
21899
21940
  }
21900
21941
  function claudeSignInLink(value) {
@@ -21951,7 +21992,7 @@ var init_claude_login_auth = __esm({
21951
21992
  if (!isAbsolute3(home) || env.CLAUDE_CONFIG_DIR && !isAbsolute3(env.CLAUDE_CONFIG_DIR)) {
21952
21993
  throw new Error("Use an absolute HOME and CLAUDE_CONFIG_DIR path for this server's Claude provider before signing in.");
21953
21994
  }
21954
- const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR || join18(home, ".claude")));
21995
+ const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR || join17(home, ".claude")));
21955
21996
  if (authenticatingHomes.has(homeKey)) {
21956
21997
  throw new Error("A Claude sign-in is already running for this server account. Finish or cancel it before starting another.");
21957
21998
  }
@@ -22047,7 +22088,7 @@ var init_claude_login_auth = __esm({
22047
22088
  if (!isAbsolute3(home) || env.CLAUDE_CONFIG_DIR && !isAbsolute3(env.CLAUDE_CONFIG_DIR)) {
22048
22089
  throw new Error("Use an absolute HOME and CLAUDE_CONFIG_DIR path for this server's Claude provider before signing out.");
22049
22090
  }
22050
- const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR || join18(home, ".claude")));
22091
+ const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR || join17(home, ".claude")));
22051
22092
  if (authenticatingHomes.has(homeKey)) {
22052
22093
  throw new Error("A Claude sign-in is running for this server account. Finish or cancel it before signing out.");
22053
22094
  }
@@ -22263,7 +22304,7 @@ var init_mcp_trim = __esm({
22263
22304
  });
22264
22305
 
22265
22306
  // server/mcp-gate-config.ts
22266
- import { join as join19 } from "node:path";
22307
+ import { join as join18 } from "node:path";
22267
22308
  function resultBudget(env = process.env) {
22268
22309
  const raw = env.OMB_MCP_RESULT_BUDGET;
22269
22310
  if (raw === void 0 || raw === "") return DEFAULT_RESULT_BUDGET;
@@ -22271,7 +22312,7 @@ function resultBudget(env = process.env) {
22271
22312
  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : DEFAULT_RESULT_BUDGET;
22272
22313
  }
22273
22314
  function spillDir(threadId) {
22274
- return join19(DATA_DIR, "tool-results", threadId.replace(/[^A-Za-z0-9_-]/g, "-").slice(0, 80) || "thread");
22315
+ return join18(DATA_DIR, "tool-results", threadId.replace(/[^A-Za-z0-9_-]/g, "-").slice(0, 80) || "thread");
22275
22316
  }
22276
22317
  function gateServer(input) {
22277
22318
  const { name, server, budget } = input;
@@ -22473,10 +22514,10 @@ var init_ask_question = __esm({
22473
22514
 
22474
22515
  // server/drivers/claude.ts
22475
22516
  import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
22476
- import { chmodSync as chmodSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, rmSync as rmSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
22517
+ import { chmodSync as chmodSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync7, rmSync as rmSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
22477
22518
  import { createServer as createNetServer } from "node:net";
22478
22519
  import { homedir as homedir9, tmpdir as tmpdir3 } from "node:os";
22479
- import { join as join20, dirname as dirname9, isAbsolute as isAbsolute4, normalize } from "node:path";
22520
+ import { join as join19, dirname as dirname9, isAbsolute as isAbsolute4, normalize } from "node:path";
22480
22521
  function claudeAuthStatus(cli, env, run3 = execCli) {
22481
22522
  return new Promise((resolve13) => {
22482
22523
  run3(cli, ["auth", "status", "--json"], { timeout: 8e3, maxBuffer: 65536, env }, (_error, stdout2) => {
@@ -22504,8 +22545,8 @@ function claudeAuthStatus(cli, env, run3 = execCli) {
22504
22545
  }
22505
22546
  function resolveClaudeConfigDir(configDir, env = process.env) {
22506
22547
  const home = env.HOME || env.USERPROFILE || homedir9();
22507
- const configured = configDir?.trim() || env.CLAUDE_CONFIG_DIR?.trim() || join20(home, ".claude");
22508
- const expanded = configured === "~" ? home : configured.startsWith("~/") ? join20(home, configured.slice(2)) : configured;
22548
+ const configured = configDir?.trim() || env.CLAUDE_CONFIG_DIR?.trim() || join19(home, ".claude");
22549
+ const expanded = configured === "~" ? home : configured.startsWith("~/") ? join19(home, configured.slice(2)) : configured;
22509
22550
  if (!isAbsolute4(expanded) || /[\p{Cc}\p{Cf}]/u.test(expanded)) {
22510
22551
  throw new Error("claude: configDir must be an absolute path or start with ~/");
22511
22552
  }
@@ -22540,7 +22581,7 @@ function inheritsUserConfig(env) {
22540
22581
  function readClaudeAuthSettings(env, instanceEnvironment = {}) {
22541
22582
  if (CLAUDE_ACCOUNT_ENV_KEYS.some((key) => instanceEnvironment[key])) return {};
22542
22583
  try {
22543
- const settings2 = JSON.parse(readFileSync8(join20(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
22584
+ const settings2 = JSON.parse(readFileSync7(join19(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
22544
22585
  const authEnv = {};
22545
22586
  for (const key of CLAUDE_ACCOUNT_ENV_KEYS) {
22546
22587
  if (!key.endsWith("_FILE_DESCRIPTOR") && typeof settings2?.env?.[key] === "string") {
@@ -22558,7 +22599,7 @@ function readClaudeAuthSettings(env, instanceEnvironment = {}) {
22558
22599
  function projectMcpServers(cwd) {
22559
22600
  let parsed;
22560
22601
  try {
22561
- parsed = JSON.parse(readFileSync8(join20(cwd, ".mcp.json"), "utf8"));
22602
+ parsed = JSON.parse(readFileSync7(join19(cwd, ".mcp.json"), "utf8"));
22562
22603
  } catch {
22563
22604
  return {};
22564
22605
  }
@@ -22626,7 +22667,7 @@ function extrasFromUnknown(value) {
22626
22667
  function readClaudeModelCatalog(env = process.env) {
22627
22668
  let settings2 = {};
22628
22669
  try {
22629
- settings2 = JSON.parse(readFileSync8(join20(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
22670
+ settings2 = JSON.parse(readFileSync7(join19(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
22630
22671
  } catch {
22631
22672
  return STATIC_CLAUDE_MODELS;
22632
22673
  }
@@ -22677,7 +22718,7 @@ function brokerSocketCandidates(threadId, botId) {
22677
22718
  const base2 = permissionSocketPath(threadId, botId);
22678
22719
  if (process.platform !== "win32") {
22679
22720
  const scope = createHash5("sha256").update(`${DATA_DIR}\0${process.pid}\0${botId ?? ""}\0${threadId}`).digest("hex").slice(0, 16);
22680
- return [base2, join20(tmpdir3(), `omb-perm-${scope}.sock`)];
22721
+ return [base2, join19(tmpdir3(), `omb-perm-${scope}.sock`)];
22681
22722
  }
22682
22723
  return [
22683
22724
  base2,
@@ -22895,7 +22936,7 @@ function claudeUserMessage(text4, images) {
22895
22936
  source: {
22896
22937
  type: "base64",
22897
22938
  media_type: image.mime,
22898
- data: readFileSync8(image.path).toString("base64")
22939
+ data: readFileSync7(image.path).toString("base64")
22899
22940
  }
22900
22941
  }));
22901
22942
  if (text4) content.push({ type: "text", text: text4 });
@@ -22999,11 +23040,11 @@ var init_claude = __esm({
22999
23040
  const { instanceId, config: config2 } = input;
23000
23041
  const environment = (model) => claudeEnvironment(model, { ...process.env, ...input.environment }, config2.configDir, input.environment);
23001
23042
  const catalogEnv = environment();
23002
- let models = STATIC_CLAUDE_MODELS;
23043
+ let models2 = STATIC_CLAUDE_MODELS;
23003
23044
  const refreshModels = async () => {
23004
23045
  try {
23005
23046
  const resolved = await mergeLocalInject(readClaudeModelCatalog(catalogEnv), catalogEnv);
23006
- if (resolved.options.length) models = resolved;
23047
+ if (resolved.options.length) models2 = resolved;
23007
23048
  } catch {
23008
23049
  }
23009
23050
  };
@@ -23196,13 +23237,13 @@ var init_claude = __esm({
23196
23237
  allowed.push("mcp__ogb");
23197
23238
  let mcpConfigPath = null;
23198
23239
  if (Object.keys(mcpServers).length) {
23199
- mcpConfigPath = join20(mkdtempSync2(join20(tmpdir3(), "omb-mcp-")), "mcp.json");
23240
+ mcpConfigPath = join19(mkdtempSync2(join19(tmpdir3(), "omb-mcp-")), "mcp.json");
23200
23241
  args.push("--mcp-config", mcpConfigPath);
23201
23242
  args.push("--allowedTools", allowed.join(","));
23202
23243
  }
23203
23244
  const env = environment(turnModel);
23204
23245
  const authSettings = isolated && !injected.injected ? readClaudeAuthSettings(env, input.environment) : {};
23205
- const authSettingsPath = mcpConfigPath && Object.keys(authSettings).length ? join20(dirname9(mcpConfigPath), "auth-settings.json") : null;
23246
+ const authSettingsPath = mcpConfigPath && Object.keys(authSettings).length ? join19(dirname9(mcpConfigPath), "auth-settings.json") : null;
23206
23247
  if (authSettingsPath) args.push("--settings", authSettingsPath);
23207
23248
  env.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS = "1";
23208
23249
  const cwd = turn.cwd ?? homedir9();
@@ -23279,8 +23320,8 @@ var init_claude = __esm({
23279
23320
  };
23280
23321
  try {
23281
23322
  if (turn.system) {
23282
- systemPromptPath = join20(mkdtempSync2(join20(tmpdir3(), "omb-system-")), "prompt.txt");
23283
- writeFileSync6(systemPromptPath, turn.system, { mode: 384 });
23323
+ systemPromptPath = join19(mkdtempSync2(join19(tmpdir3(), "omb-system-")), "prompt.txt");
23324
+ writeFileSync5(systemPromptPath, turn.system, { mode: 384 });
23284
23325
  args.push("--append-system-prompt-file", systemPromptPath);
23285
23326
  }
23286
23327
  if (socketPath) {
@@ -23330,10 +23371,10 @@ var init_claude = __esm({
23330
23371
  }
23331
23372
  }
23332
23373
  if (mcpConfigPath) {
23333
- writeFileSync6(mcpConfigPath, JSON.stringify({ mcpServers }), { mode: 384 });
23374
+ writeFileSync5(mcpConfigPath, JSON.stringify({ mcpServers }), { mode: 384 });
23334
23375
  }
23335
23376
  if (authSettingsPath) {
23336
- writeFileSync6(authSettingsPath, JSON.stringify(authSettings), { mode: 384 });
23377
+ writeFileSync5(authSettingsPath, JSON.stringify(authSettings), { mode: 384 });
23337
23378
  }
23338
23379
  if (sessionId) args.push("--resume", sessionId);
23339
23380
  else args.push("--session-id", newSessionId);
@@ -23446,7 +23487,8 @@ var init_claude = __esm({
23446
23487
  itemType: "tool",
23447
23488
  itemId: b.id,
23448
23489
  title: b.name,
23449
- summary: commandSummary(b.input)
23490
+ summary: commandSummary(b.input),
23491
+ input: toolDetailPreview(b.input)
23450
23492
  });
23451
23493
  }
23452
23494
  }
@@ -23464,7 +23506,7 @@ var init_claude = __esm({
23464
23506
  case "user":
23465
23507
  for (const b of Array.isArray(o.message?.content) ? o.message.content : []) {
23466
23508
  if (b.type === "tool_result") {
23467
- emit({ ...base2(threadId, currentTurnId()), type: "item.completed", itemType: "tool", itemId: b.tool_use_id, ok: !b.is_error });
23509
+ emit({ ...base2(threadId, currentTurnId()), type: "item.completed", itemType: "tool", itemId: b.tool_use_id, ok: !b.is_error, output: toolDetailPreview(b.content) });
23468
23510
  }
23469
23511
  }
23470
23512
  break;
@@ -23751,7 +23793,7 @@ var init_claude = __esm({
23751
23793
  displayName: input.displayName,
23752
23794
  enabled: input.enabled,
23753
23795
  get models() {
23754
- return models;
23796
+ return models2;
23755
23797
  },
23756
23798
  refreshModels,
23757
23799
  snapshot,
@@ -23816,9 +23858,9 @@ var init_claude = __esm({
23816
23858
  });
23817
23859
 
23818
23860
  // server/drivers/codex-catalog.ts
23819
- import { readdirSync as readdirSync2, readFileSync as readFileSync9 } from "node:fs";
23861
+ import { readdirSync as readdirSync2, readFileSync as readFileSync8 } from "node:fs";
23820
23862
  import { homedir as homedir10 } from "node:os";
23821
- import { basename as basename5, join as join21 } from "node:path";
23863
+ import { basename as basename5, join as join20 } from "node:path";
23822
23864
  function encodeCodexSelection(provider, model) {
23823
23865
  return `${provider}${SEP}${model}`;
23824
23866
  }
@@ -23840,7 +23882,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
23840
23882
  let settled = false;
23841
23883
  let buffer = "";
23842
23884
  let nextId = 1;
23843
- const models = [];
23885
+ const models2 = [];
23844
23886
  const cursors = /* @__PURE__ */ new Set();
23845
23887
  const pending = /* @__PURE__ */ new Map();
23846
23888
  const finish = (catalog) => {
@@ -23898,7 +23940,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
23898
23940
  continue;
23899
23941
  }
23900
23942
  const page = message2.result;
23901
- if (Array.isArray(page?.data)) models.push(...page.data);
23943
+ if (Array.isArray(page?.data)) models2.push(...page.data);
23902
23944
  const cursor3 = typeof page?.nextCursor === "string" && page.nextCursor ? page.nextCursor : null;
23903
23945
  if (cursor3 && !cursors.has(cursor3)) {
23904
23946
  cursors.add(cursor3);
@@ -23908,7 +23950,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
23908
23950
  const options = [];
23909
23951
  const seen = /* @__PURE__ */ new Set();
23910
23952
  let defaultModel = null;
23911
- for (const row of models) {
23953
+ for (const row of models2) {
23912
23954
  if (row.hidden === true || typeof row.id !== "string" || !MODEL_ID2.test(row.id) || seen.has(row.id)) continue;
23913
23955
  seen.add(row.id);
23914
23956
  options.push({
@@ -23934,7 +23976,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
23934
23976
  }
23935
23977
  function codexHome(env) {
23936
23978
  if (env.CODEX_HOME) return env.CODEX_HOME;
23937
- return join21(env.HOME || env.USERPROFILE || homedir10(), ".codex");
23979
+ return join20(env.HOME || env.USERPROFILE || homedir10(), ".codex");
23938
23980
  }
23939
23981
  function unquote(raw) {
23940
23982
  let value = raw.trim();
@@ -23997,7 +24039,7 @@ function parseCodexToml(text4) {
23997
24039
  }
23998
24040
  function readText(path3) {
23999
24041
  try {
24000
- return readFileSync9(path3, "utf8");
24042
+ return readFileSync8(path3, "utf8");
24001
24043
  } catch {
24002
24044
  return null;
24003
24045
  }
@@ -24020,11 +24062,11 @@ function niceLabel(model, provider, known, named) {
24020
24062
  }
24021
24063
  function collectCatalogNames(home) {
24022
24064
  const named = /* @__PURE__ */ new Map();
24023
- for (const file2 of listDir(join21(home, "model-catalogs"))) {
24065
+ for (const file2 of listDir(join20(home, "model-catalogs"))) {
24024
24066
  if (!file2.endsWith(".json")) continue;
24025
24067
  const provider = basename5(file2, ".json").replace(/-models$/, "");
24026
24068
  if (!PROVIDER_ID.test(provider)) continue;
24027
- const raw = readText(join21(home, "model-catalogs", file2));
24069
+ const raw = readText(join20(home, "model-catalogs", file2));
24028
24070
  if (!raw) continue;
24029
24071
  let parsed;
24030
24072
  try {
@@ -24076,7 +24118,7 @@ async function probeProviderModels(provider, env, fetchImpl) {
24076
24118
  async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli) {
24077
24119
  const official = (cli ? await readCodexAppServerModelCatalog(cli, env) : null) ?? STATIC_CODEX_MODELS;
24078
24120
  const home = codexHome(env);
24079
- const mainText = readText(join21(home, "config.toml"));
24121
+ const mainText = readText(join20(home, "config.toml"));
24080
24122
  if (!mainText) return mergeLocalInject(official, env, fetchImpl);
24081
24123
  const main2 = parseCodexToml(mainText);
24082
24124
  const known = new Map(main2.providers.map((provider) => [provider.id, provider]));
@@ -24091,7 +24133,7 @@ async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli)
24091
24133
  remember(main2.modelProvider, main2.model);
24092
24134
  for (const file2 of listDir(home)) {
24093
24135
  if (!file2.endsWith(".config.toml")) continue;
24094
- const profile = parseCodexToml(readText(join21(home, file2)) ?? "");
24136
+ const profile = parseCodexToml(readText(join20(home, file2)) ?? "");
24095
24137
  for (const provider of profile.providers) {
24096
24138
  if (!known.has(provider.id)) known.set(provider.id, provider);
24097
24139
  }
@@ -24157,8 +24199,8 @@ var init_codex_catalog = __esm({
24157
24199
 
24158
24200
  // server/drivers/codex-instructions.ts
24159
24201
  import { createHash as createHash6 } from "node:crypto";
24160
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync10 } from "node:fs";
24161
- import { join as join22 } from "node:path";
24202
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync9 } from "node:fs";
24203
+ import { join as join21 } from "node:path";
24162
24204
  function codexDeveloperInstructions(config2, botInstructions) {
24163
24205
  if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) {
24164
24206
  throw new Error("Codex returned no effective configuration; cannot safely update bot instructions.");
@@ -24172,12 +24214,12 @@ function codexDeveloperInstructions(config2, botInstructions) {
24172
24214
  ${configured}` : botInstructions;
24173
24215
  }
24174
24216
  async function syncCodexInstructions(key, nativeThreadId, instructions, resumed, request) {
24175
- const directory = join22(DATA_DIR, "codex-instructions");
24176
- const path3 = join22(directory, `${digest(JSON.stringify([key, nativeThreadId]))}.sha256`);
24217
+ const directory = join21(DATA_DIR, "codex-instructions");
24218
+ const path3 = join21(directory, `${digest(JSON.stringify([key, nativeThreadId]))}.sha256`);
24177
24219
  const fingerprint = digest(instructions);
24178
24220
  let previous;
24179
24221
  try {
24180
- previous = readFileSync10(path3, "utf8");
24222
+ previous = readFileSync9(path3, "utf8");
24181
24223
  } catch (error51) {
24182
24224
  if (error51.code !== "ENOENT") throw error51;
24183
24225
  }
@@ -24220,19 +24262,19 @@ var init_codex_instructions = __esm({
24220
24262
  import { randomUUID as randomUUID9 } from "node:crypto";
24221
24263
  import { realpathSync as realpathSync2 } from "node:fs";
24222
24264
  import { homedir as homedir11 } from "node:os";
24223
- import { basename as basename6, dirname as dirname10, isAbsolute as isAbsolute5, join as join23, resolve as resolve8 } from "node:path";
24265
+ import { basename as basename6, dirname as dirname10, isAbsolute as isAbsolute5, join as join22, resolve as resolve8 } from "node:path";
24224
24266
  import { stripVTControlCharacters as stripVTControlCharacters5 } from "node:util";
24225
24267
  function canonicalPath2(path3) {
24226
24268
  try {
24227
24269
  return realpathSync2(path3);
24228
24270
  } catch {
24229
24271
  const parent = dirname10(path3);
24230
- return parent === path3 ? path3 : join23(canonicalPath2(parent), basename6(path3));
24272
+ return parent === path3 ? path3 : join22(canonicalPath2(parent), basename6(path3));
24231
24273
  }
24232
24274
  }
24233
24275
  function codexDevicePrompt(output) {
24234
- const clean3 = stripVTControlCharacters5(output);
24235
- const lines = clean3.split(/\r?\n/).slice(0, -1).map((line) => line.trim());
24276
+ const clean = stripVTControlCharacters5(output);
24277
+ const lines = clean.split(/\r?\n/).slice(0, -1).map((line) => line.trim());
24236
24278
  if (!lines.some((line) => /^https:\/\/auth\.openai\.com\/codex\/device\/?$/.test(line))) return null;
24237
24279
  const userCode = lines.find((line) => /^[A-Z0-9]{4,8}-[A-Z0-9]{4,8}$/.test(line));
24238
24280
  return userCode ? { authorizationUrl: DEVICE_URL, userCode } : null;
@@ -24278,7 +24320,7 @@ var init_codex_device_auth = __esm({
24278
24320
  if (!isAbsolute5(home) || env.CODEX_HOME && !isAbsolute5(env.CODEX_HOME)) {
24279
24321
  throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing in.");
24280
24322
  }
24281
- const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join23(home, ".codex")));
24323
+ const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join22(home, ".codex")));
24282
24324
  if (authenticatingHomes2.has(homeKey)) {
24283
24325
  throw new Error("A ChatGPT sign-in is already running for this server account. Finish or cancel it before starting another.");
24284
24326
  }
@@ -24357,7 +24399,7 @@ var init_codex_device_auth = __esm({
24357
24399
  if (!isAbsolute5(home) || env.CODEX_HOME && !isAbsolute5(env.CODEX_HOME)) {
24358
24400
  throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing out.");
24359
24401
  }
24360
- const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join23(home, ".codex")));
24402
+ const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join22(home, ".codex")));
24361
24403
  if (authenticatingHomes2.has(homeKey)) {
24362
24404
  throw new Error("A ChatGPT sign-in is running for this server account. Finish or cancel it before signing out.");
24363
24405
  }
@@ -24516,11 +24558,11 @@ var init_codex_device_auth = __esm({
24516
24558
 
24517
24559
  // server/drivers/codex-identity.ts
24518
24560
  import { homedir as homedir12 } from "node:os";
24519
- import { isAbsolute as isAbsolute6, join as join24, resolve as resolve9 } from "node:path";
24561
+ import { isAbsolute as isAbsolute6, join as join23, resolve as resolve9 } from "node:path";
24520
24562
  function codexHome2(env) {
24521
24563
  if (env.CODEX_HOME) return isAbsolute6(env.CODEX_HOME) ? resolve9(env.CODEX_HOME) : null;
24522
24564
  const home = env.HOME || env.USERPROFILE || homedir12();
24523
- return isAbsolute6(home) ? join24(home, ".codex") : null;
24565
+ return isAbsolute6(home) ? join23(home, ".codex") : null;
24524
24566
  }
24525
24567
  async function codexAccountEmail(cli, env, timeoutMs = 3e3) {
24526
24568
  const cwd = env.HOME || env.USERPROFILE || homedir12();
@@ -24650,8 +24692,8 @@ function codexUpdateCommand(cli, platform = process.platform) {
24650
24692
  const command = (tokens.length > 0 ? tokens : [trimmed]).map(quote).join(" ");
24651
24693
  return platform === "win32" ? `& ${command} update` : `${command} update`;
24652
24694
  }
24653
- function codexAstraUpdate(version2, models, cli) {
24654
- if (models.options.some((model) => model.id === ASTRA_MODEL_ID) || !codexPredatesAstra(version2)) {
24695
+ function codexAstraUpdate(version2, models2, cli) {
24696
+ if (models2.options.some((model) => model.id === ASTRA_MODEL_ID) || !codexPredatesAstra(version2)) {
24655
24697
  return void 0;
24656
24698
  }
24657
24699
  return {
@@ -24945,11 +24987,11 @@ var init_codex = __esm({
24945
24987
  return env;
24946
24988
  };
24947
24989
  const catalogEnv = childEnv();
24948
- let models = STATIC_CODEX_MODELS;
24990
+ let models2 = STATIC_CODEX_MODELS;
24949
24991
  const refreshModels = async () => {
24950
24992
  try {
24951
24993
  const resolved = await readCodexModelCatalog(catalogEnv, fetch, config2.cli);
24952
- if (resolved.options.length) models = resolved;
24994
+ if (resolved.options.length) models2 = resolved;
24953
24995
  } catch {
24954
24996
  }
24955
24997
  };
@@ -25244,7 +25286,8 @@ var init_codex = __esm({
25244
25286
  itemType: "tool",
25245
25287
  itemId: item.id,
25246
25288
  title,
25247
- summary: item.type === "commandExecution" ? commandSummary({ command: item.command }) : void 0
25289
+ summary: item.type === "commandExecution" ? commandSummary({ command: item.command }) : void 0,
25290
+ input: toolDetailPreview(item.type === "commandExecution" ? { command: item.command, cwd: item.cwd } : item.type === "mcpToolCall" ? item.arguments : item.type === "fileChange" ? item.changes : item.query)
25248
25291
  });
25249
25292
  }
25250
25293
  break;
@@ -25277,7 +25320,8 @@ var init_codex = __esm({
25277
25320
  type: "item.completed",
25278
25321
  itemType: "tool",
25279
25322
  itemId: item.id,
25280
- ok: item.status !== "failed" && item.status !== "declined"
25323
+ ok: item.status !== "failed" && item.status !== "declined",
25324
+ output: toolDetailPreview(item.type === "commandExecution" ? { output: item.aggregatedOutput, exitCode: item.exitCode } : item.type === "mcpToolCall" ? item.error ?? item.result : item.type === "fileChange" ? item.changes : item.action)
25281
25325
  });
25282
25326
  } else if (item.type === "reasoning") {
25283
25327
  emit({ ...base2(threadId, turnId), type: "item.updated", itemType: "reasoning", tokens: null });
@@ -25568,7 +25612,7 @@ ${stderr ?? ""}`))
25568
25612
  version: version2,
25569
25613
  authenticated,
25570
25614
  ...email3 ? { account: { email: email3 } } : {},
25571
- update: codexAstraUpdate(version2, models, config2.cli),
25615
+ update: codexAstraUpdate(version2, models2, config2.cli),
25572
25616
  billing: "subscription"
25573
25617
  };
25574
25618
  };
@@ -25578,7 +25622,7 @@ ${stderr ?? ""}`))
25578
25622
  displayName: input.displayName,
25579
25623
  enabled: input.enabled,
25580
25624
  get models() {
25581
- return models;
25625
+ return models2;
25582
25626
  },
25583
25627
  refreshModels,
25584
25628
  startAuthentication: () => authentication.start(),
@@ -25925,12 +25969,12 @@ var init_grok = __esm({
25925
25969
  });
25926
25970
 
25927
25971
  // server/drivers/acp/grok.ts
25928
- import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
25972
+ import { existsSync as existsSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "node:fs";
25929
25973
  import { homedir as homedir14 } from "node:os";
25930
- import { join as join25 } from "node:path";
25974
+ import { join as join24 } from "node:path";
25931
25975
  function grokHome(env) {
25932
25976
  if (env.GROK_HOME) return env.GROK_HOME;
25933
- return join25(env.HOME || env.USERPROFILE || homedir14(), ".grok");
25977
+ return join24(env.HOME || env.USERPROFILE || homedir14(), ".grok");
25934
25978
  }
25935
25979
  function unquote2(raw) {
25936
25980
  const value = raw.trim();
@@ -25940,10 +25984,10 @@ function unquote2(raw) {
25940
25984
  return value;
25941
25985
  }
25942
25986
  function readGrokModelCatalog(env = process.env) {
25943
- const path3 = join25(grokHome(env), "config.toml");
25987
+ const path3 = join24(grokHome(env), "config.toml");
25944
25988
  let text4 = "";
25945
25989
  try {
25946
- text4 = readFileSync11(path3, "utf8");
25990
+ text4 = readFileSync10(path3, "utf8");
25947
25991
  } catch {
25948
25992
  return STATIC_GROK_MODELS;
25949
25993
  }
@@ -26013,10 +26057,10 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
26013
26057
  if (!inject) return modelId;
26014
26058
  const host = localHost(inject.host);
26015
26059
  if (!host) return modelId;
26016
- const path3 = join25(grokHome(env), "config.toml");
26060
+ const path3 = join24(grokHome(env), "config.toml");
26017
26061
  let text4 = "";
26018
26062
  try {
26019
- text4 = readFileSync11(path3, "utf8");
26063
+ text4 = readFileSync10(path3, "utf8");
26020
26064
  } catch {
26021
26065
  text4 = "";
26022
26066
  }
@@ -26067,7 +26111,7 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
26067
26111
  const next = text4 && !text4.endsWith("\n") ? `${text4}
26068
26112
 
26069
26113
  ${block2}` : `${text4}${text4 ? "\n" : ""}${block2}`;
26070
- writeFileSync7(path3, next);
26114
+ writeFileSync6(path3, next);
26071
26115
  return slug;
26072
26116
  }
26073
26117
  function grokAcceptsUnadvertisedImages(init) {
@@ -26159,7 +26203,7 @@ var init_grok2 = __esm({
26159
26203
  // an unauthenticated CLI is a user action, not something to paper over.
26160
26204
  pickAuthMethod: (methods) => methods.some((m) => m.id === "cached_token") ? "cached_token" : null,
26161
26205
  authFailure: "fail",
26162
- isAuthenticated: () => existsSync11(join25(homedir14(), ".grok", "auth.json")),
26206
+ isAuthenticated: () => existsSync11(join24(homedir14(), ".grok", "auth.json")),
26163
26207
  // `--append-system-prompt`/`--rules` are accepted by the CLI but do NOT
26164
26208
  // reach the agent-stdio system prompt (verified against 1.0.0), so the
26165
26209
  // persona is prepended codex-style.
@@ -26172,12 +26216,12 @@ ${turn.text}` : turn.text
26172
26216
  });
26173
26217
 
26174
26218
  // server/drivers/acp/gemini.ts
26175
- import { existsSync as existsSync12, readFileSync as readFileSync12 } from "node:fs";
26219
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
26176
26220
  import { homedir as homedir15 } from "node:os";
26177
- import { join as join26 } from "node:path";
26221
+ import { join as join25 } from "node:path";
26178
26222
  function liveOauthCredential() {
26179
26223
  try {
26180
- const creds = JSON.parse(readFileSync12(join26(homedir15(), ".gemini", "oauth_creds.json"), "utf8"));
26224
+ const creds = JSON.parse(readFileSync11(join25(homedir15(), ".gemini", "oauth_creds.json"), "utf8"));
26181
26225
  if (typeof creds.access_token !== "string" || creds.access_token.trim().length === 0) return false;
26182
26226
  const hasRefresh = typeof creds.refresh_token === "string" && creds.refresh_token.trim().length > 0;
26183
26227
  if (creds.expiry_date !== void 0) {
@@ -26190,7 +26234,7 @@ function liveOauthCredential() {
26190
26234
  }
26191
26235
  }
26192
26236
  function geminiIsAuthenticated(env) {
26193
- return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync12(join26(homedir15(), ".gemini", "oauth_creds.json")) && liveOauthCredential();
26237
+ return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync12(join25(homedir15(), ".gemini", "oauth_creds.json")) && liveOauthCredential();
26194
26238
  }
26195
26239
  var AUTH_PREFERENCE, nonBlank, support3, GeminiAgentDriver;
26196
26240
  var init_gemini = __esm({
@@ -26232,17 +26276,17 @@ ${turn.text}` : turn.text
26232
26276
  });
26233
26277
 
26234
26278
  // server/drivers/acp/kimi.ts
26235
- import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "node:fs";
26279
+ import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
26236
26280
  import { homedir as homedir16 } from "node:os";
26237
- import { join as join27 } from "node:path";
26281
+ import { join as join26 } from "node:path";
26238
26282
  function isLocalInjectAlias(slug) {
26239
26283
  return LOCAL_PROVIDER_PREFIXES.some((prefix) => slug.startsWith(prefix));
26240
26284
  }
26241
26285
  function kimiDataRoot(env) {
26242
- return env.KIMI_CODE_HOME || join27(env.HOME || env.USERPROFILE || homedir16(), ".kimi-code");
26286
+ return env.KIMI_CODE_HOME || join26(env.HOME || env.USERPROFILE || homedir16(), ".kimi-code");
26243
26287
  }
26244
26288
  function credentialsPath(env) {
26245
- return join27(kimiDataRoot(env), "credentials", "kimi-code.json");
26289
+ return join26(kimiDataRoot(env), "credentials", "kimi-code.json");
26246
26290
  }
26247
26291
  function quoteToml2(value) {
26248
26292
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
@@ -26530,10 +26574,10 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
26530
26574
  const alias = `${inject.host}/${inject.model.replace(/\//g, "-")}`;
26531
26575
  const dataRoot = kimiDataRoot(env);
26532
26576
  mkdirSync10(dataRoot, { recursive: true });
26533
- const path3 = join27(dataRoot, "config.toml");
26577
+ const path3 = join26(dataRoot, "config.toml");
26534
26578
  let text4 = "";
26535
26579
  try {
26536
- text4 = readFileSync13(path3, "utf8");
26580
+ text4 = readFileSync12(path3, "utf8");
26537
26581
  } catch {
26538
26582
  text4 = "";
26539
26583
  }
@@ -26571,9 +26615,9 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
26571
26615
 
26572
26616
  ` : text4 ? `${text4}
26573
26617
  ` : "";
26574
- writeFileSync8(path3, `${prefix}${blocks.join("\n")}`);
26618
+ writeFileSync7(path3, `${prefix}${blocks.join("\n")}`);
26575
26619
  } else if (text4 !== original) {
26576
- writeFileSync8(path3, text4);
26620
+ writeFileSync7(path3, text4);
26577
26621
  }
26578
26622
  return alias;
26579
26623
  }
@@ -26595,7 +26639,7 @@ function readKimiModelCatalog(env) {
26595
26639
  const dataRoot = kimiDataRoot(env);
26596
26640
  let text4 = "";
26597
26641
  try {
26598
- text4 = readFileSync13(join27(dataRoot, "config.toml"), "utf8");
26642
+ text4 = readFileSync12(join26(dataRoot, "config.toml"), "utf8");
26599
26643
  } catch {
26600
26644
  return STATIC_KIMI_MODELS;
26601
26645
  }
@@ -26714,12 +26758,12 @@ ${turn.text}` : turn.text
26714
26758
  });
26715
26759
 
26716
26760
  // server/drivers/acp/droid.ts
26717
- import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "node:fs";
26761
+ import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "node:fs";
26718
26762
  import { homedir as homedir17 } from "node:os";
26719
- import { join as join28 } from "node:path";
26763
+ import { join as join27 } from "node:path";
26720
26764
  function authFilePaths(env) {
26721
26765
  const home = env.FACTORY_HOME_OVERRIDE || env.HOME || homedir17();
26722
- return AUTH_FILES.map((name) => join28(home, ".factory", name));
26766
+ return AUTH_FILES.map((name) => join27(home, ".factory", name));
26723
26767
  }
26724
26768
  function droidInjectId(host, model) {
26725
26769
  const safe = `${host}-${model}`.replace(/[^a-zA-Z0-9._+-]+/g, "-").replace(/-+/g, "-");
@@ -26734,12 +26778,12 @@ function ensureDroidInjectModel(modelId, env = process.env) {
26734
26778
  const host = localHost(inject.host);
26735
26779
  if (!host) return modelId;
26736
26780
  const id = droidInjectId(inject.host, inject.model);
26737
- const dir = join28(factoryHome(env), ".factory");
26781
+ const dir = join27(factoryHome(env), ".factory");
26738
26782
  mkdirSync11(dir, { recursive: true });
26739
- const path3 = join28(dir, "settings.json");
26783
+ const path3 = join27(dir, "settings.json");
26740
26784
  let settings2 = {};
26741
26785
  try {
26742
- settings2 = JSON.parse(readFileSync14(path3, "utf8"));
26786
+ settings2 = JSON.parse(readFileSync13(path3, "utf8"));
26743
26787
  } catch (error51) {
26744
26788
  if (existsSync14(path3)) throw error51;
26745
26789
  }
@@ -26751,7 +26795,7 @@ function ensureDroidInjectModel(modelId, env = process.env) {
26751
26795
  if (!match.id) {
26752
26796
  match.id = id;
26753
26797
  settings2.customModels = custom2;
26754
- writeFileSync9(path3, `${JSON.stringify(settings2, null, 2)}
26798
+ writeFileSync8(path3, `${JSON.stringify(settings2, null, 2)}
26755
26799
  `);
26756
26800
  }
26757
26801
  return match.id;
@@ -26765,7 +26809,7 @@ function ensureDroidInjectModel(modelId, env = process.env) {
26765
26809
  provider: "generic-chat-completion-api"
26766
26810
  });
26767
26811
  settings2.customModels = custom2;
26768
- writeFileSync9(path3, `${JSON.stringify(settings2, null, 2)}
26812
+ writeFileSync8(path3, `${JSON.stringify(settings2, null, 2)}
26769
26813
  `);
26770
26814
  return id;
26771
26815
  }
@@ -26776,7 +26820,7 @@ function applyDroidLocalAuthEnv(env, modelId) {
26776
26820
  env.FACTORY_API_KEY = "openmausbot-local";
26777
26821
  }
26778
26822
  function readSettings(env) {
26779
- return JSON.parse(readFileSync14(join28(factoryHome(env), ".factory", "settings.json"), "utf8"));
26823
+ return JSON.parse(readFileSync13(join27(factoryHome(env), ".factory", "settings.json"), "utf8"));
26780
26824
  }
26781
26825
  async function resolveModels(env) {
26782
26826
  let catalog = MODELS3;
@@ -27166,9 +27210,9 @@ ${turn.text}` : turn.text
27166
27210
  });
27167
27211
 
27168
27212
  // server/drivers/acp/opencode-go.ts
27169
- import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "node:fs";
27213
+ import { existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "node:fs";
27170
27214
  import { homedir as homedir18 } from "node:os";
27171
- import { join as join29 } from "node:path";
27215
+ import { join as join28 } from "node:path";
27172
27216
  function labelForModel(id) {
27173
27217
  return id.split(/[-_.]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
27174
27218
  }
@@ -27269,7 +27313,7 @@ async function discoverOpenCodeModels(environment, cli = "opencode") {
27269
27313
  }
27270
27314
  function opencodeConfigDir(env) {
27271
27315
  const home = env.HOME || env.USERPROFILE || homedir18();
27272
- return join29(env.XDG_CONFIG_HOME || join29(home, ".config"), "opencode");
27316
+ return join28(env.XDG_CONFIG_HOME || join28(home, ".config"), "opencode");
27273
27317
  }
27274
27318
  function ensureOpenCodeInjectModel(modelId, env = process.env) {
27275
27319
  const inject = decodeInjectId(modelId);
@@ -27279,11 +27323,11 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
27279
27323
  const native = `${inject.host}/${inject.model}`;
27280
27324
  const dir = opencodeConfigDir(env);
27281
27325
  mkdirSync12(dir, { recursive: true });
27282
- const path3 = join29(dir, "opencode.json");
27326
+ const path3 = join28(dir, "opencode.json");
27283
27327
  let config2 = { $schema: "https://opencode.ai/config.json" };
27284
27328
  if (existsSync15(path3)) {
27285
27329
  try {
27286
- config2 = JSON.parse(readFileSync15(path3, "utf8"));
27330
+ config2 = JSON.parse(readFileSync14(path3, "utf8"));
27287
27331
  } catch {
27288
27332
  }
27289
27333
  }
@@ -27298,29 +27342,29 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
27298
27342
  const options = existing.options && typeof existing.options === "object" && !Array.isArray(existing.options) ? { ...existing.options } : {};
27299
27343
  options.baseURL = host.baseUrl;
27300
27344
  if (!options.apiKey) options.apiKey = hostApiKey(host, env);
27301
- const models = existing.models && typeof existing.models === "object" && !Array.isArray(existing.models) ? { ...existing.models } : {};
27302
- if (!models[inject.model]) {
27303
- models[inject.model] = { name: `${inject.model} (${host.label})` };
27345
+ const models2 = existing.models && typeof existing.models === "object" && !Array.isArray(existing.models) ? { ...existing.models } : {};
27346
+ if (!models2[inject.model]) {
27347
+ models2[inject.model] = { name: `${inject.model} (${host.label})` };
27304
27348
  }
27305
27349
  providers[inject.host] = {
27306
27350
  ...existing,
27307
27351
  npm: existing.npm || "@ai-sdk/openai-compatible",
27308
27352
  name: existing.name || host.label,
27309
27353
  options,
27310
- models
27354
+ models: models2
27311
27355
  };
27312
27356
  config2.provider = providers;
27313
- writeFileSync10(path3, `${JSON.stringify(config2, null, 2)}
27357
+ writeFileSync9(path3, `${JSON.stringify(config2, null, 2)}
27314
27358
  `);
27315
27359
  return native;
27316
27360
  }
27317
27361
  function storedAuthPaths(env) {
27318
27362
  const home = env.HOME || env.USERPROFILE || homedir18();
27319
27363
  const roots = [
27320
- env.XDG_DATA_HOME || join29(home, ".local", "share"),
27321
- process.platform === "darwin" ? join29(home, "Library", "Application Support") : process.platform === "win32" ? env.LOCALAPPDATA || join29(home, "AppData", "Local") : ""
27364
+ env.XDG_DATA_HOME || join28(home, ".local", "share"),
27365
+ process.platform === "darwin" ? join28(home, "Library", "Application Support") : process.platform === "win32" ? env.LOCALAPPDATA || join28(home, "AppData", "Local") : ""
27322
27366
  ].filter(Boolean);
27323
- return [...new Set(roots)].map((root) => join29(root, "opencode", "auth.json"));
27367
+ return [...new Set(roots)].map((root) => join28(root, "opencode", "auth.json"));
27324
27368
  }
27325
27369
  function usableAuthEntry(parsed) {
27326
27370
  return Object.values(parsed).some((auth) => {
@@ -27334,7 +27378,7 @@ function hasStoredOpenCodeAuth(env) {
27334
27378
  if (env.OPENCODE_AUTH_CONTENT) candidates.push(env.OPENCODE_AUTH_CONTENT);
27335
27379
  for (const path3 of storedAuthPaths(env)) {
27336
27380
  try {
27337
- candidates.push(readFileSync15(path3, "utf8"));
27381
+ candidates.push(readFileSync14(path3, "utf8"));
27338
27382
  } catch {
27339
27383
  }
27340
27384
  }
@@ -27450,13 +27494,13 @@ ${turn.text}` : turn.text
27450
27494
  });
27451
27495
 
27452
27496
  // server/drivers/acp/qwen.ts
27453
- import { chmodSync as chmodSync5, existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "node:fs";
27497
+ import { chmodSync as chmodSync5, existsSync as existsSync16, mkdirSync as mkdirSync13, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "node:fs";
27454
27498
  import { createHash as createHash7 } from "node:crypto";
27455
27499
  import { homedir as homedir19 } from "node:os";
27456
- import { join as join30 } from "node:path";
27500
+ import { join as join29 } from "node:path";
27457
27501
  function qwenHome(env) {
27458
27502
  const home = process.platform === "win32" ? env.USERPROFILE || env.HOME || homedir19() : env.HOME || env.USERPROFILE || homedir19();
27459
- return join30(home, ".qwen");
27503
+ return join29(home, ".qwen");
27460
27504
  }
27461
27505
  function isRecord2(value) {
27462
27506
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -27468,7 +27512,7 @@ function qwenModelLabel(id, name) {
27468
27512
  function readQwenRoutes(env) {
27469
27513
  let settings2;
27470
27514
  try {
27471
- settings2 = JSON.parse(readFileSync16(join30(qwenHome(env), "settings.json"), "utf8"));
27515
+ settings2 = JSON.parse(readFileSync15(join29(qwenHome(env), "settings.json"), "utf8"));
27472
27516
  } catch {
27473
27517
  return [];
27474
27518
  }
@@ -27552,11 +27596,11 @@ function ensureQwenInjectModel(modelId, env = process.env) {
27552
27596
  if (!host) return modelId;
27553
27597
  const dir = qwenHome(env);
27554
27598
  mkdirSync13(dir, { recursive: true });
27555
- const path3 = join30(dir, "settings.json");
27599
+ const path3 = join29(dir, "settings.json");
27556
27600
  let settings2 = {};
27557
27601
  if (existsSync16(path3)) {
27558
27602
  try {
27559
- settings2 = JSON.parse(readFileSync16(path3, "utf8"));
27603
+ settings2 = JSON.parse(readFileSync15(path3, "utf8"));
27560
27604
  } catch {
27561
27605
  }
27562
27606
  }
@@ -27580,7 +27624,7 @@ function ensureQwenInjectModel(modelId, env = process.env) {
27580
27624
  providers.openai = openai;
27581
27625
  settings2.modelProviders = providers;
27582
27626
  }
27583
- writeFileSync11(path3, `${JSON.stringify(settings2, null, 2)}
27627
+ writeFileSync10(path3, `${JSON.stringify(settings2, null, 2)}
27584
27628
  `, { mode: 384 });
27585
27629
  try {
27586
27630
  chmodSync5(path3, 384);
@@ -29644,7 +29688,7 @@ var init_YAMLMap = __esm({
29644
29688
  static from(schema4, obj, ctx) {
29645
29689
  const { keepUndefined, replacer } = ctx;
29646
29690
  const map3 = new this(schema4);
29647
- const add2 = (key, value) => {
29691
+ const add = (key, value) => {
29648
29692
  if (typeof replacer === "function")
29649
29693
  value = replacer.call(obj, key, value);
29650
29694
  else if (Array.isArray(replacer) && !replacer.includes(key))
@@ -29654,10 +29698,10 @@ var init_YAMLMap = __esm({
29654
29698
  };
29655
29699
  if (obj instanceof Map) {
29656
29700
  for (const [key, value] of obj)
29657
- add2(key, value);
29701
+ add(key, value);
29658
29702
  } else if (obj && typeof obj === "object") {
29659
29703
  for (const key of Object.keys(obj))
29660
- add2(key, obj[key]);
29704
+ add(key, obj[key]);
29661
29705
  }
29662
29706
  if (typeof schema4.sortMapEntries === "function") {
29663
29707
  map3.items.sort(schema4.sortMapEntries);
@@ -34551,9 +34595,9 @@ var init_browser = __esm({
34551
34595
 
34552
34596
  // server/drivers/acp/hermes.ts
34553
34597
  import { spawn as spawn6 } from "node:child_process";
34554
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync17, writeFileSync as writeFileSync12 } from "node:fs";
34598
+ import { mkdirSync as mkdirSync14, readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "node:fs";
34555
34599
  import { homedir as homedir20 } from "node:os";
34556
- import { join as join31 } from "node:path";
34600
+ import { join as join30 } from "node:path";
34557
34601
  function bindHermesScreenshotCompat(env, modelId) {
34558
34602
  delete env[HERMES_OPENMAUS_SCREENSHOT_COMPAT];
34559
34603
  delete env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL];
@@ -34563,7 +34607,7 @@ function bindHermesScreenshotCompat(env, modelId) {
34563
34607
  env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL] = inject.model;
34564
34608
  }
34565
34609
  function hermesHome(env) {
34566
- return env.HERMES_HOME || join31(env.HOME || env.USERPROFILE || homedir20(), ".hermes");
34610
+ return env.HERMES_HOME || join30(env.HOME || env.USERPROFILE || homedir20(), ".hermes");
34567
34611
  }
34568
34612
  function quoteYaml(value) {
34569
34613
  if (/^[\w./:+-]+$/.test(value)) return value;
@@ -34605,15 +34649,15 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
34605
34649
  if (!host) return modelId;
34606
34650
  const dir = hermesHome(env);
34607
34651
  mkdirSync14(dir, { recursive: true });
34608
- const path3 = join31(dir, "config.yaml");
34652
+ const path3 = join30(dir, "config.yaml");
34609
34653
  let text4 = "";
34610
34654
  try {
34611
- text4 = readFileSync17(path3, "utf8");
34655
+ text4 = readFileSync16(path3, "utf8");
34612
34656
  } catch {
34613
34657
  text4 = "";
34614
34658
  }
34615
34659
  const next = upsertHermesProvider(text4, inject.host, host.baseUrl, hostApiKey(host, env));
34616
- if (next !== text4) writeFileSync12(path3, next);
34660
+ if (next !== text4) writeFileSync11(path3, next);
34617
34661
  return hermesAcpModelId(modelId) ?? modelId;
34618
34662
  }
34619
34663
  function hermesAcpModelId(modelId) {
@@ -34676,13 +34720,13 @@ function hermesConfiguredModel(env = process.env) {
34676
34720
  const dir = hermesHome(env);
34677
34721
  let secrets = "";
34678
34722
  try {
34679
- secrets = readFileSync17(join31(dir, ".env"), "utf8");
34723
+ secrets = readFileSync16(join30(dir, ".env"), "utf8");
34680
34724
  } catch {
34681
34725
  }
34682
34726
  const hasHostedProviderKey = HERMES_HOSTED_PROVIDER_KEYS.some((name) => nonEmptyDotenvValue(secrets, name));
34683
34727
  let configuredDefault = null;
34684
34728
  try {
34685
- configuredDefault = hermesConfigDefault(readFileSync17(join31(dir, "config.yaml"), "utf8"));
34729
+ configuredDefault = hermesConfigDefault(readFileSync16(join30(dir, "config.yaml"), "utf8"));
34686
34730
  } catch {
34687
34731
  }
34688
34732
  const configuredProvider = configuredDefault?.provider.toLowerCase() ?? "";
@@ -35019,13 +35063,13 @@ var init_openai_compat = __esm({
35019
35063
  });
35020
35064
 
35021
35065
  // server/drivers/pi.ts
35022
- import { chmodSync as chmodSync6, existsSync as existsSync17, mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync3, readFileSync as readFileSync18, rmSync as rmSync5, writeFileSync as writeFileSync13 } from "node:fs";
35066
+ import { chmodSync as chmodSync6, existsSync as existsSync17, mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync3, readFileSync as readFileSync17, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "node:fs";
35023
35067
  import { homedir as homedir21, tmpdir as tmpdir4 } from "node:os";
35024
- import { join as join32 } from "node:path";
35068
+ import { join as join31 } from "node:path";
35025
35069
  function readPiPromptImages(turn) {
35026
35070
  return (turn.images ?? []).map((image) => ({
35027
35071
  type: "image",
35028
- data: readFileSync18(image.path).toString("base64"),
35072
+ data: readFileSync17(image.path).toString("base64"),
35029
35073
  mimeType: image.mime
35030
35074
  }));
35031
35075
  }
@@ -35128,7 +35172,7 @@ async function applyPiLocalCatalog(catalog, env = process.env, fetchImpl = fetch
35128
35172
  return preferPiInjectRows(await mergeLocalInject(catalog, env, fetchImpl));
35129
35173
  }
35130
35174
  function piAgentDir(env) {
35131
- return join32(env.HOME || env.USERPROFILE || homedir21(), ".pi", "agent");
35175
+ return join31(env.HOME || env.USERPROFILE || homedir21(), ".pi", "agent");
35132
35176
  }
35133
35177
  function ensurePiInjectModel(modelId, env = process.env) {
35134
35178
  const split = splitPiModel(modelId);
@@ -35139,11 +35183,11 @@ function ensurePiInjectModel(modelId, env = process.env) {
35139
35183
  if (!host) return split;
35140
35184
  const dir = piAgentDir(env);
35141
35185
  mkdirSync15(dir, { recursive: true });
35142
- const path3 = join32(dir, "models.json");
35186
+ const path3 = join31(dir, "models.json");
35143
35187
  let root = { providers: {} };
35144
35188
  if (existsSync17(path3)) {
35145
35189
  try {
35146
- const parsed = JSON.parse(readFileSync18(path3, "utf8"));
35190
+ const parsed = JSON.parse(readFileSync17(path3, "utf8"));
35147
35191
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
35148
35192
  root = parsed;
35149
35193
  } else {
@@ -35168,11 +35212,11 @@ function ensurePiInjectModel(modelId, env = process.env) {
35168
35212
  if (!existing.compat) {
35169
35213
  existing.compat = { supportsDeveloperRole: false, supportsReasoningEffort: true };
35170
35214
  }
35171
- const models = Array.isArray(existing.models) ? existing.models.filter(
35215
+ const models2 = Array.isArray(existing.models) ? existing.models.filter(
35172
35216
  (row) => Boolean(row) && typeof row === "object" && !Array.isArray(row)
35173
35217
  ) : [];
35174
- if (!models.some((row) => row.id === inject.model)) {
35175
- models.push({
35218
+ if (!models2.some((row) => row.id === inject.model)) {
35219
+ models2.push({
35176
35220
  id: inject.model,
35177
35221
  name: inject.model,
35178
35222
  reasoning: true,
@@ -35181,10 +35225,10 @@ function ensurePiInjectModel(modelId, env = process.env) {
35181
35225
  maxTokens: 16384
35182
35226
  });
35183
35227
  }
35184
- existing.models = models;
35228
+ existing.models = models2;
35185
35229
  providers[inject.host] = existing;
35186
35230
  root.providers = providers;
35187
- writeFileSync13(path3, `${JSON.stringify(root, null, 2)}
35231
+ writeFileSync12(path3, `${JSON.stringify(root, null, 2)}
35188
35232
  `, { mode: 384 });
35189
35233
  try {
35190
35234
  chmodSync6(path3, 384);
@@ -35195,7 +35239,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
35195
35239
  function readPiDefaultModel(env) {
35196
35240
  const home = env.HOME || env.USERPROFILE || homedir21();
35197
35241
  try {
35198
- const s = JSON.parse(readFileSync18(`${home}/.pi/agent/settings.json`, "utf8"));
35242
+ const s = JSON.parse(readFileSync17(`${home}/.pi/agent/settings.json`, "utf8"));
35199
35243
  return s.defaultProvider && s.defaultModel ? `${s.defaultProvider}/${s.defaultModel}` : "";
35200
35244
  } catch {
35201
35245
  return "";
@@ -35279,6 +35323,7 @@ var init_pi = __esm({
35279
35323
  init_env_path();
35280
35324
  init_procs();
35281
35325
  init_proxy_paths();
35326
+ init_tool_summary();
35282
35327
  init_contracts();
35283
35328
  init_local_inject();
35284
35329
  init_native();
@@ -35305,9 +35350,9 @@ var init_pi = __esm({
35305
35350
  async create(input) {
35306
35351
  const { instanceId, config: config2 } = input;
35307
35352
  const catalogEnv = piEnvironment({ ...process.env, ...input.environment });
35308
- let models = EMPTY3;
35353
+ let models2 = EMPTY3;
35309
35354
  const readModels = async () => {
35310
- let base3 = models;
35355
+ let base3 = models2;
35311
35356
  try {
35312
35357
  const resolved = await fetchPiModels(config2.cli, catalogEnv);
35313
35358
  if (resolved.options.length) base3 = resolved;
@@ -35315,9 +35360,9 @@ var init_pi = __esm({
35315
35360
  }
35316
35361
  try {
35317
35362
  const next = await applyPiLocalCatalog(base3, catalogEnv);
35318
- if (next.options.length) models = next;
35363
+ if (next.options.length) models2 = next;
35319
35364
  } catch {
35320
- if (base3.options.length) models = base3;
35365
+ if (base3.options.length) models2 = base3;
35321
35366
  }
35322
35367
  };
35323
35368
  const refreshModels = async () => {
@@ -35356,9 +35401,9 @@ var init_pi = __esm({
35356
35401
  const mcpServers = buildMcpServers(turn);
35357
35402
  let mcpTempDir = null;
35358
35403
  if (mcpServers) {
35359
- mcpTempDir = mkdtempSync3(join32(tmpdir4(), "omb-pi-mcp-"));
35404
+ mcpTempDir = mkdtempSync3(join31(tmpdir4(), "omb-pi-mcp-"));
35360
35405
  try {
35361
- writeFileSync13(join32(mcpTempDir, "mcp.json"), JSON.stringify({ mcpServers }), { mode: 384 });
35406
+ writeFileSync12(join31(mcpTempDir, "mcp.json"), JSON.stringify({ mcpServers }), { mode: 384 });
35362
35407
  } catch (err) {
35363
35408
  try {
35364
35409
  rmSync5(mcpTempDir, { recursive: true, force: true });
@@ -35376,7 +35421,7 @@ var init_pi = __esm({
35376
35421
  env: piEnvironment({
35377
35422
  ...process.env,
35378
35423
  ...input.environment,
35379
- ...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG: join32(mcpTempDir, "mcp.json") } : {}
35424
+ ...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG: join31(mcpTempDir, "mcp.json") } : {}
35380
35425
  })
35381
35426
  });
35382
35427
  } catch (err) {
@@ -35488,7 +35533,9 @@ var init_pi = __esm({
35488
35533
  type: "item.started",
35489
35534
  itemType: "tool",
35490
35535
  itemId: evt.toolCallId,
35491
- title: String(evt.toolName ?? "tool").slice(0, 80)
35536
+ title: String(evt.toolName ?? "tool").slice(0, 80),
35537
+ summary: commandSummary(evt.args),
35538
+ input: toolDetailPreview(evt.args)
35492
35539
  });
35493
35540
  return;
35494
35541
  }
@@ -35498,7 +35545,8 @@ var init_pi = __esm({
35498
35545
  type: "item.completed",
35499
35546
  itemType: "tool",
35500
35547
  itemId: evt.toolCallId,
35501
- ok: !evt.isError
35548
+ ok: !evt.isError,
35549
+ output: toolDetailPreview(evt.result)
35502
35550
  });
35503
35551
  return;
35504
35552
  }
@@ -35647,7 +35695,7 @@ ${turn.text}` : turn.text;
35647
35695
  displayName: input.displayName,
35648
35696
  enabled: input.enabled,
35649
35697
  get models() {
35650
- return models;
35698
+ return models2;
35651
35699
  },
35652
35700
  refreshModels,
35653
35701
  snapshot,
@@ -35713,16 +35761,16 @@ ${turn.text}` : turn.text;
35713
35761
  });
35714
35762
 
35715
35763
  // server/drivers/minimax.ts
35716
- import { readFileSync as readFileSync19 } from "node:fs";
35764
+ import { readFileSync as readFileSync18 } from "node:fs";
35717
35765
  import { homedir as homedir22 } from "node:os";
35718
- import { join as join33 } from "node:path";
35766
+ import { join as join32 } from "node:path";
35719
35767
  function normalizedApiUrl(value) {
35720
35768
  const root = value.trim().replace(/\/+$/, "");
35721
35769
  return root.endsWith("/v1") ? root : `${root}/v1`;
35722
35770
  }
35723
35771
  function loadLocalMiniMaxConfig(home = homedir22()) {
35724
35772
  try {
35725
- const raw = localConfigSchema.parse(JSON.parse(readFileSync19(join33(home, ".mmx", "config.json"), "utf8")));
35773
+ const raw = localConfigSchema.parse(JSON.parse(readFileSync18(join32(home, ".mmx", "config.json"), "utf8")));
35726
35774
  const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL2;
35727
35775
  return {
35728
35776
  apiKey: raw.api_key?.trim() ?? "",
@@ -35785,13 +35833,13 @@ var init_minimax = __esm({
35785
35833
  const local = loadLocalMiniMaxConfig();
35786
35834
  const apiKey = input.environment[API_KEY_ENV]?.trim() || process.env[API_KEY_ENV]?.trim() || local.apiKey;
35787
35835
  const apiUrl = input.config.url === DEFAULT_URL2 && local.url !== DEFAULT_URL2 ? local.url : input.config.url;
35788
- const models = local.defaultModel && MODELS4.options.some((model) => model.id === local.defaultModel) ? { ...MODELS4, default: local.defaultModel } : MODELS4;
35836
+ const models2 = local.defaultModel && MODELS4.options.some((model) => model.id === local.defaultModel) ? { ...MODELS4, default: local.defaultModel } : MODELS4;
35789
35837
  return createOpenAIChatRuntime({
35790
35838
  input,
35791
35839
  driverKind: DRIVER_KIND7,
35792
35840
  apiKey,
35793
35841
  apiUrl,
35794
- models: () => models,
35842
+ models: () => models2,
35795
35843
  requestBody: (model, messages, stream) => ({
35796
35844
  model,
35797
35845
  messages,
@@ -35936,16 +35984,16 @@ async function setupJson(url2, key, body) {
35936
35984
  }
35937
35985
  async function fetchSetupModels(url2, key) {
35938
35986
  const json2 = record2(await setupJson(url2, key));
35939
- const models = /* @__PURE__ */ new Map();
35987
+ const models2 = /* @__PURE__ */ new Map();
35940
35988
  for (const entry of Array.isArray(json2?.data) ? json2.data : []) {
35941
35989
  const item = record2(entry);
35942
35990
  const id = typeof item?.id === "string" ? item.id.trim() : "";
35943
- if (!id || /[\u0000-\u001f\u007f]/u.test(id) || models.has(id)) continue;
35991
+ if (!id || /[\u0000-\u001f\u007f]/u.test(id) || models2.has(id)) continue;
35944
35992
  const name = typeof item?.name === "string" ? item.name.replace(/[\u0000-\u001f\u007f]/gu, "").trim() : "";
35945
- models.set(id, { id, label: name || id });
35993
+ models2.set(id, { id, label: name || id });
35946
35994
  }
35947
- if (!models.size) throw new SetupApiError("The API returned no usable models. Check the API URL and model access, or enter a model ID from your provider manually.");
35948
- return [...models.values()];
35995
+ if (!models2.size) throw new SetupApiError("The API returned no usable models. Check the API URL and model access, or enter a model ID from your provider manually.");
35996
+ return [...models2.values()];
35949
35997
  }
35950
35998
  async function verifySetupCompletion(url2, key, model, provider) {
35951
35999
  const id = model.trim();
@@ -35989,8 +36037,8 @@ __export(cli_setup_exports, {
35989
36037
  saveCliStartup: () => saveCliStartup
35990
36038
  });
35991
36039
  import { spawn as spawn7 } from "node:child_process";
35992
- import { readFileSync as readFileSync20 } from "node:fs";
35993
- import { join as join34, resolve as resolve10 } from "node:path";
36040
+ import { readFileSync as readFileSync19 } from "node:fs";
36041
+ import { join as join33, resolve as resolve10 } from "node:path";
35994
36042
  import { randomUUID as randomUUID10 } from "node:crypto";
35995
36043
  async function inspect(id, entry) {
35996
36044
  const registry2 = new ProviderRegistry(BUILT_IN_DRIVERS);
@@ -36027,7 +36075,7 @@ function assertDataDir(dataDir) {
36027
36075
  }
36028
36076
  function checkStoredConfig(dataDir) {
36029
36077
  try {
36030
- parseStoredConfig(JSON.parse(readFileSync20(join34(dataDir, "config.json"), "utf8")));
36078
+ parseStoredConfig(JSON.parse(readFileSync19(join33(dataDir, "config.json"), "utf8")));
36031
36079
  } catch (error51) {
36032
36080
  if (error51.code === "ENOENT") return;
36033
36081
  throw new Error("Existing config.json could not be read or validated. Setup has not changed it; repair it before continuing.");
@@ -36059,14 +36107,14 @@ function saveCliStartup(dataDir, settings2) {
36059
36107
  function rawObject(value) {
36060
36108
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
36061
36109
  }
36062
- async function chooseModel(io, models) {
36063
- if (!models.options.length) throw new Error("This connection returned no models. Check the provider and try again.");
36110
+ async function chooseModel(io, models2) {
36111
+ if (!models2.options.length) throw new Error("This connection returned no models. Check the provider and try again.");
36064
36112
  let query = "";
36065
36113
  for (; ; ) {
36066
- const matches = models.options.filter((m) => `${m.label} ${m.id}`.toLowerCase().includes(query.toLowerCase()));
36067
- const preferred = matches.find((m) => m.id === models.default);
36114
+ const matches = models2.options.filter((m) => `${m.label} ${m.id}`.toLowerCase().includes(query.toLowerCase()));
36115
+ const preferred = matches.find((m) => m.id === models2.default);
36068
36116
  const visible = (preferred ? [preferred, ...matches.filter((m) => m !== preferred)] : matches).slice(0, 20);
36069
- const defaultIndex = visible.findIndex((m) => m.id === models.default);
36117
+ const defaultIndex = visible.findIndex((m) => m.id === models2.default);
36070
36118
  const selected = await io.choose("Choose your model", [
36071
36119
  ...visible.map((m) => m.label.trim() || m.id),
36072
36120
  "Search models\u2026"
@@ -36140,11 +36188,11 @@ async function runSetup(options, io = defaultSetupIo(), deps = dependencies) {
36140
36188
  id = prior?.[0] ?? native.id;
36141
36189
  if (!prior && runtime[id] && runtime[id].driver !== native.driver) id = `${native.id}-${randomUUID10().slice(0, 8)}`;
36142
36190
  entry = { ...cfg.instances?.[id] ?? { driver: native.driver }, enabled: true };
36143
- const models = await connectNative(native, id, entry, io, deps);
36191
+ const models2 = await connectNative(native, id, entry, io, deps);
36144
36192
  const saved = cfg.defaultModelSelection;
36145
36193
  model = await chooseModel(io, {
36146
- ...models,
36147
- default: saved?.instanceId === id ? saved.model : models.default
36194
+ ...models2,
36195
+ default: saved?.instanceId === id ? saved.model : models2.default
36148
36196
  });
36149
36197
  } else {
36150
36198
  io.log("Connect an API key");
@@ -36174,21 +36222,21 @@ async function runSetup(options, io = defaultSetupIo(), deps = dependencies) {
36174
36222
  }
36175
36223
  if (!key) throw new Error("An API key is required. Nothing was saved.");
36176
36224
  io.log("Checking the model catalog\u2026");
36177
- let models;
36225
+ let models2;
36178
36226
  try {
36179
- models = await deps.models(url2, key);
36227
+ models2 = await deps.models(url2, key);
36180
36228
  } catch (error51) {
36181
36229
  if (error51 instanceof SetupCancelled) throw error51;
36182
36230
  io.log(error51 instanceof Error ? error51.message : "Could not load this model catalog.");
36183
36231
  if (!await io.confirm("Enter an exact chat model ID and check it directly instead?", false)) throw error51;
36184
36232
  const manual = (await io.ask("Chat model ID: ")).trim();
36185
36233
  if (!manual) throw new Error("A model ID is required. Nothing was saved.");
36186
- models = [{ id: manual, label: manual }];
36234
+ models2 = [{ id: manual, label: manual }];
36187
36235
  }
36188
36236
  const previousModel = prior ? rawObject(prior[1].config).model : void 0;
36189
36237
  const preferredModel = cfg.defaultModelSelection?.instanceId === prior?.[0] ? cfg.defaultModelSelection?.model : previousModel;
36190
36238
  io.log("Choose a chat model; image, audio and embedding-only models cannot reply here.");
36191
- model = await chooseModel(io, { default: typeof preferredModel === "string" ? preferredModel : "", options: models });
36239
+ model = await chooseModel(io, { default: typeof preferredModel === "string" ? preferredModel : "", options: models2 });
36192
36240
  if (!await io.confirm("Send one short test message? Your API provider may charge for this request.", true)) throw new SetupCancelled();
36193
36241
  if (routingProvider) await deps.verify(url2, key, model, routingProvider);
36194
36242
  else await deps.verify(url2, key, model);
@@ -36305,7 +36353,7 @@ var init_browser_engine_release = __esm({
36305
36353
  });
36306
36354
 
36307
36355
  // server/browser-bundle-release.ts
36308
- import { join as join35 } from "node:path";
36356
+ import { join as join34 } from "node:path";
36309
36357
  function browserBundleSpec(target) {
36310
36358
  if (!Object.hasOwn(CHROME_ASSETS, target)) throw new Error(`Unsupported desktop browser target: ${target}`);
36311
36359
  const pinned = CHROME_ASSETS[target];
@@ -36341,10 +36389,10 @@ function browserBundlePaths(bundleDirectory, target) {
36341
36389
  const spec = browserBundleSpec(target);
36342
36390
  return {
36343
36391
  directory: bundleDirectory,
36344
- manifest: join35(bundleDirectory, "manifest.json"),
36345
- engine: join35(bundleDirectory, spec.engine.executable),
36346
- chrome: join35(bundleDirectory, spec.chrome.executable),
36347
- licenses: join35(bundleDirectory, "licenses")
36392
+ manifest: join34(bundleDirectory, "manifest.json"),
36393
+ engine: join34(bundleDirectory, spec.engine.executable),
36394
+ chrome: join34(bundleDirectory, spec.chrome.executable),
36395
+ licenses: join34(bundleDirectory, "licenses")
36348
36396
  };
36349
36397
  }
36350
36398
  var CHROME_VERSION, CHROME_ASSETS;
@@ -36384,7 +36432,7 @@ var init_bot_profile = __esm({
36384
36432
  });
36385
36433
 
36386
36434
  // server/bot-folder.ts
36387
- import { join as join36 } from "node:path";
36435
+ import { join as join35 } from "node:path";
36388
36436
  var BOTS_DIR;
36389
36437
  var init_bot_folder = __esm({
36390
36438
  "server/bot-folder.ts"() {
@@ -36392,7 +36440,7 @@ var init_bot_folder = __esm({
36392
36440
  init_atomic();
36393
36441
  init_config();
36394
36442
  init_bot_profile();
36395
- BOTS_DIR = join36(DATA_DIR, "bots");
36443
+ BOTS_DIR = join35(DATA_DIR, "bots");
36396
36444
  }
36397
36445
  });
36398
36446
 
@@ -36429,9 +36477,9 @@ __export(browser_engine_exports, {
36429
36477
  });
36430
36478
  import { spawn as spawn8 } from "node:child_process";
36431
36479
  import { createHash as createHash8, randomBytes as randomBytes3, randomUUID as randomUUID11 } from "node:crypto";
36432
- import { chmodSync as chmodSync7, constants as constants2, copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync16, readFileSync as readFileSync21, readdirSync as readdirSync3, renameSync as renameSync5, rmSync as rmSync6, statSync as statSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync14 } from "node:fs";
36480
+ import { chmodSync as chmodSync7, constants as constants2, copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync16, readFileSync as readFileSync20, readdirSync as readdirSync3, renameSync as renameSync5, rmSync as rmSync6, statSync as statSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync13 } from "node:fs";
36433
36481
  import { homedir as homedir23, tmpdir as tmpdir5 } from "node:os";
36434
- import { delimiter as delimiter6, dirname as dirname11, join as join37, resolve as resolve11 } from "node:path";
36482
+ import { delimiter as delimiter6, dirname as dirname11, join as join36, resolve as resolve11 } from "node:path";
36435
36483
  function browserRestoreKey(session) {
36436
36484
  if (!/^[A-Za-z0-9_-]{1,96}$/.test(session)) throw new Error("Invalid browser session.");
36437
36485
  return `omb-${createHash8("sha256").update(session).digest("hex")}`;
@@ -36440,20 +36488,20 @@ function browserSessionsDirectory(env) {
36440
36488
  const namespace = env.AGENT_BROWSER_NAMESPACE;
36441
36489
  if (namespace && !/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(namespace)) throw new Error("Invalid browser namespace.");
36442
36490
  const home = (process.platform === "win32" ? env.USERPROFILE : env.HOME) || homedir23();
36443
- const stateRoot = namespace ? join37(home, ".agent-browser", "namespaces", namespace, "state") : join37(home, ".agent-browser");
36444
- return join37(stateRoot, "sessions");
36491
+ const stateRoot = namespace ? join36(home, ".agent-browser", "namespaces", namespace, "state") : join36(home, ".agent-browser");
36492
+ return join36(stateRoot, "sessions");
36445
36493
  }
36446
36494
  function managedBrowserConfigPath(env) {
36447
- return join37(browserSessionsDirectory(env), "..", "omb-managed-config.json");
36495
+ return join36(browserSessionsDirectory(env), "..", "omb-managed-config.json");
36448
36496
  }
36449
36497
  function ensureManagedBrowserConfig(env) {
36450
36498
  const path3 = managedBrowserConfigPath(env);
36451
36499
  mkdirSync16(dirname11(path3), { recursive: true, mode: 448 });
36452
36500
  try {
36453
- writeFileSync14(path3, "{}\n", { flag: "wx", mode: 384 });
36501
+ writeFileSync13(path3, "{}\n", { flag: "wx", mode: 384 });
36454
36502
  } catch (error51) {
36455
36503
  if (error51.code !== "EEXIST") throw error51;
36456
- if (readFileSync21(path3, "utf8").trim() !== "{}") throw new Error("The managed browser configuration was changed. Restore it to an empty JSON object before connecting.");
36504
+ if (readFileSync20(path3, "utf8").trim() !== "{}") throw new Error("The managed browser configuration was changed. Restore it to an empty JSON object before connecting.");
36457
36505
  }
36458
36506
  return path3;
36459
36507
  }
@@ -36531,11 +36579,11 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
36531
36579
  ensureManagedBrowserConfig(env);
36532
36580
  if (options.persistent === false) return;
36533
36581
  const directory = browserSessionsDirectory(env);
36534
- const key = join37(directory, session);
36582
+ const key = join36(directory, session);
36535
36583
  let pending = preparedBrowserSessions.get(key);
36536
36584
  if (!pending) {
36537
36585
  pending = (async () => {
36538
- const targetBase = join37(directory, `${restoreKey}-${session}`);
36586
+ const targetBase = join36(directory, `${restoreKey}-${session}`);
36539
36587
  const hasTarget = () => existsSync18(`${targetBase}.json.enc`) || existsSync18(`${targetBase}.json`);
36540
36588
  if (hasTarget()) return;
36541
36589
  check2();
@@ -36545,7 +36593,7 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
36545
36593
  check2();
36546
36594
  if (hasTarget()) return;
36547
36595
  const candidates = [".json.enc", ".json"].flatMap((suffix) => {
36548
- const path3 = join37(directory, `${session}-${session}${suffix}`);
36596
+ const path3 = join36(directory, `${session}-${session}${suffix}`);
36549
36597
  try {
36550
36598
  const stat4 = statSync3(path3);
36551
36599
  if (!stat4.isFile()) throw new Error("Saved browser state is not a file.");
@@ -36580,7 +36628,7 @@ function isMusl(platform = process.platform, exists = existsSync18) {
36580
36628
  }
36581
36629
  function pinnedBinaryPath(dataDir = DATA_DIR, platform = process.platform, arch = process.arch) {
36582
36630
  const version2 = agentBrowserReleaseVersion(resolveAgentBrowserReleaseAsset(platform, arch));
36583
- return join37(dataDir, ENGINE_DIR, version2, executableName(platform));
36631
+ return join36(dataDir, ENGINE_DIR, version2, executableName(platform));
36584
36632
  }
36585
36633
  function onPath(env, platform, exists) {
36586
36634
  const pathValue = platform === "win32" ? Object.entries(env).findLast(([key]) => key.toUpperCase() === "PATH")?.[1] : env.PATH;
@@ -36596,7 +36644,7 @@ function packagedBrowser(options) {
36596
36644
  const resources = (options.env ?? process.env).OMB_RESOURCES_PATH;
36597
36645
  if (!resources) return null;
36598
36646
  try {
36599
- return browserBundlePaths(join37(resolve11(resources), "browser-engine"), `${options.platform ?? process.platform}-${options.arch ?? process.arch}`);
36647
+ return browserBundlePaths(join36(resolve11(resources), "browser-engine"), `${options.platform ?? process.platform}-${options.arch ?? process.arch}`);
36600
36648
  } catch {
36601
36649
  return null;
36602
36650
  }
@@ -36621,7 +36669,7 @@ async function installAgentBrowserBinary(options = {}) {
36621
36669
  const asset = options.asset ?? resolveAgentBrowserReleaseAsset(platform, options.arch ?? process.arch, options.musl ?? isMusl(platform));
36622
36670
  if (!asset) throw new Error(`agent-browser publishes no build for ${platform}-${options.arch ?? process.arch}.`);
36623
36671
  const destination = pinnedBinaryPath(options.dataDir, platform, options.arch);
36624
- const directory = join37(destination, "..");
36672
+ const directory = join36(destination, "..");
36625
36673
  mkdirSync16(directory, { recursive: true, mode: 448 });
36626
36674
  const url2 = agentBrowserReleaseUrl(asset);
36627
36675
  options.log?.(`downloading agent-browser ${agentBrowserReleaseVersion(asset)} (${Math.round(asset.bytes / 1024 / 1024)} MB, digest pinned)`);
@@ -36640,7 +36688,7 @@ async function installAgentBrowserBinary(options = {}) {
36640
36688
  const digest2 = createHash8("sha256").update(body).digest("hex");
36641
36689
  if (digest2 !== asset.sha256) throw new Error("the agent-browser download failed its SHA-256 check; nothing was installed");
36642
36690
  const staging = `${destination}.${randomBytes3(6).toString("hex")}.part`;
36643
- writeFileSync14(staging, body, { mode: 493 });
36691
+ writeFileSync13(staging, body, { mode: 493 });
36644
36692
  if (platform !== "win32") chmodSync7(staging, 493);
36645
36693
  renameSync5(staging, destination);
36646
36694
  return destination;
@@ -36673,9 +36721,9 @@ function ensureChrome(binaryPath, options = {}) {
36673
36721
  });
36674
36722
  }
36675
36723
  function browserEngineEncryptionKey(dataDir = DATA_DIR) {
36676
- const file2 = join37(dataDir, KEY_FILE);
36724
+ const file2 = join36(dataDir, KEY_FILE);
36677
36725
  try {
36678
- const existing = readFileSync21(file2, "utf8").trim();
36726
+ const existing = readFileSync20(file2, "utf8").trim();
36679
36727
  if (/^[0-9a-f]{64}$/u.test(existing)) return existing;
36680
36728
  } catch {
36681
36729
  }
@@ -36733,7 +36781,7 @@ function agentBrowserIntegration(input) {
36733
36781
  return { command: input.binaryPath, args: ["mcp", "--tools", "core", "--no-webmcp"], env };
36734
36782
  }
36735
36783
  function agentBrowserFrame(input) {
36736
- const file2 = join37(tmpdir5(), `openmausbot-browser-${randomUUID11()}.png`);
36784
+ const file2 = join36(tmpdir5(), `openmausbot-browser-${randomUUID11()}.png`);
36737
36785
  return new Promise((settle, fail) => {
36738
36786
  const child = spawn8(input.binaryPath, ["screenshot", file2], {
36739
36787
  env: browserRuntimeEnv(input.env),
@@ -36755,7 +36803,7 @@ function agentBrowserFrame(input) {
36755
36803
  fail(error51);
36756
36804
  return;
36757
36805
  }
36758
- settle({ png: readFileSync21(file2).toString("base64"), format: "png" });
36806
+ settle({ png: readFileSync20(file2).toString("base64"), format: "png" });
36759
36807
  } catch {
36760
36808
  fail(new Error("the browser reported a picture it did not write"));
36761
36809
  } finally {
@@ -36803,8 +36851,8 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
36803
36851
  }
36804
36852
  };
36805
36853
  try {
36806
- for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(join37(directory, base2 + suffix));
36807
- const pendingDirectory = join37(directory, ".tmp");
36854
+ for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(join36(directory, base2 + suffix));
36855
+ const pendingDirectory = join36(directory, ".tmp");
36808
36856
  let pendingFiles = [];
36809
36857
  try {
36810
36858
  pendingFiles = readdirSync3(pendingDirectory);
@@ -36812,8 +36860,8 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
36812
36860
  if (error51.code !== "ENOENT") throw error51;
36813
36861
  }
36814
36862
  const candidates = bases.map((base2) => new RegExp(`^${base2}-candidate-[0-9]+\\.json(?:\\.enc)?$`));
36815
- for (const name of pendingFiles) if (candidates.some((candidate) => candidate.test(name))) remove(join37(pendingDirectory, name));
36816
- preparedBrowserSessions.delete(join37(directory, session));
36863
+ for (const name of pendingFiles) if (candidates.some((candidate) => candidate.test(name))) remove(join36(pendingDirectory, name));
36864
+ preparedBrowserSessions.delete(join36(directory, session));
36817
36865
  return true;
36818
36866
  } catch {
36819
36867
  return false;
@@ -36841,9 +36889,9 @@ var init_browser_engine = __esm({
36841
36889
  // server/cli.ts
36842
36890
  var import_qrcode_terminal = __toESM(require_main(), 1);
36843
36891
  import { spawn as spawn9 } from "node:child_process";
36844
- import { closeSync as closeSync3, existsSync as existsSync19, mkdirSync as mkdirSync17, openSync as openSync3, readFileSync as readFileSync22 } from "node:fs";
36892
+ import { closeSync as closeSync4, existsSync as existsSync19, mkdirSync as mkdirSync17, openSync as openSync4, readFileSync as readFileSync21 } from "node:fs";
36845
36893
  import { homedir as homedir24 } from "node:os";
36846
- import { dirname as dirname12, join as join38, resolve as resolve12 } from "node:path";
36894
+ import { dirname as dirname12, join as join37, resolve as resolve12 } from "node:path";
36847
36895
  import { createInterface as createInterface2 } from "node:readline/promises";
36848
36896
  import { fileURLToPath as fileURLToPath3 } from "node:url";
36849
36897
 
@@ -38615,8 +38663,9 @@ function runServiceCommand(input, io) {
38615
38663
  }
38616
38664
 
38617
38665
  // server/fleet-cli.ts
38618
- import { spawn as spawn3 } from "node:child_process";
38619
- import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
38666
+ init_atomic();
38667
+ import { execFileSync, spawn as spawn3 } from "node:child_process";
38668
+ import { appendFileSync, closeSync as closeSync2, existsSync as existsSync2, fsyncSync as fsyncSync2, lstatSync, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync, rmSync as rmSync2 } from "node:fs";
38620
38669
  import { posix as posix3 } from "node:path";
38621
38670
 
38622
38671
  // server/fleet.ts
@@ -38708,8 +38757,9 @@ function templateUnit(spec) {
38708
38757
  "# Written by `openmausbot fleet init`. One unit for every workspace: %i is the slug.",
38709
38758
  "[Unit]",
38710
38759
  "Description=OpenMausBot workspace %i",
38711
- "After=network-online.target",
38760
+ "After=network-online.target openmausbot-fence.service",
38712
38761
  "Wants=network-online.target",
38762
+ "Requires=openmausbot-fence.service",
38713
38763
  "",
38714
38764
  "[Service]",
38715
38765
  "Type=simple",
@@ -38741,7 +38791,6 @@ function fenceUnit(layout = fleetLayout()) {
38741
38791
  "# Written by `openmausbot fleet init`: keeps each workspace's loopback ports to its own user.",
38742
38792
  "[Unit]",
38743
38793
  "Description=OpenMausBot per-workspace loopback fence",
38744
- "Before=openmausbot@.service",
38745
38794
  "",
38746
38795
  "[Service]",
38747
38796
  "Type=oneshot",
@@ -38756,14 +38805,16 @@ function fenceUnit(layout = fleetLayout()) {
38756
38805
  function fenceRules(workspaces) {
38757
38806
  const lines = ["#!/usr/sbin/nft -f", "# Written by `openmausbot fleet`; regenerated on every create, suspend, resume and delete.", "add table inet openmausbot", "flush table inet openmausbot", "table inet openmausbot {", " chain output {", " type filter hook output priority 0; policy accept;"];
38758
38807
  for (const workspace of [...workspaces].sort((a2, b) => a2.slug.localeCompare(b.slug))) {
38808
+ if (workspace.status !== "running" && workspace.status !== "suspended" && workspace.status !== "retained" && !workspace.accountCreated) continue;
38759
38809
  lines.push(` oif lo tcp dport { ${workspace.port}, ${workspace.webhookPort} } meta skuid != { ${fleetUser(workspace.slug)}, caddy, root } reject`);
38760
38810
  }
38761
38811
  lines.push(" }", "}", "");
38762
38812
  return lines.join("\n");
38763
38813
  }
38764
38814
  function caddySite(workspace) {
38765
- if (workspace.status === "suspended") {
38766
- return [`${workspace.host} {`, ' respond "This workspace is suspended." 503', "}", ""].join("\n");
38815
+ if (workspace.status !== "running") {
38816
+ const message2 = workspace.status === "suspended" ? "This workspace is suspended." : "This workspace is not available.";
38817
+ return [`${workspace.host} {`, ` respond "${message2}" 503`, "}", ""].join("\n");
38767
38818
  }
38768
38819
  return [
38769
38820
  `${workspace.host} {`,
@@ -38791,11 +38842,23 @@ function instanceEnv(input) {
38791
38842
  `OMB_PUBLIC_URL=https://${input.workspace.host}`
38792
38843
  ];
38793
38844
  if (input.licenseKey) lines.push(`OMB_LICENSE_KEY=${input.licenseKey}`);
38845
+ if (input.portalUrl) lines.push(`OMB_ADMIN_URL=${input.portalUrl}`, `OMB_ADMIN_WORKSPACE=${input.workspace.slug}`);
38794
38846
  return lines.join("\n") + "\n";
38795
38847
  }
38848
+ var MANAGED_OPENROUTER = "omb-managed-openrouter";
38849
+ function openRouterModelIds(value) {
38850
+ if (!Array.isArray(value) || value.length > 100 || value.some((id) => typeof id !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9._:/+-]{0,159}$/.test(id))) {
38851
+ throw new Error("OpenRouter models must be an array of at most 100 valid model IDs");
38852
+ }
38853
+ return [...new Set(value)];
38854
+ }
38855
+ function managedOpenRouterModels(models2) {
38856
+ return Object.fromEntries(openRouterModelIds(models2).map((id) => [id, { name: id }]));
38857
+ }
38796
38858
  function initialConfig(seed) {
38797
38859
  const config2 = { signIn: { admins: seed.admins, members: seed.members } };
38798
- if (seed.anthropicKey) config2.anthropic = { key: seed.anthropicKey };
38860
+ if (seed.anthropicKey) config2.anthropic = { key: seed.anthropicKey, ...seed.anthropicUrl ? { url: seed.anthropicUrl } : {} };
38861
+ if (seed.openrouterDefault && seed.openrouterModels?.length) config2.defaultModelSelection = { instanceId: "opencodeGo", model: `${MANAGED_OPENROUTER}/${seed.openrouterModels[0]}` };
38799
38862
  if (seed.monthlyCapUsd !== void 0) config2.budgets = { monthlyUsd: seed.monthlyCapUsd };
38800
38863
  return `${JSON.stringify(config2, null, 2)}
38801
38864
  `;
@@ -38839,6 +38902,18 @@ function initPlan(input) {
38839
38902
  }
38840
38903
  function createPlan(input) {
38841
38904
  assertSlug(input.slug);
38905
+ if (input.seed.portalUrl) {
38906
+ const portal = new URL(input.seed.portalUrl);
38907
+ if (portal.protocol !== "https:" || portal.origin !== input.seed.portalUrl) throw new Error("portalUrl must be an HTTPS origin without a path or credentials");
38908
+ if (portal.hostname === `${input.slug}.${input.registry.domain}`) throw new Error("this workspace name is reserved for the admin portal");
38909
+ if (input.seed.anthropicUrl && input.seed.anthropicUrl !== `${portal.origin}/api/gateway/${input.slug}/anthropic`) throw new Error("managed provider URL must belong to this workspace's portal gateway");
38910
+ } else if (input.seed.anthropicUrl) throw new Error("a managed provider URL requires portalUrl");
38911
+ if (input.seed.openrouterKey !== void 0 || input.seed.openrouterUrl !== void 0 || input.seed.openrouterModels !== void 0 || input.seed.openrouterDefault) {
38912
+ if (!input.seed.portalUrl || input.seed.openrouterUrl !== `${input.seed.portalUrl}/api/gateway/${input.slug}/openrouter/v1`) throw new Error("managed OpenRouter URL must belong to this workspace's portal gateway");
38913
+ if (!input.seed.openrouterKey || !/^[!-~]+$/.test(input.seed.openrouterKey)) throw new Error("managed OpenRouter requires a non-empty workspace credential without whitespace");
38914
+ const models2 = openRouterModelIds(input.seed.openrouterModels ?? []);
38915
+ if (input.seed.openrouterDefault && !models2.length) throw new Error("a default OpenRouter model requires a model assignment");
38916
+ }
38842
38917
  if (input.registry.workspaces[input.slug]) throw new Error(`workspace "${input.slug}" already exists`);
38843
38918
  if (!input.seed.admins.length) throw new Error("a workspace needs at least one admin email (--admin)");
38844
38919
  for (const entry of [...input.seed.admins, ...input.seed.members]) assertEmailOrDomain(entry);
@@ -38854,15 +38929,36 @@ function createPlan(input) {
38854
38929
  createdAt: (input.now ?? /* @__PURE__ */ new Date()).toISOString()
38855
38930
  };
38856
38931
  const registry2 = { ...input.registry, nextPort: next, workspaces: { ...input.registry.workspaces, [workspace.slug]: workspace } };
38932
+ const reserved = (accountCreated) => ({
38933
+ ...registry2,
38934
+ workspaces: { ...registry2.workspaces, [workspace.slug]: { ...workspace, status: "provisioning", accountCreated } }
38935
+ });
38857
38936
  const user = fleetUser(workspace.slug);
38858
38937
  const home = workspaceHome(layout, workspace.slug);
38859
38938
  const dataDir = workspaceDataDir(layout, workspace.slug);
38860
38939
  const steps = [
38940
+ // Reserve the identity and ports before any external resource is created.
38941
+ // A crash leaves an inspectable reservation, never a silently reusable home.
38942
+ { kind: "write", path: layout.registryFile, content: `${JSON.stringify(reserved(false), null, 2)}
38943
+ `, mode: 384 },
38861
38944
  { kind: "run", argv: ["useradd", "--system", "--create-home", "--home-dir", home, "--shell", "/usr/sbin/nologin", "--user-group", user], why: `the workspace's own account` },
38945
+ { kind: "write", path: layout.registryFile, content: `${JSON.stringify(reserved(true), null, 2)}
38946
+ `, mode: 384 },
38862
38947
  { kind: "mkdir", path: dataDir, mode: 448, owner: user },
38863
38948
  { kind: "write", path: join3(dataDir, "config.json"), content: initialConfig(input.seed), mode: 384, owner: user },
38949
+ ...input.seed.openrouterKey ? [
38950
+ { kind: "mkdir", path: join3(home, ".config"), mode: 448, owner: user },
38951
+ { kind: "mkdir", path: join3(home, ".config", "opencode"), mode: 448, owner: user },
38952
+ { kind: "write", path: join3(home, ".config", "opencode", "opencode.json"), mode: 384, owner: user, content: `${JSON.stringify({ provider: { [MANAGED_OPENROUTER]: {
38953
+ npm: "@ai-sdk/openai-compatible",
38954
+ name: "Managed OpenRouter",
38955
+ options: { baseURL: input.seed.openrouterUrl, apiKey: input.seed.openrouterKey },
38956
+ models: managedOpenRouterModels(input.seed.openrouterModels ?? [])
38957
+ } } }, null, 2)}
38958
+ ` }
38959
+ ] : [],
38864
38960
  ...input.seed.brandJson ? [{ kind: "write", path: join3(dataDir, "brand.json"), content: input.seed.brandJson, mode: 384, owner: user }] : [],
38865
- { kind: "write", path: join3(layout.instancesDir, `${workspace.slug}.env`), content: instanceEnv({ workspace, dataDir, licenseKey: input.licenseKey }), mode: 384 },
38961
+ { kind: "write", path: join3(layout.instancesDir, `${workspace.slug}.env`), content: instanceEnv({ workspace, dataDir, licenseKey: input.licenseKey, portalUrl: input.seed.portalUrl }), mode: 384 },
38866
38962
  ...input.memoryMax ? [
38867
38963
  { kind: "mkdir", path: join3(layout.unitFile.replace(/openmausbot@\.service$/, ""), `openmausbot@${workspace.slug}.service.d`), mode: 493 },
38868
38964
  { kind: "write", path: join3(layout.unitFile.replace(/openmausbot@\.service$/, ""), `openmausbot@${workspace.slug}.service.d`, "limits.conf"), content: `[Service]
@@ -38882,10 +38978,14 @@ MemoryMax=${input.memoryMax}
38882
38978
  ];
38883
38979
  return { steps, registry: registry2, workspace };
38884
38980
  }
38981
+ function assertManagedWorkspace(workspace) {
38982
+ if (workspace.status !== "running" && workspace.status !== "suspended") throw new Error(`workspace "${workspace.slug}" is ${workspace.status} and requires operator recovery`);
38983
+ }
38885
38984
  function withStatus(registry2, slug, status) {
38886
38985
  assertSlug(slug);
38887
38986
  const current = registry2.workspaces[slug];
38888
38987
  if (!current) throw new Error(`no workspace "${slug}"`);
38988
+ assertManagedWorkspace(current);
38889
38989
  const workspace = { ...current, status };
38890
38990
  return { registry: { ...registry2, workspaces: { ...registry2.workspaces, [slug]: workspace } }, workspace };
38891
38991
  }
@@ -38923,8 +39023,9 @@ function deletePlan(input) {
38923
39023
  const layout = input.layout ?? fleetLayout();
38924
39024
  const workspace = input.registry.workspaces[input.slug];
38925
39025
  if (!workspace) throw new Error(`no workspace "${input.slug}"`);
39026
+ assertManagedWorkspace(workspace);
38926
39027
  const { [input.slug]: _gone, ...rest } = input.registry.workspaces;
38927
- const registry2 = { ...input.registry, workspaces: rest };
39028
+ const registry2 = { ...input.registry, workspaces: input.keepData ? { ...rest, [input.slug]: { ...workspace, status: "retained" } } : rest };
38928
39029
  const unitsDir = layout.unitFile.replace(/openmausbot@\.service$/, "");
38929
39030
  return {
38930
39031
  registry: registry2,
@@ -38935,8 +39036,10 @@ function deletePlan(input) {
38935
39036
  { kind: "remove", path: join3(layout.instancesDir, `${workspace.slug}.env`) },
38936
39037
  { kind: "remove", path: join3(unitsDir, `openmausbot@${workspace.slug}.service.d`, "limits.conf") },
38937
39038
  { kind: "write", path: layout.fenceFile, content: fenceRules(Object.values(registry2.workspaces)), mode: 384 },
38938
- { kind: "run", argv: ["nft", "-f", layout.fenceFile], why: "drop its fence rule" },
38939
- { kind: "run", argv: input.keepData ? ["userdel", fleetUser(workspace.slug)] : ["userdel", "--remove", fleetUser(workspace.slug)], why: input.keepData ? "remove the account, keep its home and data" : "remove the account and everything it owned" },
39039
+ { kind: "run", argv: ["nft", "-f", layout.fenceFile], why: input.keepData ? "keep the retained account's fence" : "drop its fence rule" },
39040
+ // Retain the nologin account too: recycling its UID would give a new
39041
+ // workspace ownership of the preserved home, even under a different slug.
39042
+ ...input.keepData ? [{ kind: "note", text: "keeping the disabled workspace's account, home and name reserved; operator recovery is required before reuse" }] : [{ kind: "run", argv: ["userdel", "--remove", fleetUser(workspace.slug)], why: "remove the account and everything it owned" }],
38940
39043
  { kind: "write", path: layout.registryFile, content: `${JSON.stringify(registry2, null, 2)}
38941
39044
  `, mode: 384 }
38942
39045
  ]
@@ -38963,10 +39066,12 @@ function applySignIn(configText, action, email3, chatOnly) {
38963
39066
  const members = without(list(current.members));
38964
39067
  if (action === "remove") {
38965
39068
  if (admins.length + members.length === list(current.admins).length + list(current.members).length) throw new Error(`${entry} is not on the list`);
39069
+ if (!admins.length) throw new Error("a workspace needs at least one admin; add another admin first");
38966
39070
  return { config: `${JSON.stringify({ ...raw, signIn: { admins, members } }, null, 2)}
38967
- `, summary: `${entry} can no longer sign in; existing sessions stay until they expire or are revoked` };
39071
+ `, summary: `${entry} was removed from the allow-list; existing email sessions are rechecked against the remaining entries` };
38968
39072
  }
38969
39073
  const next = chatOnly ? { admins, members: [...members, entry] } : { admins: [...admins, entry], members };
39074
+ if (!next.admins.length) throw new Error("a workspace needs at least one admin; add another admin first");
38970
39075
  return { config: `${JSON.stringify({ ...raw, signIn: next }, null, 2)}
38971
39076
  `, summary: `${entry} can sign in with an emailed code (${chatOnly ? "chat and approvals" : "full access"})` };
38972
39077
  }
@@ -38978,11 +39083,14 @@ function describeSteps(steps) {
38978
39083
  for (const step of steps) {
38979
39084
  switch (step.kind) {
38980
39085
  case "mkdir":
38981
- lines.push(`install -d -m ${step.mode.toString(8)}${step.owner ? ` -o ${step.owner} -g ${step.owner}` : ""} ${shellQuote(step.path)}`);
39086
+ lines.push(step.owner ? `# Create ${shellQuote(step.path)} mode ${step.mode.toString(8)} as ${step.owner} (use fleet --yes; never root chown on tenant paths)` : `install -d -m ${step.mode.toString(8)} ${shellQuote(step.path)}`);
38982
39087
  break;
38983
39088
  case "write":
39089
+ if (step.owner) {
39090
+ lines.push(`# Write ${shellQuote(step.path)} mode ${step.mode.toString(8)} as ${step.owner} (use fleet --yes; tenant content omitted)`);
39091
+ break;
39092
+ }
38984
39093
  lines.push(`cat > ${shellQuote(step.path)} <<'OMB_EOF'`, step.content.replace(/\n$/, ""), "OMB_EOF", `chmod ${step.mode.toString(8)} ${shellQuote(step.path)}`);
38985
- if (step.owner) lines.push(`chown ${step.owner}:${step.owner} ${shellQuote(step.path)}`);
38986
39094
  break;
38987
39095
  case "append-once":
38988
39096
  lines.push(`grep -qxF ${shellQuote(step.line)} ${shellQuote(step.path)} || printf '\\n%s\\n' ${shellQuote(step.line)} >> ${shellQuote(step.path)}`);
@@ -39005,6 +39113,113 @@ function describeSteps(steps) {
39005
39113
  }
39006
39114
 
39007
39115
  // server/fleet-cli.ts
39116
+ var TENANT_IO = String.raw`
39117
+ const fs = require("node:fs");
39118
+ const path = require("node:path");
39119
+ const { randomUUID } = require("node:crypto");
39120
+ const request = JSON.parse(fs.readFileSync(0, "utf8"));
39121
+ const { uid, gid, home, file, action, content, mode } = request;
39122
+ if (process.getuid() === 0) {
39123
+ process.setgroups([]);
39124
+ process.setgid(gid);
39125
+ process.setuid(uid);
39126
+ }
39127
+ if (process.getuid() !== uid || process.getgid() !== gid || uid === 0 || gid === 0) throw Error("could not drop fleet file privileges");
39128
+ const limit = 4 * 1024 * 1024;
39129
+ const check = (stat, directory) => {
39130
+ if (stat.uid !== uid || (directory ? !stat.isDirectory() : !stat.isFile() || stat.nlink !== 1)) throw Error("unsafe workspace file or directory");
39131
+ };
39132
+ const totals = { turns: 0, costUsd: null, billableUsd: null };
39133
+ let cursor = home;
39134
+ try {
39135
+ check(fs.lstatSync(cursor), true);
39136
+ for (const part of path.relative(home, path.dirname(file)).split(path.sep).filter(Boolean)) {
39137
+ cursor = path.join(cursor, part);
39138
+ check(fs.lstatSync(cursor), true);
39139
+ }
39140
+ } catch (error) {
39141
+ if (action !== "usage" || error.code !== "ENOENT") throw error;
39142
+ process.stdout.write(JSON.stringify(totals));
39143
+ process.exit(0);
39144
+ }
39145
+ let result = null;
39146
+ if (action === "mkdir") {
39147
+ try { fs.mkdirSync(file, { mode }); } catch (error) { if (error.code !== "EEXIST") throw error; }
39148
+ check(fs.lstatSync(file), true);
39149
+ } else if (action === "read" || action === "usage") {
39150
+ let fd;
39151
+ try { fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK); }
39152
+ catch (error) { if (error.code !== "ENOENT") throw error; }
39153
+ if (fd !== undefined) {
39154
+ try {
39155
+ const stat = fs.fstatSync(fd);
39156
+ check(stat, false);
39157
+ if (stat.size > limit) throw Error("workspace file exceeds 4 MiB");
39158
+ const data = Buffer.alloc(limit + 1);
39159
+ let size = 0;
39160
+ while (size < data.length) { const read = fs.readSync(fd, data, size, data.length - size, null); if (!read) break; size += read; }
39161
+ if (size > limit) throw Error("workspace file exceeds 4 MiB");
39162
+ result = data.subarray(0, size).toString("utf8");
39163
+ } finally { fs.closeSync(fd); }
39164
+ }
39165
+ if (action === "usage") {
39166
+ const to = Date.parse(content);
39167
+ const date = new Date(to);
39168
+ const from = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1);
39169
+ // Scan one bounded month's text without retaining rows or allocating groups.
39170
+ const text = result || "";
39171
+ for (let offset = 0; offset < text.length;) {
39172
+ const end = text.indexOf("\n", offset);
39173
+ const line = text.slice(offset, end < 0 ? text.length : end);
39174
+ offset = end < 0 ? text.length : end + 1;
39175
+ let row;
39176
+ try { row = JSON.parse(line); } catch { continue; }
39177
+ if (!row || typeof row !== "object" || typeof row.at !== "string" || typeof row.botId !== "string" || typeof row.model !== "string" || typeof row.input !== "number" || typeof row.output !== "number" || !row.trigger || typeof row.trigger !== "object" || typeof row.trigger.kind !== "string") continue;
39178
+ const at = Date.parse(row.at);
39179
+ if (!(at >= from && at <= to)) continue;
39180
+ totals.turns++;
39181
+ if (typeof row.costUsd === "number" && Number.isFinite(row.costUsd) && row.costUsd >= 0) {
39182
+ const sum = (totals.costUsd || 0) + row.costUsd;
39183
+ if (!Number.isFinite(sum)) throw Error("workspace usage total exceeds numeric range");
39184
+ totals.costUsd = sum;
39185
+ }
39186
+ }
39187
+ result = totals;
39188
+ }
39189
+ } else if (action === "write") {
39190
+ if (Buffer.byteLength(content) > limit) throw Error("workspace file exceeds 4 MiB");
39191
+ try { check(fs.lstatSync(file), false); } catch (error) { if (error.code !== "ENOENT") throw error; }
39192
+ const temp = file + ".fleet-" + randomUUID();
39193
+ let fd;
39194
+ try {
39195
+ fd = fs.openSync(temp, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_NOFOLLOW, mode);
39196
+ fs.writeFileSync(fd, content);
39197
+ fs.fsyncSync(fd);
39198
+ fs.closeSync(fd); fd = undefined;
39199
+ fs.renameSync(temp, file);
39200
+ } finally {
39201
+ if (fd !== undefined) fs.closeSync(fd);
39202
+ try { fs.unlinkSync(temp); } catch (error) { if (error.code !== "ENOENT") throw error; }
39203
+ }
39204
+ } else throw Error("unknown fleet file operation");
39205
+ process.stdout.write(JSON.stringify(result));
39206
+ `;
39207
+ function tenantIo(owner, file2, action, content, mode) {
39208
+ assertUnixUser(owner);
39209
+ const options = { encoding: "utf8", timeout: 5e3, killSignal: "SIGKILL", cwd: "/", env: { PATH: "/usr/bin:/bin" }, stdio: ["pipe", "pipe", "pipe"] };
39210
+ const account = execFileSync("/usr/bin/getent", ["passwd", owner], { ...options, maxBuffer: 8192 }).trim().split(":");
39211
+ const uid = Number(account[2]);
39212
+ const gid = Number(account[3]);
39213
+ const home = account[5];
39214
+ if (account.length !== 7 || account[0] !== owner || !Number.isSafeInteger(uid) || uid <= 0 || !Number.isSafeInteger(gid) || gid <= 0 || !home?.startsWith("/") || posix3.normalize(home) !== home || !file2.startsWith(`${home}/`) || posix3.normalize(file2) !== file2) {
39215
+ throw new Error(`unsafe filesystem identity or path for ${owner}`);
39216
+ }
39217
+ try {
39218
+ return JSON.parse(execFileSync(process.execPath, ["--eval", TENANT_IO], { ...options, maxBuffer: action === "usage" ? 8192 : 32 * 1024 * 1024, input: JSON.stringify({ uid, gid, home, file: file2, action, content, mode }) }));
39219
+ } catch {
39220
+ throw new Error(`could not ${action} workspace file as ${owner}: check ownership, links, file size and permissions`);
39221
+ }
39222
+ }
39008
39223
  function defaultFleetDeps() {
39009
39224
  return {
39010
39225
  isRoot: () => typeof process.getuid === "function" && process.getuid() === 0,
@@ -39024,9 +39239,37 @@ function defaultFleetDeps() {
39024
39239
  resolve13({ code: null, output: error51 instanceof Error ? error51.message : String(error51) });
39025
39240
  }
39026
39241
  }),
39027
- readText: (path3) => existsSync2(path3) ? readFileSync(path3, "utf8") : null,
39028
- writeText: (path3, content, mode) => writeFileSync4(path3, content, { mode }),
39029
- mkdir: (path3, mode) => mkdirSync3(path3, { recursive: true, mode }),
39242
+ pathExists: (path3) => {
39243
+ try {
39244
+ lstatSync(path3);
39245
+ return true;
39246
+ } catch (error51) {
39247
+ if (error51.code === "ENOENT") return false;
39248
+ throw error51;
39249
+ }
39250
+ },
39251
+ readText: (path3, owner) => owner ? tenantIo(owner, path3, "read") : existsSync2(path3) ? readFileSync(path3, "utf8") : null,
39252
+ usage: (dataDir, owner, now) => {
39253
+ const at = now.toISOString();
39254
+ return tenantIo(owner, posix3.join(dataDir, "usage", `${at.slice(0, 7)}.jsonl`), "usage", at);
39255
+ },
39256
+ writeText: (path3, content, mode, owner) => {
39257
+ if (owner) {
39258
+ tenantIo(owner, path3, "write", content, mode);
39259
+ return;
39260
+ }
39261
+ writeFileAtomic(path3, content, { mode });
39262
+ const directory = openSync2(posix3.dirname(path3), "r");
39263
+ try {
39264
+ fsyncSync2(directory);
39265
+ } finally {
39266
+ closeSync2(directory);
39267
+ }
39268
+ },
39269
+ mkdir: (path3, mode, owner) => {
39270
+ if (owner) tenantIo(owner, path3, "mkdir", void 0, mode);
39271
+ else mkdirSync3(path3, { recursive: true, mode });
39272
+ },
39030
39273
  appendOnce: (path3, line) => {
39031
39274
  const current = existsSync2(path3) ? readFileSync(path3, "utf8") : "";
39032
39275
  if (!current.split("\n").some((existing) => existing.trim() === line)) appendFileSync(path3, `${current.endsWith("\n") || !current ? "" : "\n"}
@@ -39053,18 +39296,10 @@ async function executePlan(steps, deps, io) {
39053
39296
  for (const step of steps) {
39054
39297
  switch (step.kind) {
39055
39298
  case "mkdir":
39056
- deps.mkdir(step.path, step.mode);
39057
- if (step.owner) {
39058
- const chown = await deps.run(["chown", `${step.owner}:${step.owner}`, step.path]);
39059
- if (chown.code !== 0) return failed(io, `chown ${step.path}`, chown.output);
39060
- }
39299
+ deps.mkdir(step.path, step.mode, step.owner);
39061
39300
  break;
39062
39301
  case "write":
39063
- deps.writeText(step.path, step.content, step.mode);
39064
- if (step.owner) {
39065
- const chown = await deps.run(["chown", `${step.owner}:${step.owner}`, step.path]);
39066
- if (chown.code !== 0) return failed(io, `chown ${step.path}`, chown.output);
39067
- }
39302
+ deps.writeText(step.path, step.content, step.mode, step.owner);
39068
39303
  break;
39069
39304
  case "append-once":
39070
39305
  deps.appendOnce(step.path, step.line);
@@ -39107,19 +39342,28 @@ function planFleetAction(input, deps) {
39107
39342
  if (warning) throw new Error(warning);
39108
39343
  const existing = deps.readText(layout.registryFile);
39109
39344
  if (existing !== null && !input.yes) throw new Error(`${layout.registryFile} exists; pass --yes to rewrite the template unit and fence (workspaces are kept)`);
39110
- const plan = initPlan({ domain: input.domain, node: input.node, script: input.script, operator: input.operator, layout });
39111
- if (existing) {
39112
- const kept = parseRegistry(existing);
39113
- plan.registry.workspaces = kept.workspaces;
39114
- plan.registry.nextPort = kept.nextPort;
39115
- return plan.steps.map((step) => step.kind === "write" && step.path === layout.registryFile ? { ...step, content: `${JSON.stringify(plan.registry, null, 2)}
39116
- ` } : step);
39345
+ const kept = existing !== null ? parseRegistry(existing) : void 0;
39346
+ const plan = initPlan({ domain: input.domain, node: input.node, script: input.script, operator: input.operator ?? kept?.operator, layout });
39347
+ if (kept) {
39348
+ const registry2 = { ...kept, ...plan.registry, workspaces: kept.workspaces, nextPort: kept.nextPort };
39349
+ return plan.steps.flatMap((step) => {
39350
+ if (step.kind === "write" && step.path === layout.registryFile) return [{ ...step, content: `${JSON.stringify(registry2, null, 2)}
39351
+ ` }];
39352
+ if (step.kind === "write" && step.path === layout.fenceFile) return [{ ...step, content: fenceRules(Object.values(registry2.workspaces)) }];
39353
+ if (step.kind === "run" && step.argv.join(" ") === "systemctl enable --now openmausbot-fence.service") return [step, { kind: "run", argv: ["nft", "-f", layout.fenceFile], why: "reapply the preserved workspace fence" }];
39354
+ return [step];
39355
+ });
39117
39356
  }
39118
39357
  return plan.steps;
39119
39358
  }
39120
39359
  case "create": {
39121
39360
  if (!input.slug) throw new Error("fleet create needs a workspace name");
39361
+ assertSlug(input.slug);
39122
39362
  const registry2 = loadRegistry(layout, deps);
39363
+ if (registry2.workspaces[input.slug]) throw new Error(`workspace "${input.slug}" already exists`);
39364
+ for (const path3 of [workspaceHome(layout, input.slug), posix3.join(layout.instancesDir, `${input.slug}.env`), posix3.join(layout.caddyDir, `${input.slug}.caddy`), posix3.join(posix3.dirname(layout.unitFile), `openmausbot@${input.slug}.service.d`)]) {
39365
+ if (deps.pathExists(path3)) throw new Error(`workspace path already exists: ${path3}; recover or remove it explicitly before provisioning a fresh workspace`);
39366
+ }
39123
39367
  const brandJson = input.brandJson ?? (input.brandFile ? deps.readText(input.brandFile) : void 0);
39124
39368
  if (input.brandFile && brandJson === null) throw new Error(`${input.brandFile} not found`);
39125
39369
  if (brandJson) JSON.parse(brandJson);
@@ -39128,19 +39372,60 @@ function planFleetAction(input, deps) {
39128
39372
  return createPlan({
39129
39373
  registry: registry2,
39130
39374
  slug: input.slug,
39131
- seed: { admins: input.admins, members: input.members, ...anthropicKey ? { anthropicKey } : {}, ...input.cap !== void 0 ? { monthlyCapUsd: input.cap } : {}, ...brandJson ? { brandJson } : {} },
39375
+ seed: { admins: input.admins, members: input.members, ...anthropicKey ? { anthropicKey } : {}, ...input.anthropicUrl ? { anthropicUrl: input.anthropicUrl } : {}, ...input.portalUrl ? { portalUrl: input.portalUrl } : {}, ...input.cap !== void 0 ? { monthlyCapUsd: input.cap } : {}, ...brandJson ? { brandJson } : {}, openrouterKey: input.openrouterKey, openrouterUrl: input.openrouterUrl, openrouterModels: input.openrouterModels, openrouterDefault: input.openrouterDefault },
39132
39376
  ...input.licenseKey ? { licenseKey: input.licenseKey } : {},
39133
39377
  ...input.memory ? { memoryMax: input.memory } : {},
39134
39378
  layout
39135
39379
  }).steps;
39136
39380
  }
39381
+ case "providers": {
39382
+ if (!input.slug) throw new Error("fleet providers needs a workspace name");
39383
+ assertSlug(input.slug);
39384
+ const workspace = loadRegistry(layout, deps).workspaces[input.slug];
39385
+ if (!workspace) throw new Error(`no workspace "${input.slug}"`);
39386
+ assertManagedWorkspace(workspace);
39387
+ const models2 = managedOpenRouterModels(input.openrouterModels);
39388
+ const env = deps.readText(posix3.join(layout.instancesDir, `${input.slug}.env`)) ?? "";
39389
+ const portal = /^OMB_ADMIN_URL=(.+)$/m.exec(env)?.[1];
39390
+ if (!portal || !env.split("\n").includes(`OMB_ADMIN_WORKSPACE=${input.slug}`)) throw new Error("workspace has no trusted portal configuration; operator recovery is required");
39391
+ try {
39392
+ const url2 = new URL(portal);
39393
+ if (url2.protocol !== "https:" || url2.origin !== portal) throw new Error();
39394
+ } catch {
39395
+ throw new Error("workspace portal configuration is invalid; operator recovery is required");
39396
+ }
39397
+ const file2 = posix3.join(workspaceHome(layout, input.slug), ".config", "opencode", "opencode.json");
39398
+ const raw = deps.readText(file2, fleetUser(input.slug));
39399
+ const object2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
39400
+ let config2;
39401
+ try {
39402
+ const parsed = JSON.parse(raw ?? "null");
39403
+ if (!object2(parsed)) throw new Error();
39404
+ config2 = parsed;
39405
+ } catch {
39406
+ throw new Error("managed OpenRouter configuration is missing or invalid; operator recovery is required");
39407
+ }
39408
+ const providers = config2.provider;
39409
+ const managed = object2(providers) ? providers[MANAGED_OPENROUTER] : void 0;
39410
+ const options = object2(managed) ? managed.options : void 0;
39411
+ if (!object2(managed) || managed.npm !== "@ai-sdk/openai-compatible" || !object2(options) || options.baseURL !== `${portal}/api/gateway/${input.slug}/openrouter/v1` || typeof options.apiKey !== "string" || !/^[!-~]+$/.test(options.apiKey)) {
39412
+ throw new Error("managed OpenRouter configuration does not match the workspace portal; operator recovery is required");
39413
+ }
39414
+ managed.models = models2;
39415
+ return [
39416
+ { kind: "write", path: file2, content: `${JSON.stringify(config2, null, 2)}
39417
+ `, mode: 384, owner: fleetUser(input.slug) },
39418
+ { kind: "note", text: "managed OpenRouter models updated; refresh the workspace model picker to load its catalog" }
39419
+ ];
39420
+ }
39137
39421
  case "users": {
39138
39422
  if (!input.slug || !input.userAction || !input.email) throw new Error("fleet users needs: NAME add|remove EMAIL [--chat-only]");
39139
39423
  assertSlug(input.slug);
39140
39424
  const registry2 = loadRegistry(layout, deps);
39141
39425
  if (!registry2.workspaces[input.slug]) throw new Error(`no workspace "${input.slug}"`);
39426
+ assertManagedWorkspace(registry2.workspaces[input.slug]);
39142
39427
  const file2 = posix3.join(workspaceDataDir(layout, input.slug), "config.json");
39143
- const next = applySignIn(deps.readText(file2) ?? "{}", input.userAction, input.email, Boolean(input.chatOnly));
39428
+ const next = applySignIn(deps.readText(file2, fleetUser(input.slug)) ?? "{}", input.userAction, input.email, Boolean(input.chatOnly));
39144
39429
  return [
39145
39430
  { kind: "write", path: file2, content: next.config, mode: 384, owner: fleetUser(input.slug) },
39146
39431
  { kind: "note", text: next.summary }
@@ -39154,7 +39439,7 @@ function planFleetAction(input, deps) {
39154
39439
  }
39155
39440
  case "delete": {
39156
39441
  if (!input.slug) throw new Error("fleet delete needs a workspace name");
39157
- if (!input.yes) throw new Error(`fleet delete removes the workspace's account${input.keepData ? "" : " and all of its data"}; pass --yes to confirm${input.keepData ? "" : ", or --keep-data to keep the home folder"}`);
39442
+ if (!input.yes) throw new Error(`fleet delete ${input.keepData ? "retires the workspace, keeping its data and reserved Unix account" : "removes the workspace's account and all of its data"}; pass --yes to confirm${input.keepData ? "" : ", or --keep-data to keep the home folder and account reserved"}`);
39158
39443
  const registry2 = loadRegistry(layout, deps);
39159
39444
  return deletePlan({ registry: registry2, slug: input.slug, keepData: input.keepData, layout }).steps;
39160
39445
  }
@@ -39164,8 +39449,18 @@ function planFleetAction(input, deps) {
39164
39449
  return [];
39165
39450
  }
39166
39451
  }
39452
+ function markProvisioningFailed(input, deps) {
39453
+ if (input.action !== "create" || !input.slug) return;
39454
+ const layout = fleetLayout(input.root ?? "/");
39455
+ const registry2 = loadRegistry(layout, deps);
39456
+ const workspace = registry2.workspaces[input.slug];
39457
+ if (workspace?.status !== "provisioning") return;
39458
+ registry2.workspaces[input.slug] = { ...workspace, status: "error" };
39459
+ deps.writeText(layout.registryFile, `${JSON.stringify(registry2, null, 2)}
39460
+ `, 384);
39461
+ }
39167
39462
  function printPlan(steps, io, reason) {
39168
- io.log(`${reason}; run these as root:`);
39463
+ io.log(`${reason}; inspect this plan, then rerun the fleet command as root without --dry-run (with --yes where required):`);
39169
39464
  io.log("");
39170
39465
  for (const line of describeSteps(steps)) io.log(line);
39171
39466
  return 0;
@@ -39182,13 +39477,21 @@ async function runFleetCommand(input, io, deps = defaultFleetDeps()) {
39182
39477
  return 0;
39183
39478
  }
39184
39479
  for (const workspace of rows) {
39185
- const live = asRoot ? (await deps.run(["systemctl", "is-active", `openmausbot@${workspace.slug}.service`])).output.trim() : workspace.status;
39480
+ const live = asRoot && (workspace.status === "running" || workspace.status === "suspended") ? (await deps.run(["systemctl", "is-active", `openmausbot@${workspace.slug}.service`])).output.trim() : workspace.status;
39186
39481
  io.log(`${workspace.slug.padEnd(24)} https://${workspace.host.padEnd(40)} :${String(workspace.port).padEnd(6)} ${live}`);
39187
39482
  }
39188
39483
  return 0;
39189
39484
  }
39190
39485
  const steps = planFleetAction(input, deps);
39191
- return asRoot ? await executePlan(steps, deps, io) : printPlan(steps, io, input.dryRun ? "dry run" : "not running as root");
39486
+ if (!asRoot) return printPlan(steps, io, input.dryRun ? "dry run" : "not running as root");
39487
+ try {
39488
+ const code = await executePlan(steps, deps, io);
39489
+ if (code !== 0) markProvisioningFailed(input, deps);
39490
+ return code;
39491
+ } catch (error51) {
39492
+ markProvisioningFailed(input, deps);
39493
+ throw error51;
39494
+ }
39192
39495
  } catch (error51) {
39193
39496
  io.error(`fleet: ${error51 instanceof Error ? error51.message : String(error51)}`);
39194
39497
  return 2;
@@ -39199,147 +39502,6 @@ async function runFleetCommand(input, io, deps = defaultFleetDeps()) {
39199
39502
  import { appendFileSync as appendFileSync2, chmodSync as chmodSync2, existsSync as existsSync3, mkdirSync as mkdirSync4, rmSync as rmSync3 } from "node:fs";
39200
39503
  import { createServer } from "node:http";
39201
39504
  import { dirname as dirname2 } from "node:path";
39202
-
39203
- // server/usage-ledger.ts
39204
- import { readFileSync as readFileSync2 } from "node:fs";
39205
- import { join as join4 } from "node:path";
39206
-
39207
- // server/prices.ts
39208
- function priceFor(turn, prices) {
39209
- return prices[`${turn.driverKind}/${turn.model}`] ?? prices[turn.model] ?? prices.default ?? null;
39210
- }
39211
- var clean = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
39212
- function billableFor(turn, prices) {
39213
- const price = priceFor(turn, prices);
39214
- if (!price) return null;
39215
- const input = clean(turn.input);
39216
- const cached3 = Math.min(clean(turn.cachedInput), input);
39217
- const fresh = input - cached3;
39218
- const cachedRate = price.cachedInputPerMillion ?? price.inputPerMillion;
39219
- return (fresh * price.inputPerMillion + cached3 * cachedRate + clean(turn.output) * price.outputPerMillion) / 1e6;
39220
- }
39221
-
39222
- // server/usage-ledger.ts
39223
- var DIR = "usage";
39224
- var DAY_MS = 24 * 60 * 6e4;
39225
- var clean2 = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
39226
- var finiteOrNull = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
39227
- function monthKey(at) {
39228
- return `${at.getUTCFullYear()}-${String(at.getUTCMonth() + 1).padStart(2, "0")}`;
39229
- }
39230
- var isTrigger = (value) => typeof value === "object" && value !== null && typeof value.kind === "string";
39231
- var isUsageRow = (value) => typeof value === "object" && value !== null && typeof value.at === "string" && typeof value.botId === "string" && typeof value.model === "string" && typeof value.input === "number" && typeof value.output === "number" && isTrigger(value.trigger);
39232
- function monthsBetween(from, to) {
39233
- const keys = [];
39234
- const cursor3 = new Date(Date.UTC(from.getUTCFullYear(), from.getUTCMonth(), 1));
39235
- while (cursor3.getTime() <= to.getTime()) {
39236
- keys.push(monthKey(cursor3));
39237
- cursor3.setUTCMonth(cursor3.getUTCMonth() + 1);
39238
- }
39239
- return keys;
39240
- }
39241
- function readUsage(dataDir, range) {
39242
- const rows = [];
39243
- const from = range.from.getTime();
39244
- const to = range.to.getTime();
39245
- for (const key of monthsBetween(range.from, range.to)) {
39246
- let text4;
39247
- try {
39248
- text4 = readFileSync2(join4(dataDir, DIR, `${key}.jsonl`), "utf8");
39249
- } catch {
39250
- continue;
39251
- }
39252
- for (const line of text4.split("\n")) {
39253
- if (!line) continue;
39254
- try {
39255
- const value = JSON.parse(line);
39256
- if (!isUsageRow(value)) continue;
39257
- const at = Date.parse(value.at);
39258
- if (at >= from && at <= to) rows.push(value);
39259
- } catch {
39260
- }
39261
- }
39262
- }
39263
- return rows;
39264
- }
39265
- function triggerKey(trigger) {
39266
- switch (trigger.kind) {
39267
- case "user":
39268
- return `user:${(trigger.email ?? trigger.label ?? "").toLowerCase() || "unknown"}`;
39269
- case "owner":
39270
- return "owner";
39271
- case "routine":
39272
- return `routine:${trigger.routineId ?? trigger.label ?? "unknown"}`;
39273
- default:
39274
- return "bot";
39275
- }
39276
- }
39277
- function triggerLabel(trigger) {
39278
- switch (trigger.kind) {
39279
- case "user":
39280
- return trigger.email ?? trigger.label ?? "Signed-in user";
39281
- case "owner":
39282
- return "This computer";
39283
- case "routine":
39284
- return `Routine: ${trigger.label ?? trigger.routineId ?? "unknown"}`;
39285
- default:
39286
- return "Bot to bot";
39287
- }
39288
- }
39289
- function groupOf(row, groupBy) {
39290
- switch (groupBy) {
39291
- case "bot":
39292
- return { key: `bot:${row.botId}`, label: row.botName || row.botId };
39293
- case "model":
39294
- return { key: `model:${row.driverKind}/${row.model}`, label: row.model };
39295
- case "user":
39296
- return { key: triggerKey(row.trigger), label: triggerLabel(row.trigger) };
39297
- case "day":
39298
- return { key: `day:${row.at.slice(0, 10)}`, label: row.at.slice(0, 10) };
39299
- default:
39300
- return { key: `engine:${row.driverKind}`, label: row.driverKind };
39301
- }
39302
- }
39303
- function emptyGroup(key, label) {
39304
- return { key, label, turns: 0, input: 0, output: 0, cachedInput: 0, costUsd: null, unpriced: 0, billableUsd: null };
39305
- }
39306
- function add(group, row, prices) {
39307
- group.turns += 1;
39308
- group.input += clean2(row.input);
39309
- group.output += clean2(row.output);
39310
- group.cachedInput += clean2(row.cachedInput);
39311
- const cost = finiteOrNull(row.costUsd);
39312
- if (cost === null) group.unpriced += 1;
39313
- else group.costUsd = (group.costUsd ?? 0) + cost;
39314
- const billable = prices ? billableFor(row, prices) : null;
39315
- if (billable !== null) group.billableUsd = (group.billableUsd ?? 0) + billable;
39316
- }
39317
- function summarizeUsage(rows, groupBy, prices = null) {
39318
- const groups = /* @__PURE__ */ new Map();
39319
- const total = emptyGroup("total", "total");
39320
- for (const row of rows) {
39321
- const { key, label } = groupOf(row, groupBy);
39322
- let group = groups.get(key);
39323
- if (!group) {
39324
- group = emptyGroup(key, label);
39325
- groups.set(key, group);
39326
- }
39327
- add(group, row, prices);
39328
- add(total, row, prices);
39329
- }
39330
- const ordered = [...groups.values()];
39331
- if (groupBy === "day") ordered.sort((a2, b) => a2.key.localeCompare(b.key));
39332
- else {
39333
- ordered.sort((a2, b) => {
39334
- const costA = a2.costUsd ?? Number.NEGATIVE_INFINITY;
39335
- const costB = b.costUsd ?? Number.NEGATIVE_INFINITY;
39336
- return costB - costA || b.input + b.output - (a2.input + a2.output);
39337
- });
39338
- }
39339
- return { groups: ordered, total };
39340
- }
39341
-
39342
- // server/fleet-agent.ts
39343
39505
  var MAX_BODY = 256 * 1024;
39344
39506
  function readBody(req) {
39345
39507
  return new Promise((resolve13, reject) => {
@@ -39363,10 +39525,20 @@ function readBody(req) {
39363
39525
  var strings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean) : [];
39364
39526
  var text = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
39365
39527
  var number = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
39366
- function workspaceUsage(layout, slug, now) {
39367
- const from = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
39368
- const total = summarizeUsage(readUsage(workspaceDataDir(layout, slug), { from, to: now }), "bot").total;
39369
- return { month: now.toISOString().slice(0, 7), turns: total.turns, costUsd: total.costUsd, billableUsd: total.billableUsd };
39528
+ var models = (value) => {
39529
+ try {
39530
+ return openRouterModelIds(value);
39531
+ } catch (error51) {
39532
+ throw Object.assign(error51, { status: 400 });
39533
+ }
39534
+ };
39535
+ function workspaceUsage(layout, slug, now, deps) {
39536
+ const month = now.toISOString().slice(0, 7);
39537
+ try {
39538
+ return { month, ...deps.usage(workspaceDataDir(layout, slug), fleetUser(slug), now) };
39539
+ } catch {
39540
+ return { month, turns: null, costUsd: null, billableUsd: null, unavailable: true };
39541
+ }
39370
39542
  }
39371
39543
  function createFleetAgent(options) {
39372
39544
  const deps = options.deps ?? defaultFleetDeps();
@@ -39393,7 +39565,7 @@ function createFleetAgent(options) {
39393
39565
  } catch {
39394
39566
  }
39395
39567
  };
39396
- const act = async (input) => {
39568
+ const runAction = async (input) => {
39397
39569
  const log = [];
39398
39570
  const errors = [];
39399
39571
  const io = { log: (line) => log.push(line), error: (line) => errors.push(line) };
@@ -39404,10 +39576,29 @@ function createFleetAgent(options) {
39404
39576
  audit({ action: input.action, slug: input.slug, ok: false, error: error51 instanceof Error ? error51.message : String(error51) });
39405
39577
  return { status: 400, body: { error: error51 instanceof Error ? error51.message : String(error51) } };
39406
39578
  }
39407
- const code = await executePlan(steps, deps, io);
39579
+ let code = 1;
39580
+ try {
39581
+ code = await executePlan(steps, deps, io);
39582
+ } catch (error51) {
39583
+ errors.push(error51 instanceof Error ? error51.message : String(error51));
39584
+ }
39585
+ if (code !== 0) {
39586
+ try {
39587
+ markProvisioningFailed(input, deps);
39588
+ } catch {
39589
+ errors.push("could not record the failed provisioning; inspect the reserved workspace before recovery");
39590
+ }
39591
+ }
39408
39592
  audit({ action: input.action, slug: input.slug, ok: code === 0, ...input.email ? { email: input.email } : {} });
39409
39593
  return code === 0 ? { status: 200, body: { ok: true, log } } : { status: 500, body: { error: errors[0] ?? "the step failed", log: [...log, ...errors] } };
39410
39594
  };
39595
+ let mutationTail = Promise.resolve();
39596
+ const act = (input) => {
39597
+ const result = mutationTail.then(() => runAction(input));
39598
+ mutationTail = result.catch(() => {
39599
+ });
39600
+ return result;
39601
+ };
39411
39602
  const handle = async (req, res) => {
39412
39603
  const url2 = new URL(req.url ?? "/", "http://fleet");
39413
39604
  const method = req.method ?? "GET";
@@ -39419,10 +39610,11 @@ function createFleetAgent(options) {
39419
39610
  if (method === "GET" && url2.pathname === "/health") return send(200, { ok: true });
39420
39611
  if (method === "GET" && url2.pathname === "/workspaces") {
39421
39612
  const registry2 = loadRegistry(layout, deps);
39613
+ const statusOnly = url2.searchParams.get("statusOnly") === "true";
39422
39614
  const workspaces = [];
39423
39615
  for (const workspace of Object.values(registry2.workspaces).sort((a2, b) => a2.slug.localeCompare(b.slug))) {
39424
- const live = (await deps.run(["systemctl", "is-active", `openmausbot@${workspace.slug}.service`])).output.trim() || "unknown";
39425
- workspaces.push({ ...workspace, live, usage: workspaceUsage(layout, workspace.slug, now()) });
39616
+ const live = workspace.status === "running" || workspace.status === "suspended" ? (await deps.run(["systemctl", "is-active", `openmausbot@${workspace.slug}.service`])).output.trim() || "unknown" : workspace.status;
39617
+ workspaces.push(statusOnly ? { slug: workspace.slug, live } : { ...workspace, live, usage: workspaceUsage(layout, workspace.slug, now(), deps) });
39426
39618
  }
39427
39619
  return send(200, { domain: registry2.domain, operator: registry2.operator ?? null, workspaces });
39428
39620
  }
@@ -39437,7 +39629,13 @@ function createFleetAgent(options) {
39437
39629
  cap: number(body.cap),
39438
39630
  memory: text(body.memory),
39439
39631
  brandJson: text(body.brandJson),
39440
- anthropicKey: text(body.anthropicKey)
39632
+ anthropicKey: text(body.anthropicKey),
39633
+ portalUrl: text(body.portalUrl),
39634
+ anthropicUrl: text(body.anthropicUrl),
39635
+ openrouterKey: text(body.openrouterKey),
39636
+ openrouterUrl: text(body.openrouterUrl),
39637
+ openrouterModels: body.openrouterModels === void 0 ? void 0 : models(body.openrouterModels),
39638
+ openrouterDefault: body.openrouterDefault === true
39441
39639
  });
39442
39640
  return send(result.status, result.body);
39443
39641
  }
@@ -39445,11 +39643,16 @@ function createFleetAgent(options) {
39445
39643
  const result = await act({ ...base2(), action: "upgrade" });
39446
39644
  return send(result.status, result.body);
39447
39645
  }
39448
- const match = /^\/workspaces\/([a-z0-9-]+)(?:\/(users|suspend|resume))?$/.exec(url2.pathname);
39646
+ const match = /^\/workspaces\/([a-z0-9-]+)(?:\/(users|providers|suspend|resume))?$/.exec(url2.pathname);
39449
39647
  if (match) {
39450
39648
  const slug = match[1];
39451
39649
  assertSlug(slug);
39452
39650
  const sub = match[2];
39651
+ if (method === "POST" && sub === "providers") {
39652
+ const body = await readBody(req);
39653
+ const result = await act({ ...base2(), action: "providers", slug, openrouterModels: models(body.models) });
39654
+ return send(result.status, result.body);
39655
+ }
39453
39656
  if (method === "POST" && sub === "users") {
39454
39657
  const body = await readBody(req);
39455
39658
  const action = body.action === "remove" ? "remove" : body.action === "add" ? "add" : void 0;
@@ -39500,11 +39703,11 @@ import { execFile } from "node:child_process";
39500
39703
 
39501
39704
  // companion/src/listener.ts
39502
39705
  import { homedir as homedir3, networkInterfaces } from "node:os";
39503
- import { delimiter as delimiter2, join as join5 } from "node:path";
39706
+ import { delimiter as delimiter2, join as join4 } from "node:path";
39504
39707
  function tailscaleCandidates(home = homedir3()) {
39505
39708
  return [
39506
39709
  "/Applications/Tailscale.app/Contents/MacOS/Tailscale",
39507
- join5(home, "Applications", "Tailscale.app", "Contents", "MacOS", "Tailscale"),
39710
+ join4(home, "Applications", "Tailscale.app", "Contents", "MacOS", "Tailscale"),
39508
39711
  "/opt/homebrew/bin/tailscale",
39509
39712
  "/usr/local/bin/tailscale",
39510
39713
  "/usr/bin/tailscale",
@@ -39722,9 +39925,9 @@ function phonePairingInstructions(phone, input) {
39722
39925
  // server/tunnel.ts
39723
39926
  import { spawn as spawn4 } from "node:child_process";
39724
39927
  import { randomUUID as randomUUID4 } from "node:crypto";
39725
- import { chmodSync as chmodSync3, existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync3, statSync } from "node:fs";
39928
+ import { chmodSync as chmodSync3, existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync2, statSync } from "node:fs";
39726
39929
  import { hostname } from "node:os";
39727
- import { dirname as dirname3, join as join6, resolve as resolve2 } from "node:path";
39930
+ import { dirname as dirname3, join as join5, resolve as resolve2 } from "node:path";
39728
39931
  import { fileURLToPath } from "node:url";
39729
39932
 
39730
39933
  // electron/secure-credential-state.mjs
@@ -39807,13 +40010,13 @@ function plainDocument(value) {
39807
40010
  return document;
39808
40011
  }
39809
40012
  function openTunnelCredentials(dataDir) {
39810
- const file2 = join6(dataDir, TUNNEL_CREDENTIALS_FILE);
40013
+ const file2 = join5(dataDir, TUNNEL_CREDENTIALS_FILE);
39811
40014
  let initial = {};
39812
40015
  let status = "empty";
39813
40016
  if (existsSync4(file2)) {
39814
40017
  try {
39815
40018
  if (process.platform !== "win32" && (statSync(file2).mode & 63) !== 0) chmodSync3(file2, 384);
39816
- const parsed = plainDocument(JSON.parse(readFileSync3(file2, "utf8")));
40019
+ const parsed = plainDocument(JSON.parse(readFileSync2(file2, "utf8")));
39817
40020
  if (!parsed) throw new Error("not a JSON object");
39818
40021
  initial = parsed;
39819
40022
  status = "ok";
@@ -39889,11 +40092,11 @@ function cloudflaredPath(dataDir, env = process.env) {
39889
40092
  return resolveCloudflaredBinary({ isPackaged: false, appPath: dataDir, environment: env });
39890
40093
  }
39891
40094
  function prepareEntry(here = HERE) {
39892
- const bundled = join6(here, "prepare-cloudflared.js");
40095
+ const bundled = join5(here, "prepare-cloudflared.js");
39893
40096
  return existsSync4(bundled) ? bundled : resolve2(here, "..", "scripts", "prepare-cloudflared.mjs");
39894
40097
  }
39895
40098
  function guardianEntry(here = HERE) {
39896
- const bundled = join6(here, "tunnel-guardian.js");
40099
+ const bundled = join5(here, "tunnel-guardian.js");
39897
40100
  if (existsSync4(bundled)) return bundled;
39898
40101
  const source = resolve2(here, "..", "electron", "managed-companion-guardian-main.mjs");
39899
40102
  return existsSync4(source) ? source : null;
@@ -39928,7 +40131,7 @@ function startTunnel(options) {
39928
40131
  const tunnel = createManagedCompanionTunnel({
39929
40132
  binaryPath: options.binaryPath,
39930
40133
  guardianEntry: options.guardian,
39931
- runtimeRoot: join6(options.dataDir, TUNNEL_RUNTIME_DIR),
40134
+ runtimeRoot: join5(options.dataDir, TUNNEL_RUNTIME_DIR),
39932
40135
  originPort,
39933
40136
  environment: env,
39934
40137
  fetchImpl: options.fetchImpl,
@@ -39974,7 +40177,7 @@ function parseArgs(argv, env = process.env) {
39974
40177
  const options = {
39975
40178
  command: command === "--help" || command === "-h" ? "help" : command,
39976
40179
  port: Number(env.OMB_PORT || 8799),
39977
- dataDir: env.OMB_DATA_DIR || join38(homedir24(), ".openmausbot"),
40180
+ dataDir: env.OMB_DATA_DIR || join37(homedir24(), ".openmausbot"),
39978
40181
  tailscale: false,
39979
40182
  tunnel: false,
39980
40183
  client: false,
@@ -40141,7 +40344,7 @@ function defaultIo() {
40141
40344
  }
40142
40345
  function serverVersion(here = HERE2) {
40143
40346
  try {
40144
- const parsed = JSON.parse(readFileSync22(resolve12(here, "..", "package.json"), "utf8"));
40347
+ const parsed = JSON.parse(readFileSync21(resolve12(here, "..", "package.json"), "utf8"));
40145
40348
  const version2 = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "version") : void 0;
40146
40349
  return typeof version2 === "string" && version2 ? version2 : "0.0.0";
40147
40350
  } catch {
@@ -40166,7 +40369,7 @@ async function isWorkspaceRunning(options) {
40166
40369
  try {
40167
40370
  const { status, body } = await api(options.port, "/api/health");
40168
40371
  if (status !== 200 || body?.app !== "openmausbot") return false;
40169
- const expected = readFileSync22(join38(options.dataDir, "environment-id"), "utf8").trim();
40372
+ const expected = readFileSync21(join37(options.dataDir, "environment-id"), "utf8").trim();
40170
40373
  const descriptor = await api(options.port, "/.well-known/openmausbot/environment");
40171
40374
  return /^[0-9a-f-]{36}$/i.test(expected) && descriptor.status === 200 && descriptor.body?.environmentId === expected;
40172
40375
  } catch {
@@ -40366,11 +40569,11 @@ async function runStatus(options, io = defaultIo()) {
40366
40569
  return code;
40367
40570
  }
40368
40571
  async function runAccess(options, io = defaultIo()) {
40369
- const file2 = join38(options.dataDir, "config.json");
40572
+ const file2 = join37(options.dataDir, "config.json");
40370
40573
  let raw = {};
40371
40574
  if (existsSync19(file2)) {
40372
40575
  try {
40373
- const parsed = JSON.parse(readFileSync22(file2, "utf8"));
40576
+ const parsed = JSON.parse(readFileSync21(file2, "utf8"));
40374
40577
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("not an object");
40375
40578
  raw = Object.fromEntries(Object.entries(parsed));
40376
40579
  } catch (error51) {
@@ -40518,16 +40721,16 @@ async function runBrowser(options, io = defaultIo()) {
40518
40721
  return 0;
40519
40722
  }
40520
40723
  function serverEntry(here = HERE2) {
40521
- const bundled = join38(here, "index.js");
40724
+ const bundled = join37(here, "index.js");
40522
40725
  const root = resolve12(here, "..");
40523
40726
  if (existsSync19(bundled)) {
40524
- const staticDir2 = [join38(root, "dist"), join38(here, "..", "ui")].find((d) => existsSync19(join38(d, "index.html"))) ?? null;
40525
- const skillsDir = existsSync19(join38(root, "skills")) ? join38(root, "skills") : null;
40727
+ const staticDir2 = [join37(root, "dist"), join37(here, "..", "ui")].find((d) => existsSync19(join37(d, "index.html"))) ?? null;
40728
+ const skillsDir = existsSync19(join37(root, "skills")) ? join37(root, "skills") : null;
40526
40729
  return { command: process.execPath, args: [bundled], staticDir: staticDir2, skillsDir };
40527
40730
  }
40528
- const source = join38(here, "index.ts");
40529
- const staticDir = existsSync19(join38(root, "dist", "index.html")) ? join38(root, "dist") : null;
40530
- return { command: process.execPath, args: ["--experimental-strip-types", source], staticDir, skillsDir: existsSync19(join38(root, "skills")) ? join38(root, "skills") : null };
40731
+ const source = join37(here, "index.ts");
40732
+ const staticDir = existsSync19(join37(root, "dist", "index.html")) ? join37(root, "dist") : null;
40733
+ return { command: process.execPath, args: ["--experimental-strip-types", source], staticDir, skillsDir: existsSync19(join37(root, "skills")) ? join37(root, "skills") : null };
40531
40734
  }
40532
40735
  async function planTunnel(options, log) {
40533
40736
  let access2 = null;
@@ -40627,10 +40830,10 @@ async function runServe(options, log = console.log) {
40627
40830
  let child;
40628
40831
  try {
40629
40832
  if (options.guided) {
40630
- const logsDir = join38(options.dataDir, "logs");
40833
+ const logsDir = join37(options.dataDir, "logs");
40631
40834
  mkdirSync17(logsDir, { recursive: true, mode: 448 });
40632
- logPath = join38(logsDir, `server-${Date.now()}-${process.pid}.log`);
40633
- logFd = openSync3(logPath, "wx", 384);
40835
+ logPath = join37(logsDir, `server-${Date.now()}-${process.pid}.log`);
40836
+ logFd = openSync4(logPath, "wx", 384);
40634
40837
  log("\nStarting your workspace\u2026");
40635
40838
  }
40636
40839
  if (tailscale) {
@@ -40654,7 +40857,7 @@ async function runServe(options, log = console.log) {
40654
40857
  }
40655
40858
  throw error51;
40656
40859
  } finally {
40657
- if (logFd !== void 0) closeSync3(logFd);
40860
+ if (logFd !== void 0) closeSync4(logFd);
40658
40861
  process.removeListener("SIGINT", cancelStartup);
40659
40862
  process.removeListener("SIGTERM", cancelStartup);
40660
40863
  }
@@ -40821,7 +41024,7 @@ async function runOnboardingCommand(options, io = defaultIo(), startServer = run
40821
41024
  }
40822
41025
  if (options.command === "setup") {
40823
41026
  io.log("\nAll set. Start with: openmausbot (or npx openmausbot without a global install).");
40824
- if (options.dataDir !== join38(homedir24(), ".openmausbot") || options.port !== 8799) {
41027
+ if (options.dataDir !== join37(homedir24(), ".openmausbot") || options.port !== 8799) {
40825
41028
  io.log(`Use the same --data-dir (${options.dataDir}) and --port (${options.port}) options when starting.`);
40826
41029
  }
40827
41030
  return 0;