openmausbot 0.1.67 → 0.1.68

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.
@@ -17577,9 +17577,9 @@ var init_schema = __esm({
17577
17577
  });
17578
17578
 
17579
17579
  // server/config.ts
17580
- import { readFileSync as readFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync3, renameSync as renameSync3 } from "node:fs";
17581
- import { homedir as homedir2 } from "node:os";
17582
- import { join as join4 } from "node:path";
17580
+ import { readFileSync as readFileSync2, mkdirSync as mkdirSync4, existsSync as existsSync3, renameSync as renameSync3 } from "node:fs";
17581
+ import { homedir as homedir4 } from "node:os";
17582
+ import { join as join5 } from "node:path";
17583
17583
  function isValidSshAlias(value) {
17584
17584
  return typeof value === "string" && SSH_ALIAS.test(value);
17585
17585
  }
@@ -17712,7 +17712,7 @@ function parseStoredConfig(value) {
17712
17712
  function loadConfig() {
17713
17713
  let cfg = {};
17714
17714
  try {
17715
- cfg = parseStoredConfig(parseJson(readFileSync2(join4(DATA_DIR, "config.json"), "utf8")));
17715
+ cfg = parseStoredConfig(parseJson(readFileSync2(join5(DATA_DIR, "config.json"), "utf8")));
17716
17716
  } catch {
17717
17717
  }
17718
17718
  cfg.xai = { ...cfg.xai };
@@ -17744,7 +17744,7 @@ function stripWorkspaceCredentialEnv(env) {
17744
17744
  for (const key of WORKSPACE_CREDENTIAL_ENV) delete env[key];
17745
17745
  }
17746
17746
  function saveConfig(patch, options = {}) {
17747
- const p = join4(DATA_DIR, "config.json");
17747
+ const p = join5(DATA_DIR, "config.json");
17748
17748
  let disk = {};
17749
17749
  try {
17750
17750
  const parsed = jsonObjectSchema.safeParse(parseJson(readFileSync2(p, "utf8")));
@@ -17754,7 +17754,7 @@ function saveConfig(patch, options = {}) {
17754
17754
  const checkedPatch = appConfigSchema.partial().parse(patch);
17755
17755
  const storedProfiles = storedBrowserProfilesSchema.safeParse(disk.browserProfiles);
17756
17756
  if (storedProfiles.success) disk.browserProfiles = storedProfiles.data;
17757
- for (const key of ["xai", "openaiCompat", "composio", "box", "opencodeGo", "tts", "imageGen", "profile", "rooms", "localVm", "features"]) {
17757
+ for (const key of ["xai", "openaiCompat", "composio", "box", "opencodeGo", "tts", "imageGen", "profile", "rooms", "threads", "localVm", "features"]) {
17758
17758
  const section = checkedPatch[key];
17759
17759
  if (!section) continue;
17760
17760
  const current = jsonObjectSchema.safeParse(disk[key]);
@@ -17803,7 +17803,7 @@ function saveConfig(patch, options = {}) {
17803
17803
  }
17804
17804
  disk.instances = diskInstances;
17805
17805
  }
17806
- mkdirSync3(DATA_DIR, { recursive: true });
17806
+ mkdirSync4(DATA_DIR, { recursive: true });
17807
17807
  writeFileAtomic(p, JSON.stringify(disk, null, 2), { mode: 384 });
17808
17808
  }
17809
17809
  function injectedEnvironment(cfg, driver) {
@@ -17875,7 +17875,7 @@ function instanceConfigs(cfg) {
17875
17875
  }
17876
17876
  return map3;
17877
17877
  }
17878
- var optionalText, SSH_ALIAS, LEGACY_BROWSER_PROFILE_ID, BROWSER_PROFILE_ID, MIN_ROOM_TURN_TIMEOUT_MINUTES, MAX_ROOM_TURN_TIMEOUT_MINUTES, MIN_LOCAL_VM_MAX_INSTANCES, MAX_LOCAL_VM_MAX_INSTANCES, vpsConfigSchema, roomConfigSchema, localVmConfigSchema, browserProfileSchema, legacyBrowserProfileSchema, legacyBrowserProfilesSchema, storedBrowserProfilesSchema, browserProfilesSchema, featureConfigSchema, instanceConfigSchema, instanceConfigMapSchema, defaultModelSelectionSchema, appConfigSchema, storedAppConfigSchema, appConfigPatchSchema, jsonObjectSchema, DATA_DIR, LEGACY_DATA_DIR, EVENTS_DIR, NATIVE_DIR, WORKSPACE_CREDENTIAL_ENV, PROVIDER_CREDENTIAL_ENV;
17878
+ var optionalText, SSH_ALIAS, LEGACY_BROWSER_PROFILE_ID, BROWSER_PROFILE_ID, MIN_ROOM_TURN_TIMEOUT_MINUTES, MAX_ROOM_TURN_TIMEOUT_MINUTES, MAX_CONCURRENT_BOT_THREADS, MIN_LOCAL_VM_MAX_INSTANCES, MAX_LOCAL_VM_MAX_INSTANCES, vpsConfigSchema, roomConfigSchema, localVmConfigSchema, browserProfileSchema, legacyBrowserProfileSchema, legacyBrowserProfilesSchema, storedBrowserProfilesSchema, browserProfilesSchema, featureConfigSchema, instanceConfigSchema, instanceConfigMapSchema, defaultModelSelectionSchema, appConfigSchema, storedAppConfigSchema, appConfigPatchSchema, jsonObjectSchema, DATA_DIR, LEGACY_DATA_DIR, EVENTS_DIR, NATIVE_DIR, WORKSPACE_CREDENTIAL_ENV, PROVIDER_CREDENTIAL_ENV;
17879
17879
  var init_config = __esm({
17880
17880
  "server/config.ts"() {
17881
17881
  "use strict";
@@ -17890,6 +17890,7 @@ var init_config = __esm({
17890
17890
  BROWSER_PROFILE_ID = /^[a-z0-9_-]{1,40}$/;
17891
17891
  MIN_ROOM_TURN_TIMEOUT_MINUTES = 1;
17892
17892
  MAX_ROOM_TURN_TIMEOUT_MINUTES = 1440;
17893
+ MAX_CONCURRENT_BOT_THREADS = 10;
17893
17894
  MIN_LOCAL_VM_MAX_INSTANCES = 1;
17894
17895
  MAX_LOCAL_VM_MAX_INSTANCES = 4;
17895
17896
  vpsConfigSchema = external_exports.object({
@@ -17995,6 +17996,7 @@ var init_config = __esm({
17995
17996
  * system language. Unknown tags degrade to English in the renderer. */
17996
17997
  language: optionalText,
17997
17998
  rooms: roomConfigSchema.optional(),
17999
+ threads: external_exports.object({ maxConcurrentPerBot: external_exports.number().int().min(1).max(MAX_CONCURRENT_BOT_THREADS) }).strict().optional(),
17998
18000
  localVm: localVmConfigSchema.optional(),
17999
18001
  features: featureConfigSchema.optional(),
18000
18002
  browserProfiles: browserProfilesSchema.optional(),
@@ -18010,10 +18012,10 @@ var init_config = __esm({
18010
18012
  });
18011
18013
  appConfigPatchSchema = appConfigSchema.omit({ instances: true, mcpServers: true, cliStartup: true, customDomain: true });
18012
18014
  jsonObjectSchema = external_exports.record(external_exports.string(), external_exports.json());
18013
- DATA_DIR = process.env.OMB_DATA_DIR ?? join4(homedir2(), ".openmausbot");
18014
- LEGACY_DATA_DIR = join4(homedir2(), ".opengrokbot");
18015
- EVENTS_DIR = join4(DATA_DIR, "events");
18016
- NATIVE_DIR = join4(DATA_DIR, "native");
18015
+ DATA_DIR = process.env.OMB_DATA_DIR ?? join5(homedir4(), ".openmausbot");
18016
+ LEGACY_DATA_DIR = join5(homedir4(), ".opengrokbot");
18017
+ EVENTS_DIR = join5(DATA_DIR, "events");
18018
+ NATIVE_DIR = join5(DATA_DIR, "native");
18017
18019
  WORKSPACE_CREDENTIAL_ENV = [
18018
18020
  "XAI_API_KEY",
18019
18021
  "OPENAI_COMPAT_API_KEY",
@@ -18050,9 +18052,9 @@ var init_config = __esm({
18050
18052
  // electron/data-dir-lease.mjs
18051
18053
  import { execFileSync } from "node:child_process";
18052
18054
  import { createHash as createHash2, randomUUID as randomUUID5 } from "node:crypto";
18053
- import { existsSync as existsSync4, linkSync, mkdirSync as mkdirSync4, readFileSync as readFileSync3, renameSync as renameSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
18055
+ import { existsSync as existsSync4, linkSync, mkdirSync as mkdirSync5, readFileSync as readFileSync3, renameSync as renameSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
18054
18056
  import { hostname as hostname4, uptime } from "node:os";
18055
- import { join as join5 } from "node:path";
18057
+ import { join as join6 } from "node:path";
18056
18058
  function leaseError(message2, cause) {
18057
18059
  return Object.assign(new DataDirLeaseError(message2), { cause });
18058
18060
  }
@@ -18157,7 +18159,7 @@ function unlinkExact(path3, message2) {
18157
18159
  function publishRecord(path3, record3, prepareMessage, acquireMessage) {
18158
18160
  const candidatePath = `${path3}.candidate-${record3.pid}-${record3.token}`;
18159
18161
  try {
18160
- writeFileSync3(candidatePath, `${JSON.stringify(record3)}
18162
+ writeFileSync4(candidatePath, `${JSON.stringify(record3)}
18161
18163
  `, { flag: "wx", mode: 384, flush: true });
18162
18164
  } catch (error51) {
18163
18165
  throw leaseError(prepareMessage, error51);
@@ -18240,14 +18242,14 @@ function prepareDataDir(dataDir, legacyDataDir) {
18240
18242
  }
18241
18243
  }
18242
18244
  try {
18243
- mkdirSync4(dataDir, { recursive: true, mode: 448 });
18245
+ mkdirSync5(dataDir, { recursive: true, mode: 448 });
18244
18246
  } catch (error51) {
18245
18247
  throw leaseError("OpenMausBot cannot create its data directory.", error51);
18246
18248
  }
18247
- return join5(dataDir, LEASE_NAME);
18249
+ return join6(dataDir, LEASE_NAME);
18248
18250
  }
18249
18251
  function assertNoLiveDelegatedChild(dataDir) {
18250
- const childLeasePath = join5(dataDir, DELEGATED_CHILD_DIR, LEASE_NAME);
18252
+ const childLeasePath = join6(dataDir, DELEGATED_CHILD_DIR, LEASE_NAME);
18251
18253
  const child = readOwner(childLeasePath);
18252
18254
  if (!child) return;
18253
18255
  if (child.host !== hostname4()) {
@@ -18278,7 +18280,7 @@ function acquireDataDirLeaseInternal(dataDir, options = {}) {
18278
18280
  };
18279
18281
  const candidatePath = `${leasePath}.candidate-${owner.pid}-${owner.token}`;
18280
18282
  try {
18281
- writeFileSync3(candidatePath, `${JSON.stringify(owner)}
18283
+ writeFileSync4(candidatePath, `${JSON.stringify(owner)}
18282
18284
  `, { flag: "wx", mode: 384, flush: true });
18283
18285
  } catch (error51) {
18284
18286
  throw leaseError("OpenMausBot cannot prepare its data-directory lease.", error51);
@@ -18371,62 +18373,62 @@ var init_data_dir_lease2 = __esm({
18371
18373
  // server/env-path.ts
18372
18374
  import { execFile as execFile2 } from "node:child_process";
18373
18375
  import { closeSync as closeSync2, existsSync as existsSync5, openSync as openSync2, readFileSync as readFileSync4, readSync, statSync as statSync2, readdirSync } from "node:fs";
18374
- import { homedir as homedir3 } from "node:os";
18375
- import { basename, delimiter as delimiter3, dirname as dirname2, extname, join as join6 } from "node:path";
18376
+ import { homedir as homedir5 } from "node:os";
18377
+ import { basename as basename2, delimiter as delimiter3, dirname as dirname3, extname, join as join7 } from "node:path";
18376
18378
  function nvmBinDirs() {
18377
18379
  try {
18378
- const base2 = join6(homedir3(), ".nvm", "versions", "node");
18379
- return readdirSync(base2).filter((v) => v.startsWith("v")).sort((a2, b) => b.localeCompare(a2, void 0, { numeric: true })).map((v) => join6(base2, v, "bin"));
18380
+ const base2 = join7(homedir5(), ".nvm", "versions", "node");
18381
+ return readdirSync(base2).filter((v) => v.startsWith("v")).sort((a2, b) => b.localeCompare(a2, void 0, { numeric: true })).map((v) => join7(base2, v, "bin"));
18380
18382
  } catch {
18381
18383
  return [];
18382
18384
  }
18383
18385
  }
18384
18386
  function knownDirs() {
18385
- const home = homedir3();
18387
+ const home = homedir5();
18386
18388
  return [
18387
- join6(home, ".local", "bin"),
18389
+ join7(home, ".local", "bin"),
18388
18390
  // claude installer default
18389
- join6(home, ".npm-global", "bin"),
18391
+ join7(home, ".npm-global", "bin"),
18390
18392
  // npm prefix ~/.npm-global (claude, opencode)
18391
- join6(home, ".kimi-code", "bin"),
18393
+ join7(home, ".kimi-code", "bin"),
18392
18394
  // kimi-code installer
18393
- join6(home, ".grok", "bin"),
18395
+ join7(home, ".grok", "bin"),
18394
18396
  // x.ai installer
18395
- join6(home, ".opencode", "bin"),
18397
+ join7(home, ".opencode", "bin"),
18396
18398
  // opencode installer
18397
- join6(home, ".claude", "local"),
18399
+ join7(home, ".claude", "local"),
18398
18400
  // claude "local install"
18399
18401
  "/opt/homebrew/bin",
18400
18402
  // brew, Apple silicon
18401
18403
  "/usr/local/bin",
18402
18404
  // brew Intel / classic installs
18403
- join6(home, ".volta", "bin"),
18404
- join6(home, ".bun", "bin"),
18405
- join6(home, ".asdf", "shims"),
18406
- join6(home, ".deno", "bin"),
18407
- join6(home, "bin"),
18405
+ join7(home, ".volta", "bin"),
18406
+ join7(home, ".bun", "bin"),
18407
+ join7(home, ".asdf", "shims"),
18408
+ join7(home, ".deno", "bin"),
18409
+ join7(home, "bin"),
18408
18410
  ...nvmBinDirs()
18409
18411
  ];
18410
18412
  }
18411
18413
  function windowsKnownDirs() {
18412
- const home = homedir3();
18413
- const appData = process.env.APPDATA ?? join6(home, "AppData", "Roaming");
18414
- const localAppData = process.env.LOCALAPPDATA ?? join6(home, "AppData", "Local");
18414
+ const home = homedir5();
18415
+ const appData = process.env.APPDATA ?? join7(home, "AppData", "Roaming");
18416
+ const localAppData = process.env.LOCALAPPDATA ?? join7(home, "AppData", "Local");
18415
18417
  return [
18416
- join6(appData, "npm"),
18418
+ join7(appData, "npm"),
18417
18419
  // npm -g shims: claude, codex
18418
- join6(home, ".grok", "bin"),
18420
+ join7(home, ".grok", "bin"),
18419
18421
  // x.ai installer
18420
- join6(localAppData, "agy", "bin"),
18422
+ join7(localAppData, "agy", "bin"),
18421
18423
  // Antigravity installer
18422
- join6(home, ".local", "bin"),
18424
+ join7(home, ".local", "bin"),
18423
18425
  // claude native installer
18424
- join6(home, ".claude", "local"),
18425
- join6(home, "bin"),
18426
+ join7(home, ".claude", "local"),
18427
+ join7(home, "bin"),
18426
18428
  // Factory droid installer (%USERPROFILE%\bin)
18427
- join6(home, ".bun", "bin"),
18428
- join6(home, ".deno", "bin"),
18429
- join6(home, "go", "bin")
18429
+ join7(home, ".bun", "bin"),
18430
+ join7(home, ".deno", "bin"),
18431
+ join7(home, "go", "bin")
18430
18432
  ];
18431
18433
  }
18432
18434
  function resetPathCache() {
@@ -18480,7 +18482,7 @@ function findCliCandidates(name) {
18480
18482
  for (const dir of augmentedPath().split(delimiter3)) {
18481
18483
  if (!dir) continue;
18482
18484
  for (const ext of exts) {
18483
- const p = join6(dir, name + ext);
18485
+ const p = join7(dir, name + ext);
18484
18486
  if (existsSync5(p)) {
18485
18487
  out.push(p);
18486
18488
  break;
@@ -18525,13 +18527,13 @@ function whichWin(cli) {
18525
18527
  if (/[\\/]/.test(cli) || /^[a-zA-Z]:/.test(cli)) return probe(cli);
18526
18528
  for (const dir of augmentedPath().split(delimiter3)) {
18527
18529
  if (!dir) continue;
18528
- const hit = probe(join6(dir, cli));
18530
+ const hit = probe(join7(dir, cli));
18529
18531
  if (hit) return hit;
18530
18532
  }
18531
18533
  return null;
18532
18534
  }
18533
18535
  function nodeExe(near) {
18534
- const local = join6(near, "node.exe");
18536
+ const local = join7(near, "node.exe");
18535
18537
  if (isFile(local)) return local;
18536
18538
  const onPath2 = whichWin("node.exe");
18537
18539
  if (onPath2 && extname(onPath2).toLowerCase() === ".exe") return onPath2;
@@ -18544,8 +18546,8 @@ function parseCmdShim(shim) {
18544
18546
  } catch {
18545
18547
  return null;
18546
18548
  }
18547
- const dir = dirname2(shim);
18548
- const targets = [...text2.matchAll(/"%~?dp0%?\\?([^"]+)"/g)].map((m) => join6(dir, m[1])).filter((p) => isFile(p) && basename(p).toLowerCase() !== "node.exe");
18549
+ const dir = dirname3(shim);
18550
+ const targets = [...text2.matchAll(/"%~?dp0%?\\?([^"]+)"/g)].map((m) => join7(dir, m[1])).filter((p) => isFile(p) && basename2(p).toLowerCase() !== "node.exe");
18549
18551
  const script = targets.find((p) => /\.[cm]?js$/i.test(p));
18550
18552
  if (script) {
18551
18553
  const node = nodeExe(dir);
@@ -18573,7 +18575,7 @@ function parseNodeShebang(file2) {
18573
18575
  }
18574
18576
  }
18575
18577
  if (!/^#!.*\bnode(\.exe)?\b/.test(head)) return null;
18576
- const node = nodeExe(dirname2(file2));
18578
+ const node = nodeExe(dirname3(file2));
18577
18579
  return node ? { command: node, args: [file2] } : null;
18578
18580
  }
18579
18581
  function resolveWord(cli, args) {
@@ -18615,7 +18617,7 @@ import {
18615
18617
  spawn as spawn4,
18616
18618
  execFile as execFile3
18617
18619
  } from "node:child_process";
18618
- import { join as join7 } from "node:path";
18620
+ import { join as join8 } from "node:path";
18619
18621
  function resolveCli(cli, args = []) {
18620
18622
  return resolveCliSpawn(cli, args);
18621
18623
  }
@@ -18709,7 +18711,7 @@ function killCliTree(child, timeoutMs = 5e3) {
18709
18711
  });
18710
18712
  }
18711
18713
  function brokerSocketPath(dataDir, tag) {
18712
- return process.platform === "win32" ? `\\\\.\\pipe\\openmausbot-perm-${process.pid}-${tag}` : join7(dataDir, `perm-${tag}.sock`);
18714
+ return process.platform === "win32" ? `\\\\.\\pipe\\openmausbot-perm-${process.pid}-${tag}` : join8(dataDir, `perm-${tag}.sock`);
18713
18715
  }
18714
18716
  var WINDOWS_SAFE_COMMAND_LINE_CHARS;
18715
18717
  var init_procs = __esm({
@@ -18937,8 +18939,8 @@ var init_registry = __esm({
18937
18939
 
18938
18940
  // server/drivers/local-inject.ts
18939
18941
  import { readFileSync as readFileSync5 } from "node:fs";
18940
- import { homedir as homedir4 } from "node:os";
18941
- import { join as join8 } from "node:path";
18942
+ import { homedir as homedir6 } from "node:os";
18943
+ import { join as join9 } from "node:path";
18942
18944
  function contextWindowsFromPs(extra) {
18943
18945
  const out = /* @__PURE__ */ new Map();
18944
18946
  const rec = extra && typeof extra === "object" ? extra : null;
@@ -19021,9 +19023,9 @@ function firstUnslothToken(row) {
19021
19023
  return null;
19022
19024
  }
19023
19025
  function readUnslothKey(env) {
19024
- const home = env.HOME || env.USERPROFILE || homedir4();
19026
+ const home = env.HOME || env.USERPROFILE || homedir6();
19025
19027
  try {
19026
- const raw = JSON.parse(readFileSync5(join8(home, ".unsloth", "studio", "auth", "agent_api_key.json"), "utf8"));
19028
+ const raw = JSON.parse(readFileSync5(join9(home, ".unsloth", "studio", "auth", "agent_api_key.json"), "utf8"));
19027
19029
  if (raw.servers && typeof raw.servers === "object") {
19028
19030
  const servers = raw.servers;
19029
19031
  for (const url2 of ["http://127.0.0.1:8888", "http://localhost:8888"]) {
@@ -19225,17 +19227,17 @@ var init_local_inject = __esm({
19225
19227
 
19226
19228
  // server/proxy-paths.ts
19227
19229
  import { existsSync as existsSync6 } from "node:fs";
19228
- import { dirname as dirname3, join as join9 } from "node:path";
19230
+ import { dirname as dirname4, join as join10 } from "node:path";
19229
19231
  import { fileURLToPath as fileURLToPath2 } from "node:url";
19230
19232
  function resolveProxy(relative2) {
19231
- const source = join9(SERVER_ROOT, `${relative2}.ts`);
19232
- return existsSync6(source) ? source : join9(SERVER_ROOT, `${relative2}.js`);
19233
+ const source = join10(SERVER_ROOT, `${relative2}.ts`);
19234
+ return existsSync6(source) ? source : join10(SERVER_ROOT, `${relative2}.js`);
19233
19235
  }
19234
19236
  var SERVER_ROOT, SPAWNED_PROXIES;
19235
19237
  var init_proxy_paths = __esm({
19236
19238
  "server/proxy-paths.ts"() {
19237
19239
  "use strict";
19238
- SERVER_ROOT = dirname3(fileURLToPath2(import.meta.url));
19240
+ SERVER_ROOT = dirname4(fileURLToPath2(import.meta.url));
19239
19241
  SPAWNED_PROXIES = {
19240
19242
  browser: resolveProxy("browser-proxy"),
19241
19243
  computer: resolveProxy("computer-proxy"),
@@ -19257,7 +19259,7 @@ var init_proxy_paths = __esm({
19257
19259
 
19258
19260
  // server/container-computer.ts
19259
19261
  import { execFile as execFile4 } from "node:child_process";
19260
- import { join as join10, resolve as resolve3 } from "node:path";
19262
+ import { join as join11, resolve as resolve3 } from "node:path";
19261
19263
  import { promisify } from "node:util";
19262
19264
  function computerProxyEnv(computer) {
19263
19265
  return {
@@ -19281,7 +19283,7 @@ var init_container_computer = __esm({
19281
19283
  IMAGE_REPOSITORY = "localhost/openmausbot/cua-local-vm";
19282
19284
  IMAGE_LAYER_VERSION = "5";
19283
19285
  IMAGE = `${IMAGE_REPOSITORY}:driver-${CUA_DRIVER_VERSION}-v${IMAGE_LAYER_VERSION}`;
19284
- VM_WORKSPACE_DIR = join10(DATA_DIR, "vm-home");
19286
+ VM_WORKSPACE_DIR = join11(DATA_DIR, "vm-home");
19285
19287
  MEMORY_BYTES = 4 * 1024 * 1024 * 1024;
19286
19288
  SHM_BYTES = 512 * 1024 * 1024;
19287
19289
  containerMcpPath = SPAWNED_PROXIES.containerMcp;
@@ -19372,11 +19374,11 @@ var init_redact = __esm({
19372
19374
 
19373
19375
  // server/drivers/native.ts
19374
19376
  import { appendFileSync } from "node:fs";
19375
- import { join as join11 } from "node:path";
19377
+ import { join as join12 } from "node:path";
19376
19378
  function appendNative(threadId, entry) {
19377
19379
  try {
19378
19380
  appendFileSync(
19379
- join11(NATIVE_DIR, `${threadId}.ndjson`),
19381
+ join12(NATIVE_DIR, `${threadId}.ndjson`),
19380
19382
  JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), ...entry, msg: redactSecrets(entry.msg) }) + "\n",
19381
19383
  { mode: 384 }
19382
19384
  );
@@ -19392,9 +19394,9 @@ var init_native = __esm({
19392
19394
  });
19393
19395
 
19394
19396
  // server/drivers/acp/core.ts
19395
- import { homedir as homedir5 } from "node:os";
19397
+ import { homedir as homedir7 } from "node:os";
19396
19398
  import { lstat, mkdir, readFile, realpath, stat, writeFile } from "node:fs/promises";
19397
- import { basename as basename2, dirname as dirname4, isAbsolute, relative, resolve as resolve4 } from "node:path";
19399
+ import { basename as basename3, dirname as dirname5, isAbsolute, relative, resolve as resolve4 } from "node:path";
19398
19400
  function skipSubscriptionAuthForLocalInject(model) {
19399
19401
  return Boolean(decodeInjectId(model));
19400
19402
  }
@@ -19579,7 +19581,7 @@ function createAcpDriver(support11) {
19579
19581
  throw new Error("local computer control requires interactive provider approvals");
19580
19582
  }
19581
19583
  const turnId = newId();
19582
- const cwd = turn.cwd ?? turnConfig.workspace ?? homedir5();
19584
+ const cwd = turn.cwd ?? turnConfig.workspace ?? homedir7();
19583
19585
  const env = childEnv(turnConfig);
19584
19586
  if (support11.requireAuthenticationBeforeSpawn && !skipSubscriptionAuthForLocalInject(turn.model) && !await support11.isAuthenticated(env, turnConfig, instanceId)) {
19585
19587
  emit({ ...base2(threadId, turnId), type: "turn.started" });
@@ -19647,12 +19649,12 @@ function createAcpDriver(support11) {
19647
19649
  if (lexical === ".." || lexical.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute(lexical)) {
19648
19650
  throw new Error("ACP file path is outside the session workspace.");
19649
19651
  }
19650
- const suffix = [basename2(requested)];
19651
- let ancestor = dirname4(requested);
19652
+ const suffix = [basename3(requested)];
19653
+ let ancestor = dirname5(requested);
19652
19654
  while (!await lstat(ancestor).catch(() => null)) {
19653
- const parent = dirname4(ancestor);
19655
+ const parent = dirname5(ancestor);
19654
19656
  if (parent === ancestor) throw new Error("ACP file path has no accessible parent.");
19655
- suffix.unshift(basename2(ancestor));
19657
+ suffix.unshift(basename3(ancestor));
19656
19658
  ancestor = parent;
19657
19659
  }
19658
19660
  const candidate = resolve4(await realpath(ancestor), ...suffix);
@@ -19698,7 +19700,7 @@ function createAcpDriver(support11) {
19698
19700
  if (typeof params.content !== "string" || Buffer.byteLength(params.content) > CLIENT_FILE_MAX_BYTES) {
19699
19701
  throw new Error(`ACP can only write text files under ${CLIENT_FILE_MAX_BYTES} bytes.`);
19700
19702
  }
19701
- await mkdir(dirname4(path3), { recursive: true });
19703
+ await mkdir(dirname5(path3), { recursive: true });
19702
19704
  await writeFile(path3, params.content, "utf8");
19703
19705
  send({ jsonrpc: "2.0", id: msg.id, result: {} });
19704
19706
  } catch (error51) {
@@ -20162,7 +20164,7 @@ import { createHash as createHash3, randomUUID as randomUUID6 } from "node:crypt
20162
20164
  import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm, stat as stat2 } from "node:fs/promises";
20163
20165
  import { request as httpRequest } from "node:http";
20164
20166
  import { tmpdir as tmpdir2 } from "node:os";
20165
- import { delimiter as delimiter4, join as join12, resolve as resolve5 } from "node:path";
20167
+ import { delimiter as delimiter4, join as join13, resolve as resolve5 } from "node:path";
20166
20168
  function quoteBrowserArgument(value) {
20167
20169
  return `'${value.replaceAll("'", `'"'"'`)}'`;
20168
20170
  }
@@ -20197,11 +20199,11 @@ function authorizationUrlFromLine(line) {
20197
20199
  }
20198
20200
  }
20199
20201
  function antigravityProfileDirectory(instanceId, baseDir = DATA_DIR) {
20200
- return join12(baseDir, "providers", "antigravity", createHash3("sha256").update(instanceId).digest("hex"));
20202
+ return join13(baseDir, "providers", "antigravity", createHash3("sha256").update(instanceId).digest("hex"));
20201
20203
  }
20202
20204
  async function prepareAntigravityProfile(input) {
20203
20205
  const directory = resolve5(input.profileDirectory ?? antigravityProfileDirectory(input.instanceId, input.baseDir));
20204
- const acpDirectory = join12(directory, "antigravity-acp");
20206
+ const acpDirectory = join13(directory, "antigravity-acp");
20205
20207
  await mkdir2(acpDirectory, { recursive: true, mode: 448 });
20206
20208
  if (process.platform !== "win32") {
20207
20209
  const { chmod: chmod2 } = await import("node:fs/promises");
@@ -20209,7 +20211,7 @@ async function prepareAntigravityProfile(input) {
20209
20211
  await chmod2(acpDirectory, 448);
20210
20212
  }
20211
20213
  writeFileAtomic(
20212
- join12(acpDirectory, "settings.json"),
20214
+ join13(acpDirectory, "settings.json"),
20213
20215
  `${JSON.stringify({ auth: { type: "oauth-personal" } })}
20214
20216
  `,
20215
20217
  { mode: 384 }
@@ -20232,7 +20234,7 @@ async function prepareAntigravityProfile(input) {
20232
20234
  PYTHONUNBUFFERED: "1",
20233
20235
  ELECTRON_RUN_AS_NODE: "1"
20234
20236
  });
20235
- return { directory, tokenPath: join12(acpDirectory, "acp_token.json"), environment };
20237
+ return { directory, tokenPath: join13(acpDirectory, "acp_token.json"), environment };
20236
20238
  }
20237
20239
  async function antigravityProfileAuthenticated(profile) {
20238
20240
  try {
@@ -20361,7 +20363,7 @@ function isValidAntigravityInitializeResult(initialized, expectedVersion) {
20361
20363
  return true;
20362
20364
  }
20363
20365
  async function validateAntigravityRuntime(runtime, expectedVersion) {
20364
- const profileDirectory = await mkdtemp(join12(tmpdir2(), "openmaus-antigravity-verify-"));
20366
+ const profileDirectory = await mkdtemp(join13(tmpdir2(), "openmaus-antigravity-verify-"));
20365
20367
  let client;
20366
20368
  let failed = false;
20367
20369
  let failure;
@@ -20750,7 +20752,7 @@ import {
20750
20752
  writeFile as writeFile2
20751
20753
  } from "node:fs/promises";
20752
20754
  import { constants } from "node:fs";
20753
- import { delimiter as delimiter5, dirname as dirname5, isAbsolute as isAbsolute2, join as join13, resolve as resolve6 } from "node:path";
20755
+ import { delimiter as delimiter5, dirname as dirname6, isAbsolute as isAbsolute2, join as join14, resolve as resolve6 } from "node:path";
20754
20756
  import { Transform } from "node:stream";
20755
20757
  import { pipeline } from "node:stream/promises";
20756
20758
  import { createInflateRaw } from "node:zlib";
@@ -20771,7 +20773,7 @@ async function retryOnWindowsFileLock(step, options = {}) {
20771
20773
  }
20772
20774
  }
20773
20775
  function runtimeRoot(baseDir = DATA_DIR) {
20774
- return join13(baseDir, "tools", "antigravity-acp", `${process.platform}-${process.arch}`);
20776
+ return join14(baseDir, "tools", "antigravity-acp", `${process.platform}-${process.arch}`);
20775
20777
  }
20776
20778
  function executableNames(platform = process.platform) {
20777
20779
  return platform === "win32" ? { executable: "agy_acp_server.exe", harness: "localharness_external.exe" } : { executable: "agy_acp_server.par", harness: "localharness_external" };
@@ -20788,10 +20790,10 @@ async function executableFile(path3, bytes) {
20788
20790
  }
20789
20791
  async function completedRelease(directory, asset) {
20790
20792
  try {
20791
- const record3 = JSON.parse(await readFile3(join13(directory, INSTALL_RECORD), "utf8"));
20793
+ const record3 = JSON.parse(await readFile3(join14(directory, INSTALL_RECORD), "utf8"));
20792
20794
  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;
20793
- const executablePath = join13(directory, asset.executable.name);
20794
- const harnessPath = join13(directory, asset.harness.name);
20795
+ const executablePath = join14(directory, asset.executable.name);
20796
+ const harnessPath = join14(directory, asset.harness.name);
20795
20797
  if (!await executableFile(executablePath, asset.executable.bytes)) return null;
20796
20798
  if (!await executableFile(harnessPath, asset.harness.bytes)) return null;
20797
20799
  return { executablePath, harnessPath, source: "managed", version: asset.version };
@@ -20807,7 +20809,7 @@ function pathCandidates(binary2, env) {
20807
20809
  async function externalRuntime(candidate, source) {
20808
20810
  const names = executableNames();
20809
20811
  const executablePath = resolve6(candidate);
20810
- const harnessPath = join13(dirname5(executablePath), names.harness);
20812
+ const harnessPath = join14(dirname6(executablePath), names.harness);
20811
20813
  if (!await executableFile(executablePath) || !await executableFile(harnessPath)) return null;
20812
20814
  return { executablePath, harnessPath, source, version: null };
20813
20815
  }
@@ -20827,7 +20829,7 @@ async function resolveAntigravityRuntime(binaryPath, env = process.env, baseDir
20827
20829
  }
20828
20830
  const asset = resolveAntigravityReleaseAsset();
20829
20831
  if (asset) {
20830
- const managed = await completedRelease(join13(runtimeRoot(baseDir), "versions", asset.sha256), asset);
20832
+ const managed = await completedRelease(join14(runtimeRoot(baseDir), "versions", asset.sha256), asset);
20831
20833
  if (managed) return managed;
20832
20834
  }
20833
20835
  const names = executableNames();
@@ -20949,7 +20951,7 @@ async function extractPinnedArchive(archivePath, outputDirectory, asset) {
20949
20951
  start: dataOffset,
20950
20952
  end: dataOffset + entry.compressedBytes - 1
20951
20953
  });
20952
- const destination = createWriteStream(join13(outputDirectory, entry.name), { flags: "wx", mode: 448 });
20954
+ const destination = createWriteStream(join14(outputDirectory, entry.name), { flags: "wx", mode: 448 });
20953
20955
  if (entry.compression === 8) await pipeline(source, createInflateRaw(), counter, destination);
20954
20956
  else await pipeline(source, counter, destination);
20955
20957
  if (bytes !== expectedBytes) throw new Error("An extracted file was truncated.");
@@ -20960,8 +20962,8 @@ async function installOnce(options) {
20960
20962
  const asset = options.asset === void 0 ? resolveAntigravityReleaseAsset() : options.asset;
20961
20963
  if (!asset) throw new Error(`Google does not publish an Antigravity runtime for ${process.platform}-${process.arch}.`);
20962
20964
  const root = runtimeRoot(options.baseDir);
20963
- const versions = join13(root, "versions");
20964
- const destination = join13(versions, asset.sha256);
20965
+ const versions = join14(root, "versions");
20966
+ const destination = join14(versions, asset.sha256);
20965
20967
  await mkdir3(versions, { recursive: true, mode: 448 });
20966
20968
  const existing = await completedRelease(destination, asset);
20967
20969
  if (existing) {
@@ -20977,11 +20979,11 @@ async function installOnce(options) {
20977
20979
  } catch (error51) {
20978
20980
  if (error51 instanceof Error && error51.message.includes("Antigravity needs")) throw error51;
20979
20981
  }
20980
- const staging = await mkdtemp2(join13(versions, `${STAGING_PREFIX}${randomUUID7()}-`));
20982
+ const staging = await mkdtemp2(join14(versions, `${STAGING_PREFIX}${randomUUID7()}-`));
20981
20983
  let failed = false;
20982
20984
  try {
20983
- const archivePath = join13(staging, "download.zip");
20984
- const runtimeDirectory = join13(staging, "runtime");
20985
+ const archivePath = join14(staging, "download.zip");
20986
+ const runtimeDirectory = join14(staging, "runtime");
20985
20987
  await mkdir3(runtimeDirectory, { mode: 448 });
20986
20988
  const controller = new AbortController();
20987
20989
  const timeout = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS2);
@@ -21027,12 +21029,12 @@ async function installOnce(options) {
21027
21029
  }
21028
21030
  await extractPinnedArchive(archivePath, runtimeDirectory, asset);
21029
21031
  if (process.platform !== "win32") {
21030
- await chmod(join13(runtimeDirectory, asset.executable.name), 493);
21031
- await chmod(join13(runtimeDirectory, asset.harness.name), 493);
21032
+ await chmod(join14(runtimeDirectory, asset.executable.name), 493);
21033
+ await chmod(join14(runtimeDirectory, asset.harness.name), 493);
21032
21034
  }
21033
21035
  const candidate = {
21034
- executablePath: join13(runtimeDirectory, asset.executable.name),
21035
- harnessPath: join13(runtimeDirectory, asset.harness.name),
21036
+ executablePath: join14(runtimeDirectory, asset.executable.name),
21037
+ harnessPath: join14(runtimeDirectory, asset.harness.name),
21036
21038
  source: "managed",
21037
21039
  version: asset.version
21038
21040
  };
@@ -21043,7 +21045,7 @@ async function installOnce(options) {
21043
21045
  executable: asset.executable,
21044
21046
  harness: asset.harness
21045
21047
  };
21046
- await writeFile2(join13(runtimeDirectory, INSTALL_RECORD), `${JSON.stringify(record3)}
21048
+ await writeFile2(join14(runtimeDirectory, INSTALL_RECORD), `${JSON.stringify(record3)}
21047
21049
  `, { flag: "wx", mode: 384 });
21048
21050
  try {
21049
21051
  await retryOnWindowsFileLock(() => rename(runtimeDirectory, destination));
@@ -21482,15 +21484,15 @@ var init_boxagent = __esm({
21482
21484
  // server/drivers/claude-login-auth.ts
21483
21485
  import { randomUUID as randomUUID8 } from "node:crypto";
21484
21486
  import { realpathSync } from "node:fs";
21485
- import { homedir as homedir6 } from "node:os";
21486
- import { basename as basename3, dirname as dirname6, isAbsolute as isAbsolute3, join as join14, resolve as resolve7 } from "node:path";
21487
+ import { homedir as homedir8 } from "node:os";
21488
+ import { basename as basename4, dirname as dirname7, isAbsolute as isAbsolute3, join as join15, resolve as resolve7 } from "node:path";
21487
21489
  import { stripVTControlCharacters as stripVTControlCharacters3 } from "node:util";
21488
21490
  function canonicalPath(path3) {
21489
21491
  try {
21490
21492
  return realpathSync(path3);
21491
21493
  } catch {
21492
- const parent = dirname6(path3);
21493
- return parent === path3 ? path3 : join14(canonicalPath(parent), basename3(path3));
21494
+ const parent = dirname7(path3);
21495
+ return parent === path3 ? path3 : join15(canonicalPath(parent), basename4(path3));
21494
21496
  }
21495
21497
  }
21496
21498
  function claudeSignInLink(value) {
@@ -21542,11 +21544,11 @@ var init_claude_login_auth = __esm({
21542
21544
  await this.flow?.stopping;
21543
21545
  if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing in.");
21544
21546
  const env = this.options.environment();
21545
- const home = env.HOME || env.USERPROFILE || homedir6();
21547
+ const home = env.HOME || env.USERPROFILE || homedir8();
21546
21548
  if (!isAbsolute3(home) || env.CLAUDE_CONFIG_DIR && !isAbsolute3(env.CLAUDE_CONFIG_DIR)) {
21547
21549
  throw new Error("Use an absolute HOME and CLAUDE_CONFIG_DIR path for this server's Claude provider before signing in.");
21548
21550
  }
21549
- const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR || join14(home, ".claude")));
21551
+ const homeKey = canonicalPath(resolve7(env.CLAUDE_CONFIG_DIR || join15(home, ".claude")));
21550
21552
  if (authenticatingHomes.has(homeKey)) {
21551
21553
  throw new Error("A Claude sign-in is already running for this server account. Finish or cancel it before starting another.");
21552
21554
  }
@@ -21856,10 +21858,10 @@ var init_retry = __esm({
21856
21858
 
21857
21859
  // server/drivers/claude.ts
21858
21860
  import { createHash as createHash5, randomBytes as randomBytes2 } from "node:crypto";
21859
- import { chmodSync as chmodSync3, mkdtempSync as mkdtempSync2, readFileSync as readFileSync6, rmSync as rmSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "node:fs";
21861
+ import { chmodSync as chmodSync3, mkdtempSync as mkdtempSync2, readFileSync as readFileSync6, rmSync as rmSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
21860
21862
  import { createServer as createNetServer } from "node:net";
21861
- import { homedir as homedir7, tmpdir as tmpdir3 } from "node:os";
21862
- import { join as join15, dirname as dirname7, isAbsolute as isAbsolute4, normalize } from "node:path";
21863
+ import { homedir as homedir9, tmpdir as tmpdir3 } from "node:os";
21864
+ import { join as join16, dirname as dirname8, isAbsolute as isAbsolute4, normalize } from "node:path";
21863
21865
  function claudeAuthStatus(cli, env, run3 = execCli) {
21864
21866
  return new Promise((resolve12) => {
21865
21867
  run3(cli, ["auth", "status", "--json"], { timeout: 8e3, maxBuffer: 65536, env }, (_error, stdout2) => {
@@ -21884,9 +21886,9 @@ function claudeAuthStatus(cli, env, run3 = execCli) {
21884
21886
  });
21885
21887
  }
21886
21888
  function resolveClaudeConfigDir(configDir, env = process.env) {
21887
- const home = env.HOME || env.USERPROFILE || homedir7();
21888
- const configured = configDir?.trim() || env.CLAUDE_CONFIG_DIR?.trim() || join15(home, ".claude");
21889
- const expanded = configured === "~" ? home : configured.startsWith("~/") ? join15(home, configured.slice(2)) : configured;
21889
+ const home = env.HOME || env.USERPROFILE || homedir9();
21890
+ const configured = configDir?.trim() || env.CLAUDE_CONFIG_DIR?.trim() || join16(home, ".claude");
21891
+ const expanded = configured === "~" ? home : configured.startsWith("~/") ? join16(home, configured.slice(2)) : configured;
21890
21892
  if (!isAbsolute4(expanded) || /[\p{Cc}\p{Cf}]/u.test(expanded)) {
21891
21893
  throw new Error("claude: configDir must be an absolute path or start with ~/");
21892
21894
  }
@@ -21938,7 +21940,7 @@ function extrasFromUnknown(value) {
21938
21940
  function readClaudeModelCatalog(env = process.env) {
21939
21941
  let settings2 = {};
21940
21942
  try {
21941
- settings2 = JSON.parse(readFileSync6(join15(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
21943
+ settings2 = JSON.parse(readFileSync6(join16(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
21942
21944
  } catch {
21943
21945
  return STATIC_CLAUDE_MODELS;
21944
21946
  }
@@ -21962,7 +21964,7 @@ function readClaudeModelCatalog(env = process.env) {
21962
21964
  function removePrivateTempDir(filePath) {
21963
21965
  if (!filePath) return true;
21964
21966
  try {
21965
- rmSync2(dirname7(filePath), { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
21967
+ rmSync2(dirname8(filePath), { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
21966
21968
  return true;
21967
21969
  } catch {
21968
21970
  return false;
@@ -21988,7 +21990,7 @@ function brokerSocketCandidates(threadId) {
21988
21990
  const base2 = permissionSocketPath(threadId);
21989
21991
  if (process.platform !== "win32") {
21990
21992
  const scope = createHash5("sha256").update(`${DATA_DIR}\0${process.pid}\0${threadId}`).digest("hex").slice(0, 16);
21991
- return [base2, join15(tmpdir3(), `omb-perm-${scope}.sock`)];
21993
+ return [base2, join16(tmpdir3(), `omb-perm-${scope}.sock`)];
21992
21994
  }
21993
21995
  return [
21994
21996
  base2,
@@ -22454,13 +22456,13 @@ var init_claude = __esm({
22454
22456
  allowed.push("mcp__ogb");
22455
22457
  let mcpConfigPath = null;
22456
22458
  if (Object.keys(mcpServers).length) {
22457
- mcpConfigPath = join15(mkdtempSync2(join15(tmpdir3(), "omb-mcp-")), "mcp.json");
22459
+ mcpConfigPath = join16(mkdtempSync2(join16(tmpdir3(), "omb-mcp-")), "mcp.json");
22458
22460
  args.push("--mcp-config", mcpConfigPath);
22459
22461
  args.push("--allowedTools", allowed.join(","));
22460
22462
  }
22461
22463
  const env = environment(turnModel);
22462
22464
  env.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS = "1";
22463
- const cwd = turn.cwd ?? homedir7();
22465
+ const cwd = turn.cwd ?? homedir9();
22464
22466
  const privateFileFlags = /* @__PURE__ */ new Set(["--mcp-config"]);
22465
22467
  const keyArgs = args.filter((a2, i2) => !privateFileFlags.has(a2) && !privateFileFlags.has(args[i2 - 1] ?? ""));
22466
22468
  const argsKey = JSON.stringify({
@@ -22489,7 +22491,7 @@ var init_claude = __esm({
22489
22491
  retryState.delete(threadId);
22490
22492
  if (mcpConfigPath) {
22491
22493
  try {
22492
- rmSync2(dirname7(mcpConfigPath), { recursive: true, force: true });
22494
+ rmSync2(dirname8(mcpConfigPath), { recursive: true, force: true });
22493
22495
  } catch {
22494
22496
  }
22495
22497
  }
@@ -22497,7 +22499,7 @@ var init_claude = __esm({
22497
22499
  }
22498
22500
  if (mcpConfigPath) {
22499
22501
  try {
22500
- rmSync2(dirname7(mcpConfigPath), { recursive: true, force: true });
22502
+ rmSync2(dirname8(mcpConfigPath), { recursive: true, force: true });
22501
22503
  } catch {
22502
22504
  }
22503
22505
  }
@@ -22509,7 +22511,7 @@ var init_claude = __esm({
22509
22511
  broker = void 0;
22510
22512
  if (mcpConfigPath) {
22511
22513
  try {
22512
- rmSync2(dirname7(mcpConfigPath), { recursive: true, force: true });
22514
+ rmSync2(dirname8(mcpConfigPath), { recursive: true, force: true });
22513
22515
  } catch {
22514
22516
  }
22515
22517
  mcpConfigPath = null;
@@ -22522,8 +22524,8 @@ var init_claude = __esm({
22522
22524
  };
22523
22525
  try {
22524
22526
  if (turn.system) {
22525
- systemPromptPath = join15(mkdtempSync2(join15(tmpdir3(), "omb-system-")), "prompt.txt");
22526
- writeFileSync4(systemPromptPath, turn.system, { mode: 384 });
22527
+ systemPromptPath = join16(mkdtempSync2(join16(tmpdir3(), "omb-system-")), "prompt.txt");
22528
+ writeFileSync5(systemPromptPath, turn.system, { mode: 384 });
22527
22529
  args.push("--append-system-prompt-file", systemPromptPath);
22528
22530
  }
22529
22531
  if (socketPath) {
@@ -22563,7 +22565,7 @@ var init_claude = __esm({
22563
22565
  }
22564
22566
  }
22565
22567
  if (mcpConfigPath) {
22566
- writeFileSync4(mcpConfigPath, JSON.stringify({ mcpServers }), { mode: 384 });
22568
+ writeFileSync5(mcpConfigPath, JSON.stringify({ mcpServers }), { mode: 384 });
22567
22569
  }
22568
22570
  if (sessionId) args.push("--resume", sessionId);
22569
22571
  else args.push("--session-id", newSessionId);
@@ -22602,7 +22604,7 @@ var init_claude = __esm({
22602
22604
  session.broker?.pause();
22603
22605
  if (session.mcpConfigPath) {
22604
22606
  try {
22605
- rmSync2(dirname7(session.mcpConfigPath), { recursive: true, force: true });
22607
+ rmSync2(dirname8(session.mcpConfigPath), { recursive: true, force: true });
22606
22608
  } catch {
22607
22609
  }
22608
22610
  session.mcpConfigPath = null;
@@ -22730,7 +22732,7 @@ var init_claude = __esm({
22730
22732
  failedBroker?.close();
22731
22733
  if (session.mcpConfigPath) {
22732
22734
  try {
22733
- rmSync2(dirname7(session.mcpConfigPath), { recursive: true, force: true });
22735
+ rmSync2(dirname8(session.mcpConfigPath), { recursive: true, force: true });
22734
22736
  } catch {
22735
22737
  }
22736
22738
  session.mcpConfigPath = null;
@@ -22799,7 +22801,7 @@ var init_claude = __esm({
22799
22801
  session.broker?.close();
22800
22802
  if (session.mcpConfigPath) {
22801
22803
  try {
22802
- rmSync2(dirname7(session.mcpConfigPath), { recursive: true, force: true });
22804
+ rmSync2(dirname8(session.mcpConfigPath), { recursive: true, force: true });
22803
22805
  } catch {
22804
22806
  }
22805
22807
  }
@@ -22961,8 +22963,8 @@ var init_claude = __esm({
22961
22963
 
22962
22964
  // server/drivers/codex-catalog.ts
22963
22965
  import { readdirSync as readdirSync2, readFileSync as readFileSync7 } from "node:fs";
22964
- import { homedir as homedir8 } from "node:os";
22965
- import { basename as basename4, join as join16 } from "node:path";
22966
+ import { homedir as homedir10 } from "node:os";
22967
+ import { basename as basename5, join as join17 } from "node:path";
22966
22968
  function encodeCodexSelection(provider, model) {
22967
22969
  return `${provider}${SEP}${model}`;
22968
22970
  }
@@ -22977,7 +22979,7 @@ function decodeCodexSelection(id) {
22977
22979
  function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
22978
22980
  return new Promise((resolve12) => {
22979
22981
  const child = spawnCli(cli, ["app-server"], {
22980
- cwd: homedir8(),
22982
+ cwd: homedir10(),
22981
22983
  env,
22982
22984
  stdio: ["pipe", "pipe", "pipe"]
22983
22985
  });
@@ -23078,7 +23080,7 @@ function readCodexAppServerModelCatalog(cli, env, timeoutMs = 8e3) {
23078
23080
  }
23079
23081
  function codexHome(env) {
23080
23082
  if (env.CODEX_HOME) return env.CODEX_HOME;
23081
- return join16(env.HOME || env.USERPROFILE || homedir8(), ".codex");
23083
+ return join17(env.HOME || env.USERPROFILE || homedir10(), ".codex");
23082
23084
  }
23083
23085
  function unquote(raw) {
23084
23086
  let value = raw.trim();
@@ -23164,11 +23166,11 @@ function niceLabel(model, provider, known, named) {
23164
23166
  }
23165
23167
  function collectCatalogNames(home) {
23166
23168
  const named = /* @__PURE__ */ new Map();
23167
- for (const file2 of listDir(join16(home, "model-catalogs"))) {
23169
+ for (const file2 of listDir(join17(home, "model-catalogs"))) {
23168
23170
  if (!file2.endsWith(".json")) continue;
23169
- const provider = basename4(file2, ".json").replace(/-models$/, "");
23171
+ const provider = basename5(file2, ".json").replace(/-models$/, "");
23170
23172
  if (!PROVIDER_ID.test(provider)) continue;
23171
- const raw = readText(join16(home, "model-catalogs", file2));
23173
+ const raw = readText(join17(home, "model-catalogs", file2));
23172
23174
  if (!raw) continue;
23173
23175
  let parsed;
23174
23176
  try {
@@ -23220,7 +23222,7 @@ async function probeProviderModels(provider, env, fetchImpl) {
23220
23222
  async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli) {
23221
23223
  const official = (cli ? await readCodexAppServerModelCatalog(cli, env) : null) ?? STATIC_CODEX_MODELS;
23222
23224
  const home = codexHome(env);
23223
- const mainText = readText(join16(home, "config.toml"));
23225
+ const mainText = readText(join17(home, "config.toml"));
23224
23226
  if (!mainText) return mergeLocalInject(official, env, fetchImpl);
23225
23227
  const main2 = parseCodexToml(mainText);
23226
23228
  const known = new Map(main2.providers.map((provider) => [provider.id, provider]));
@@ -23235,7 +23237,7 @@ async function readCodexModelCatalog(env = process.env, fetchImpl = fetch, cli)
23235
23237
  remember(main2.modelProvider, main2.model);
23236
23238
  for (const file2 of listDir(home)) {
23237
23239
  if (!file2.endsWith(".config.toml")) continue;
23238
- const profile = parseCodexToml(readText(join16(home, file2)) ?? "");
23240
+ const profile = parseCodexToml(readText(join17(home, file2)) ?? "");
23239
23241
  for (const provider of profile.providers) {
23240
23242
  if (!known.has(provider.id)) known.set(provider.id, provider);
23241
23243
  }
@@ -23301,8 +23303,8 @@ var init_codex_catalog = __esm({
23301
23303
 
23302
23304
  // server/drivers/codex-instructions.ts
23303
23305
  import { createHash as createHash6 } from "node:crypto";
23304
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync8 } from "node:fs";
23305
- import { join as join17 } from "node:path";
23306
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync8 } from "node:fs";
23307
+ import { join as join18 } from "node:path";
23306
23308
  function codexDeveloperInstructions(config2, botInstructions) {
23307
23309
  if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) {
23308
23310
  throw new Error("Codex returned no effective configuration; cannot safely update bot instructions.");
@@ -23316,8 +23318,8 @@ function codexDeveloperInstructions(config2, botInstructions) {
23316
23318
  ${configured}` : botInstructions;
23317
23319
  }
23318
23320
  async function syncCodexInstructions(key, nativeThreadId, instructions, resumed, request) {
23319
- const directory = join17(DATA_DIR, "codex-instructions");
23320
- const path3 = join17(directory, `${digest(JSON.stringify([key, nativeThreadId]))}.sha256`);
23321
+ const directory = join18(DATA_DIR, "codex-instructions");
23322
+ const path3 = join18(directory, `${digest(JSON.stringify([key, nativeThreadId]))}.sha256`);
23321
23323
  const fingerprint = digest(instructions);
23322
23324
  let previous;
23323
23325
  try {
@@ -23346,7 +23348,7 @@ async function syncCodexInstructions(key, nativeThreadId, instructions, resumed,
23346
23348
  }
23347
23349
  }
23348
23350
  if (!resumed || previous !== fingerprint) {
23349
- mkdirSync5(directory, { recursive: true });
23351
+ mkdirSync6(directory, { recursive: true });
23350
23352
  writeFileAtomic(path3, fingerprint, { mode: 384 });
23351
23353
  }
23352
23354
  }
@@ -23363,15 +23365,15 @@ var init_codex_instructions = __esm({
23363
23365
  // server/drivers/codex-device-auth.ts
23364
23366
  import { randomUUID as randomUUID9 } from "node:crypto";
23365
23367
  import { realpathSync as realpathSync2 } from "node:fs";
23366
- import { homedir as homedir9 } from "node:os";
23367
- import { basename as basename5, dirname as dirname8, isAbsolute as isAbsolute5, join as join18, resolve as resolve8 } from "node:path";
23368
+ import { homedir as homedir11 } from "node:os";
23369
+ import { basename as basename6, dirname as dirname9, isAbsolute as isAbsolute5, join as join19, resolve as resolve8 } from "node:path";
23368
23370
  import { stripVTControlCharacters as stripVTControlCharacters4 } from "node:util";
23369
23371
  function canonicalPath2(path3) {
23370
23372
  try {
23371
23373
  return realpathSync2(path3);
23372
23374
  } catch {
23373
- const parent = dirname8(path3);
23374
- return parent === path3 ? path3 : join18(canonicalPath2(parent), basename5(path3));
23375
+ const parent = dirname9(path3);
23376
+ return parent === path3 ? path3 : join19(canonicalPath2(parent), basename6(path3));
23375
23377
  }
23376
23378
  }
23377
23379
  function codexDevicePrompt(output) {
@@ -23417,11 +23419,11 @@ var init_codex_device_auth = __esm({
23417
23419
  await this.flow?.stopping;
23418
23420
  if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing in.");
23419
23421
  const env = this.options.environment();
23420
- const home = env.HOME || env.USERPROFILE || homedir9();
23422
+ const home = env.HOME || env.USERPROFILE || homedir11();
23421
23423
  if (!isAbsolute5(home) || env.CODEX_HOME && !isAbsolute5(env.CODEX_HOME)) {
23422
23424
  throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing in.");
23423
23425
  }
23424
- const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join18(home, ".codex")));
23426
+ const homeKey = canonicalPath2(resolve8(env.CODEX_HOME || join19(home, ".codex")));
23425
23427
  if (authenticatingHomes2.has(homeKey)) {
23426
23428
  throw new Error("A ChatGPT sign-in is already running for this server account. Finish or cancel it before starting another.");
23427
23429
  }
@@ -23580,7 +23582,7 @@ var init_codex_device_auth = __esm({
23580
23582
 
23581
23583
  // server/drivers/codex.ts
23582
23584
  import { existsSync as existsSync7 } from "node:fs";
23583
- import { homedir as homedir10 } from "node:os";
23585
+ import { homedir as homedir12 } from "node:os";
23584
23586
  function codexPredatesAstra(version2) {
23585
23587
  const value = version2.trim();
23586
23588
  const match = /\bcodex-cli\s+v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9a-z.-]+)?(?![\d.])\b/i.exec(value) ?? /^v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9a-z.-]+)?$/i.exec(value);
@@ -23983,7 +23985,7 @@ var init_codex = __esm({
23983
23985
  );
23984
23986
  }
23985
23987
  const child = spawnCli(config2.cli, appServerArgs, {
23986
- cwd: turn.cwd ?? homedir10(),
23988
+ cwd: turn.cwd ?? homedir12(),
23987
23989
  env,
23988
23990
  stdio: ["pipe", "pipe", "pipe"]
23989
23991
  });
@@ -24289,7 +24291,7 @@ var init_codex = __esm({
24289
24291
  let effectiveConfig;
24290
24292
  try {
24291
24293
  const configured = await request("config/read", {
24292
- cwd: turn.cwd ?? homedir10(),
24294
+ cwd: turn.cwd ?? homedir12(),
24293
24295
  includeLayers: false
24294
24296
  });
24295
24297
  effectiveConfig = configured?.config;
@@ -24333,7 +24335,7 @@ var init_codex = __esm({
24333
24335
  const selection = decodeCodexSelection(turn.model);
24334
24336
  const startThread = () => request("thread/start", {
24335
24337
  developerInstructions,
24336
- cwd: turn.cwd ?? homedir10(),
24338
+ cwd: turn.cwd ?? homedir12(),
24337
24339
  model: selection.model,
24338
24340
  ...selection.modelProvider ? { modelProvider: selection.modelProvider } : {},
24339
24341
  ...approvalParams.thread,
@@ -24790,12 +24792,12 @@ var init_grok = __esm({
24790
24792
  });
24791
24793
 
24792
24794
  // server/drivers/acp/grok.ts
24793
- import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
24794
- import { homedir as homedir11 } from "node:os";
24795
- import { join as join19 } from "node:path";
24795
+ import { existsSync as existsSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
24796
+ import { homedir as homedir13 } from "node:os";
24797
+ import { join as join20 } from "node:path";
24796
24798
  function grokHome(env) {
24797
24799
  if (env.GROK_HOME) return env.GROK_HOME;
24798
- return join19(env.HOME || env.USERPROFILE || homedir11(), ".grok");
24800
+ return join20(env.HOME || env.USERPROFILE || homedir13(), ".grok");
24799
24801
  }
24800
24802
  function unquote2(raw) {
24801
24803
  const value = raw.trim();
@@ -24805,7 +24807,7 @@ function unquote2(raw) {
24805
24807
  return value;
24806
24808
  }
24807
24809
  function readGrokModelCatalog(env = process.env) {
24808
- const path3 = join19(grokHome(env), "config.toml");
24810
+ const path3 = join20(grokHome(env), "config.toml");
24809
24811
  let text2 = "";
24810
24812
  try {
24811
24813
  text2 = readFileSync9(path3, "utf8");
@@ -24878,7 +24880,7 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
24878
24880
  if (!inject) return modelId;
24879
24881
  const host = localHost(inject.host);
24880
24882
  if (!host) return modelId;
24881
- const path3 = join19(grokHome(env), "config.toml");
24883
+ const path3 = join20(grokHome(env), "config.toml");
24882
24884
  let text2 = "";
24883
24885
  try {
24884
24886
  text2 = readFileSync9(path3, "utf8");
@@ -24932,7 +24934,7 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
24932
24934
  const next = text2 && !text2.endsWith("\n") ? `${text2}
24933
24935
 
24934
24936
  ${block2}` : `${text2}${text2 ? "\n" : ""}${block2}`;
24935
- writeFileSync5(path3, next);
24937
+ writeFileSync6(path3, next);
24936
24938
  return slug;
24937
24939
  }
24938
24940
  var STATIC_GROK_MODELS, SLUG, support2, GrokAgentDriver;
@@ -25013,7 +25015,7 @@ var init_grok2 = __esm({
25013
25015
  // an unauthenticated CLI is a user action, not something to paper over.
25014
25016
  pickAuthMethod: (methods) => methods.some((m) => m.id === "cached_token") ? "cached_token" : null,
25015
25017
  authFailure: "fail",
25016
- isAuthenticated: () => existsSync8(join19(homedir11(), ".grok", "auth.json")),
25018
+ isAuthenticated: () => existsSync8(join20(homedir13(), ".grok", "auth.json")),
25017
25019
  // `--append-system-prompt`/`--rules` are accepted by the CLI but do NOT
25018
25020
  // reach the agent-stdio system prompt (verified against 1.0.0), so the
25019
25021
  // persona is prepended codex-style.
@@ -25027,11 +25029,11 @@ ${turn.text}` : turn.text
25027
25029
 
25028
25030
  // server/drivers/acp/gemini.ts
25029
25031
  import { existsSync as existsSync9, readFileSync as readFileSync10 } from "node:fs";
25030
- import { homedir as homedir12 } from "node:os";
25031
- import { join as join20 } from "node:path";
25032
+ import { homedir as homedir14 } from "node:os";
25033
+ import { join as join21 } from "node:path";
25032
25034
  function liveOauthCredential() {
25033
25035
  try {
25034
- const creds = JSON.parse(readFileSync10(join20(homedir12(), ".gemini", "oauth_creds.json"), "utf8"));
25036
+ const creds = JSON.parse(readFileSync10(join21(homedir14(), ".gemini", "oauth_creds.json"), "utf8"));
25035
25037
  if (typeof creds.access_token !== "string" || creds.access_token.trim().length === 0) return false;
25036
25038
  const hasRefresh = typeof creds.refresh_token === "string" && creds.refresh_token.trim().length > 0;
25037
25039
  if (creds.expiry_date !== void 0) {
@@ -25044,7 +25046,7 @@ function liveOauthCredential() {
25044
25046
  }
25045
25047
  }
25046
25048
  function geminiIsAuthenticated(env) {
25047
- return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync9(join20(homedir12(), ".gemini", "oauth_creds.json")) && liveOauthCredential();
25049
+ return nonBlank(env.GEMINI_API_KEY) || nonBlank(env.GOOGLE_API_KEY) || existsSync9(join21(homedir14(), ".gemini", "oauth_creds.json")) && liveOauthCredential();
25048
25050
  }
25049
25051
  var AUTH_PREFERENCE, nonBlank, support3, GeminiAgentDriver;
25050
25052
  var init_gemini = __esm({
@@ -25086,17 +25088,17 @@ ${turn.text}` : turn.text
25086
25088
  });
25087
25089
 
25088
25090
  // server/drivers/acp/kimi.ts
25089
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
25090
- import { homedir as homedir13 } from "node:os";
25091
- import { join as join21 } from "node:path";
25091
+ import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "node:fs";
25092
+ import { homedir as homedir15 } from "node:os";
25093
+ import { join as join22 } from "node:path";
25092
25094
  function isLocalInjectAlias(slug) {
25093
25095
  return LOCAL_PROVIDER_PREFIXES.some((prefix) => slug.startsWith(prefix));
25094
25096
  }
25095
25097
  function kimiDataRoot(env) {
25096
- return env.KIMI_CODE_HOME || join21(env.HOME || env.USERPROFILE || homedir13(), ".kimi-code");
25098
+ return env.KIMI_CODE_HOME || join22(env.HOME || env.USERPROFILE || homedir15(), ".kimi-code");
25097
25099
  }
25098
25100
  function credentialsPath(env) {
25099
- return join21(kimiDataRoot(env), "credentials", "kimi-code.json");
25101
+ return join22(kimiDataRoot(env), "credentials", "kimi-code.json");
25100
25102
  }
25101
25103
  function quoteToml2(value) {
25102
25104
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
@@ -25383,8 +25385,8 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
25383
25385
  if (!host) return modelId;
25384
25386
  const alias = `${inject.host}/${inject.model.replace(/\//g, "-")}`;
25385
25387
  const dataRoot = kimiDataRoot(env);
25386
- mkdirSync6(dataRoot, { recursive: true });
25387
- const path3 = join21(dataRoot, "config.toml");
25388
+ mkdirSync7(dataRoot, { recursive: true });
25389
+ const path3 = join22(dataRoot, "config.toml");
25388
25390
  let text2 = "";
25389
25391
  try {
25390
25392
  text2 = readFileSync11(path3, "utf8");
@@ -25425,9 +25427,9 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
25425
25427
 
25426
25428
  ` : text2 ? `${text2}
25427
25429
  ` : "";
25428
- writeFileSync6(path3, `${prefix}${blocks.join("\n")}`);
25430
+ writeFileSync7(path3, `${prefix}${blocks.join("\n")}`);
25429
25431
  } else if (text2 !== original) {
25430
- writeFileSync6(path3, text2);
25432
+ writeFileSync7(path3, text2);
25431
25433
  }
25432
25434
  return alias;
25433
25435
  }
@@ -25449,7 +25451,7 @@ function readKimiModelCatalog(env) {
25449
25451
  const dataRoot = kimiDataRoot(env);
25450
25452
  let text2 = "";
25451
25453
  try {
25452
- text2 = readFileSync11(join21(dataRoot, "config.toml"), "utf8");
25454
+ text2 = readFileSync11(join22(dataRoot, "config.toml"), "utf8");
25453
25455
  } catch {
25454
25456
  return STATIC_KIMI_MODELS;
25455
25457
  }
@@ -25568,19 +25570,19 @@ ${turn.text}` : turn.text
25568
25570
  });
25569
25571
 
25570
25572
  // server/drivers/acp/droid.ts
25571
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "node:fs";
25572
- import { homedir as homedir14 } from "node:os";
25573
- import { join as join22 } from "node:path";
25573
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "node:fs";
25574
+ import { homedir as homedir16 } from "node:os";
25575
+ import { join as join23 } from "node:path";
25574
25576
  function authFilePaths(env) {
25575
- const home = env.FACTORY_HOME_OVERRIDE || env.HOME || homedir14();
25576
- return AUTH_FILES.map((name) => join22(home, ".factory", name));
25577
+ const home = env.FACTORY_HOME_OVERRIDE || env.HOME || homedir16();
25578
+ return AUTH_FILES.map((name) => join23(home, ".factory", name));
25577
25579
  }
25578
25580
  function droidInjectId(host, model) {
25579
25581
  const safe = `${host}-${model}`.replace(/[^a-zA-Z0-9._+-]+/g, "-").replace(/-+/g, "-");
25580
25582
  return `${INJECT_ID_PREFIX}${safe}`;
25581
25583
  }
25582
25584
  function factoryHome(env) {
25583
- return env.FACTORY_HOME_OVERRIDE || env.HOME || env.USERPROFILE || homedir14();
25585
+ return env.FACTORY_HOME_OVERRIDE || env.HOME || env.USERPROFILE || homedir16();
25584
25586
  }
25585
25587
  function ensureDroidInjectModel(modelId, env = process.env) {
25586
25588
  const inject = decodeInjectId(modelId);
@@ -25588,9 +25590,9 @@ function ensureDroidInjectModel(modelId, env = process.env) {
25588
25590
  const host = localHost(inject.host);
25589
25591
  if (!host) return modelId;
25590
25592
  const id = droidInjectId(inject.host, inject.model);
25591
- const dir = join22(factoryHome(env), ".factory");
25592
- mkdirSync7(dir, { recursive: true });
25593
- const path3 = join22(dir, "settings.json");
25593
+ const dir = join23(factoryHome(env), ".factory");
25594
+ mkdirSync8(dir, { recursive: true });
25595
+ const path3 = join23(dir, "settings.json");
25594
25596
  let settings2 = {};
25595
25597
  try {
25596
25598
  settings2 = JSON.parse(readFileSync12(path3, "utf8"));
@@ -25605,7 +25607,7 @@ function ensureDroidInjectModel(modelId, env = process.env) {
25605
25607
  if (!match.id) {
25606
25608
  match.id = id;
25607
25609
  settings2.customModels = custom2;
25608
- writeFileSync7(path3, `${JSON.stringify(settings2, null, 2)}
25610
+ writeFileSync8(path3, `${JSON.stringify(settings2, null, 2)}
25609
25611
  `);
25610
25612
  }
25611
25613
  return match.id;
@@ -25619,7 +25621,7 @@ function ensureDroidInjectModel(modelId, env = process.env) {
25619
25621
  provider: "generic-chat-completion-api"
25620
25622
  });
25621
25623
  settings2.customModels = custom2;
25622
- writeFileSync7(path3, `${JSON.stringify(settings2, null, 2)}
25624
+ writeFileSync8(path3, `${JSON.stringify(settings2, null, 2)}
25623
25625
  `);
25624
25626
  return id;
25625
25627
  }
@@ -25630,7 +25632,7 @@ function applyDroidLocalAuthEnv(env, modelId) {
25630
25632
  env.FACTORY_API_KEY = "openmausbot-local";
25631
25633
  }
25632
25634
  function readSettings(env) {
25633
- return JSON.parse(readFileSync12(join22(factoryHome(env), ".factory", "settings.json"), "utf8"));
25635
+ return JSON.parse(readFileSync12(join23(factoryHome(env), ".factory", "settings.json"), "utf8"));
25634
25636
  }
25635
25637
  async function resolveModels(env) {
25636
25638
  let catalog = MODELS3;
@@ -26020,9 +26022,9 @@ ${turn.text}` : turn.text
26020
26022
  });
26021
26023
 
26022
26024
  // server/drivers/acp/opencode-go.ts
26023
- import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "node:fs";
26024
- import { homedir as homedir15 } from "node:os";
26025
- import { join as join23 } from "node:path";
26025
+ import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
26026
+ import { homedir as homedir17 } from "node:os";
26027
+ import { join as join24 } from "node:path";
26026
26028
  function labelForModel(id) {
26027
26029
  return id.split(/[-_.]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
26028
26030
  }
@@ -26122,8 +26124,8 @@ async function discoverOpenCodeModels(environment, cli = "opencode") {
26122
26124
  }
26123
26125
  }
26124
26126
  function opencodeConfigDir(env) {
26125
- const home = env.HOME || env.USERPROFILE || homedir15();
26126
- return join23(env.XDG_CONFIG_HOME || join23(home, ".config"), "opencode");
26127
+ const home = env.HOME || env.USERPROFILE || homedir17();
26128
+ return join24(env.XDG_CONFIG_HOME || join24(home, ".config"), "opencode");
26127
26129
  }
26128
26130
  function ensureOpenCodeInjectModel(modelId, env = process.env) {
26129
26131
  const inject = decodeInjectId(modelId);
@@ -26132,8 +26134,8 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
26132
26134
  if (!host) return modelId;
26133
26135
  const native = `${inject.host}/${inject.model}`;
26134
26136
  const dir = opencodeConfigDir(env);
26135
- mkdirSync8(dir, { recursive: true });
26136
- const path3 = join23(dir, "opencode.json");
26137
+ mkdirSync9(dir, { recursive: true });
26138
+ const path3 = join24(dir, "opencode.json");
26137
26139
  let config2 = { $schema: "https://opencode.ai/config.json" };
26138
26140
  if (existsSync12(path3)) {
26139
26141
  try {
@@ -26164,17 +26166,17 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
26164
26166
  models
26165
26167
  };
26166
26168
  config2.provider = providers;
26167
- writeFileSync8(path3, `${JSON.stringify(config2, null, 2)}
26169
+ writeFileSync9(path3, `${JSON.stringify(config2, null, 2)}
26168
26170
  `);
26169
26171
  return native;
26170
26172
  }
26171
26173
  function storedAuthPaths(env) {
26172
- const home = env.HOME || env.USERPROFILE || homedir15();
26174
+ const home = env.HOME || env.USERPROFILE || homedir17();
26173
26175
  const roots = [
26174
- env.XDG_DATA_HOME || join23(home, ".local", "share"),
26175
- process.platform === "darwin" ? join23(home, "Library", "Application Support") : process.platform === "win32" ? env.LOCALAPPDATA || join23(home, "AppData", "Local") : ""
26176
+ env.XDG_DATA_HOME || join24(home, ".local", "share"),
26177
+ process.platform === "darwin" ? join24(home, "Library", "Application Support") : process.platform === "win32" ? env.LOCALAPPDATA || join24(home, "AppData", "Local") : ""
26176
26178
  ].filter(Boolean);
26177
- return [...new Set(roots)].map((root) => join23(root, "opencode", "auth.json"));
26179
+ return [...new Set(roots)].map((root) => join24(root, "opencode", "auth.json"));
26178
26180
  }
26179
26181
  function usableAuthEntry(parsed) {
26180
26182
  return Object.values(parsed).some((auth) => {
@@ -26304,11 +26306,11 @@ ${turn.text}` : turn.text
26304
26306
  });
26305
26307
 
26306
26308
  // server/drivers/acp/qwen.ts
26307
- import { chmodSync as chmodSync4, existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "node:fs";
26308
- import { homedir as homedir16 } from "node:os";
26309
- import { join as join24 } from "node:path";
26309
+ import { chmodSync as chmodSync4, existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "node:fs";
26310
+ import { homedir as homedir18 } from "node:os";
26311
+ import { join as join25 } from "node:path";
26310
26312
  function qwenHome(env) {
26311
- return join24(env.HOME || env.USERPROFILE || homedir16(), ".qwen");
26313
+ return join25(env.HOME || env.USERPROFILE || homedir18(), ".qwen");
26312
26314
  }
26313
26315
  function envKeyFor(hostId) {
26314
26316
  return `OPENMAUSBOT_QWEN_${hostId.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_API_KEY`;
@@ -26319,8 +26321,8 @@ function ensureQwenInjectModel(modelId, env = process.env) {
26319
26321
  const host = localHost(inject.host);
26320
26322
  if (!host) return modelId;
26321
26323
  const dir = qwenHome(env);
26322
- mkdirSync9(dir, { recursive: true });
26323
- const path3 = join24(dir, "settings.json");
26324
+ mkdirSync10(dir, { recursive: true });
26325
+ const path3 = join25(dir, "settings.json");
26324
26326
  let settings2 = {};
26325
26327
  if (existsSync13(path3)) {
26326
26328
  try {
@@ -26348,7 +26350,7 @@ function ensureQwenInjectModel(modelId, env = process.env) {
26348
26350
  providers.openai = openai;
26349
26351
  settings2.modelProviders = providers;
26350
26352
  }
26351
- writeFileSync9(path3, `${JSON.stringify(settings2, null, 2)}
26353
+ writeFileSync10(path3, `${JSON.stringify(settings2, null, 2)}
26352
26354
  `, { mode: 384 });
26353
26355
  try {
26354
26356
  chmodSync4(path3, 384);
@@ -33285,9 +33287,9 @@ var init_browser = __esm({
33285
33287
 
33286
33288
  // server/drivers/acp/hermes.ts
33287
33289
  import { spawn as spawn5 } from "node:child_process";
33288
- import { mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "node:fs";
33289
- import { homedir as homedir17 } from "node:os";
33290
- import { join as join25 } from "node:path";
33290
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "node:fs";
33291
+ import { homedir as homedir19 } from "node:os";
33292
+ import { join as join26 } from "node:path";
33291
33293
  function bindHermesScreenshotCompat(env, modelId) {
33292
33294
  delete env[HERMES_OPENMAUS_SCREENSHOT_COMPAT];
33293
33295
  delete env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL];
@@ -33297,7 +33299,7 @@ function bindHermesScreenshotCompat(env, modelId) {
33297
33299
  env[HERMES_OPENMAUS_SCREENSHOT_COMPAT_MODEL] = inject.model;
33298
33300
  }
33299
33301
  function hermesHome(env) {
33300
- return env.HERMES_HOME || join25(env.HOME || env.USERPROFILE || homedir17(), ".hermes");
33302
+ return env.HERMES_HOME || join26(env.HOME || env.USERPROFILE || homedir19(), ".hermes");
33301
33303
  }
33302
33304
  function quoteYaml(value) {
33303
33305
  if (/^[\w./:+-]+$/.test(value)) return value;
@@ -33338,8 +33340,8 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
33338
33340
  const host = localHost(inject.host);
33339
33341
  if (!host) return modelId;
33340
33342
  const dir = hermesHome(env);
33341
- mkdirSync10(dir, { recursive: true });
33342
- const path3 = join25(dir, "config.yaml");
33343
+ mkdirSync11(dir, { recursive: true });
33344
+ const path3 = join26(dir, "config.yaml");
33343
33345
  let text2 = "";
33344
33346
  try {
33345
33347
  text2 = readFileSync15(path3, "utf8");
@@ -33347,7 +33349,7 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
33347
33349
  text2 = "";
33348
33350
  }
33349
33351
  const next = upsertHermesProvider(text2, inject.host, host.baseUrl, hostApiKey(host, env));
33350
- if (next !== text2) writeFileSync10(path3, next);
33352
+ if (next !== text2) writeFileSync11(path3, next);
33351
33353
  return hermesAcpModelId(modelId) ?? modelId;
33352
33354
  }
33353
33355
  function hermesAcpModelId(modelId) {
@@ -33410,13 +33412,13 @@ function hermesConfiguredModel(env = process.env) {
33410
33412
  const dir = hermesHome(env);
33411
33413
  let secrets = "";
33412
33414
  try {
33413
- secrets = readFileSync15(join25(dir, ".env"), "utf8");
33415
+ secrets = readFileSync15(join26(dir, ".env"), "utf8");
33414
33416
  } catch {
33415
33417
  }
33416
33418
  const hasHostedProviderKey = HERMES_HOSTED_PROVIDER_KEYS.some((name) => nonEmptyDotenvValue(secrets, name));
33417
33419
  let configuredDefault = null;
33418
33420
  try {
33419
- configuredDefault = hermesConfigDefault(readFileSync15(join25(dir, "config.yaml"), "utf8"));
33421
+ configuredDefault = hermesConfigDefault(readFileSync15(join26(dir, "config.yaml"), "utf8"));
33420
33422
  } catch {
33421
33423
  }
33422
33424
  const configuredProvider = configuredDefault?.provider.toLowerCase() ?? "";
@@ -33501,7 +33503,7 @@ async function fetchHermesAcpModels(cli, env) {
33501
33503
  }
33502
33504
  if (msg?.id === initId) {
33503
33505
  if (!msg.result) return done([]);
33504
- sessionId = send("session/new", { cwd: env.HOME || env.USERPROFILE || homedir17(), mcpServers: [] });
33506
+ sessionId = send("session/new", { cwd: env.HOME || env.USERPROFILE || homedir19(), mcpServers: [] });
33505
33507
  } else if (sessionId && msg?.id === sessionId) {
33506
33508
  const list = Array.isArray(msg.result?.models?.availableModels) ? msg.result.models.availableModels : [];
33507
33509
  done(
@@ -33736,9 +33738,9 @@ var init_openai_compat = __esm({
33736
33738
  });
33737
33739
 
33738
33740
  // server/drivers/pi.ts
33739
- import { chmodSync as chmodSync5, existsSync as existsSync14, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, rmSync as rmSync3, writeFileSync as writeFileSync11 } from "node:fs";
33740
- import { homedir as homedir18, tmpdir as tmpdir4 } from "node:os";
33741
- import { join as join26 } from "node:path";
33741
+ import { chmodSync as chmodSync5, existsSync as existsSync14, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readFileSync as readFileSync16, rmSync as rmSync3, writeFileSync as writeFileSync12 } from "node:fs";
33742
+ import { homedir as homedir20, tmpdir as tmpdir4 } from "node:os";
33743
+ import { join as join27 } from "node:path";
33742
33744
  function readPiPromptImages(turn) {
33743
33745
  return (turn.images ?? []).map((image) => ({
33744
33746
  type: "image",
@@ -33845,7 +33847,7 @@ async function applyPiLocalCatalog(catalog, env = process.env, fetchImpl = fetch
33845
33847
  return preferPiInjectRows(await mergeLocalInject(catalog, env, fetchImpl));
33846
33848
  }
33847
33849
  function piAgentDir(env) {
33848
- return join26(env.HOME || env.USERPROFILE || homedir18(), ".pi", "agent");
33850
+ return join27(env.HOME || env.USERPROFILE || homedir20(), ".pi", "agent");
33849
33851
  }
33850
33852
  function ensurePiInjectModel(modelId, env = process.env) {
33851
33853
  const split = splitPiModel(modelId);
@@ -33855,8 +33857,8 @@ function ensurePiInjectModel(modelId, env = process.env) {
33855
33857
  const host = localHost(inject.host);
33856
33858
  if (!host) return split;
33857
33859
  const dir = piAgentDir(env);
33858
- mkdirSync11(dir, { recursive: true });
33859
- const path3 = join26(dir, "models.json");
33860
+ mkdirSync12(dir, { recursive: true });
33861
+ const path3 = join27(dir, "models.json");
33860
33862
  let root = { providers: {} };
33861
33863
  if (existsSync14(path3)) {
33862
33864
  try {
@@ -33901,7 +33903,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
33901
33903
  existing.models = models;
33902
33904
  providers[inject.host] = existing;
33903
33905
  root.providers = providers;
33904
- writeFileSync11(path3, `${JSON.stringify(root, null, 2)}
33906
+ writeFileSync12(path3, `${JSON.stringify(root, null, 2)}
33905
33907
  `, { mode: 384 });
33906
33908
  try {
33907
33909
  chmodSync5(path3, 384);
@@ -33910,7 +33912,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
33910
33912
  return split;
33911
33913
  }
33912
33914
  function readPiDefaultModel(env) {
33913
- const home = env.HOME || env.USERPROFILE || homedir18();
33915
+ const home = env.HOME || env.USERPROFILE || homedir20();
33914
33916
  try {
33915
33917
  const s = JSON.parse(readFileSync16(`${home}/.pi/agent/settings.json`, "utf8"));
33916
33918
  return s.defaultProvider && s.defaultModel ? `${s.defaultProvider}/${s.defaultModel}` : "";
@@ -34058,9 +34060,9 @@ var init_pi = __esm({
34058
34060
  const mcpServers = buildMcpServers(turn);
34059
34061
  let mcpTempDir = null;
34060
34062
  if (mcpServers) {
34061
- mcpTempDir = mkdtempSync3(join26(tmpdir4(), "omb-pi-mcp-"));
34063
+ mcpTempDir = mkdtempSync3(join27(tmpdir4(), "omb-pi-mcp-"));
34062
34064
  try {
34063
- writeFileSync11(join26(mcpTempDir, "mcp.json"), JSON.stringify({ mcpServers }), { mode: 384 });
34065
+ writeFileSync12(join27(mcpTempDir, "mcp.json"), JSON.stringify({ mcpServers }), { mode: 384 });
34064
34066
  } catch (err) {
34065
34067
  try {
34066
34068
  rmSync3(mcpTempDir, { recursive: true, force: true });
@@ -34078,7 +34080,7 @@ var init_pi = __esm({
34078
34080
  env: piEnvironment({
34079
34081
  ...process.env,
34080
34082
  ...input.environment,
34081
- ...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG: join26(mcpTempDir, "mcp.json") } : {}
34083
+ ...mcpServers && mcpTempDir ? { OMB_MCP_CONFIG: join27(mcpTempDir, "mcp.json") } : {}
34082
34084
  })
34083
34085
  });
34084
34086
  } catch (err) {
@@ -34416,15 +34418,15 @@ ${turn.text}` : turn.text;
34416
34418
 
34417
34419
  // server/drivers/minimax.ts
34418
34420
  import { readFileSync as readFileSync17 } from "node:fs";
34419
- import { homedir as homedir19 } from "node:os";
34420
- import { join as join27 } from "node:path";
34421
+ import { homedir as homedir21 } from "node:os";
34422
+ import { join as join28 } from "node:path";
34421
34423
  function normalizedApiUrl(value) {
34422
34424
  const root = value.trim().replace(/\/+$/, "");
34423
34425
  return root.endsWith("/v1") ? root : `${root}/v1`;
34424
34426
  }
34425
- function loadLocalMiniMaxConfig(home = homedir19()) {
34427
+ function loadLocalMiniMaxConfig(home = homedir21()) {
34426
34428
  try {
34427
- const raw = localConfigSchema.parse(JSON.parse(readFileSync17(join27(home, ".mmx", "config.json"), "utf8")));
34429
+ const raw = localConfigSchema.parse(JSON.parse(readFileSync17(join28(home, ".mmx", "config.json"), "utf8")));
34428
34430
  const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL2;
34429
34431
  return {
34430
34432
  apiKey: raw.api_key?.trim() ?? "",
@@ -34692,7 +34694,7 @@ __export(cli_setup_exports, {
34692
34694
  });
34693
34695
  import { spawn as spawn6 } from "node:child_process";
34694
34696
  import { readFileSync as readFileSync18 } from "node:fs";
34695
- import { join as join28, resolve as resolve9 } from "node:path";
34697
+ import { join as join29, resolve as resolve9 } from "node:path";
34696
34698
  import { randomUUID as randomUUID10 } from "node:crypto";
34697
34699
  async function inspect(id, entry) {
34698
34700
  const registry2 = new ProviderRegistry(BUILT_IN_DRIVERS);
@@ -34729,7 +34731,7 @@ function assertDataDir(dataDir) {
34729
34731
  }
34730
34732
  function checkStoredConfig(dataDir) {
34731
34733
  try {
34732
- parseStoredConfig(JSON.parse(readFileSync18(join28(dataDir, "config.json"), "utf8")));
34734
+ parseStoredConfig(JSON.parse(readFileSync18(join29(dataDir, "config.json"), "utf8")));
34733
34735
  } catch (error51) {
34734
34736
  if (error51.code === "ENOENT") return;
34735
34737
  throw new Error("Existing config.json could not be read or validated. Setup has not changed it; repair it before continuing.");
@@ -35007,7 +35009,7 @@ var init_browser_engine_release = __esm({
35007
35009
  });
35008
35010
 
35009
35011
  // server/browser-bundle-release.ts
35010
- import { join as join29 } from "node:path";
35012
+ import { join as join30 } from "node:path";
35011
35013
  function browserBundleSpec(target) {
35012
35014
  if (!Object.hasOwn(CHROME_ASSETS, target)) throw new Error(`Unsupported desktop browser target: ${target}`);
35013
35015
  const pinned = CHROME_ASSETS[target];
@@ -35043,10 +35045,10 @@ function browserBundlePaths(bundleDirectory, target) {
35043
35045
  const spec = browserBundleSpec(target);
35044
35046
  return {
35045
35047
  directory: bundleDirectory,
35046
- manifest: join29(bundleDirectory, "manifest.json"),
35047
- engine: join29(bundleDirectory, spec.engine.executable),
35048
- chrome: join29(bundleDirectory, spec.chrome.executable),
35049
- licenses: join29(bundleDirectory, "licenses")
35048
+ manifest: join30(bundleDirectory, "manifest.json"),
35049
+ engine: join30(bundleDirectory, spec.engine.executable),
35050
+ chrome: join30(bundleDirectory, spec.chrome.executable),
35051
+ licenses: join30(bundleDirectory, "licenses")
35050
35052
  };
35051
35053
  }
35052
35054
  var CHROME_VERSION, CHROME_ASSETS;
@@ -35101,9 +35103,9 @@ __export(browser_engine_exports, {
35101
35103
  });
35102
35104
  import { spawn as spawn7 } from "node:child_process";
35103
35105
  import { createHash as createHash7, randomBytes as randomBytes3, randomUUID as randomUUID11 } from "node:crypto";
35104
- import { chmodSync as chmodSync6, constants as constants2, copyFileSync, existsSync as existsSync15, mkdirSync as mkdirSync12, readFileSync as readFileSync19, readdirSync as readdirSync3, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync12 } from "node:fs";
35105
- import { homedir as homedir20, tmpdir as tmpdir5 } from "node:os";
35106
- import { delimiter as delimiter6, dirname as dirname9, join as join30, resolve as resolve10 } from "node:path";
35106
+ import { chmodSync as chmodSync6, constants as constants2, copyFileSync, existsSync as existsSync15, mkdirSync as mkdirSync13, readFileSync as readFileSync19, readdirSync as readdirSync3, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync13 } from "node:fs";
35107
+ import { homedir as homedir22, tmpdir as tmpdir5 } from "node:os";
35108
+ import { delimiter as delimiter6, dirname as dirname10, join as join31, resolve as resolve10 } from "node:path";
35107
35109
  function browserRestoreKey(session) {
35108
35110
  if (!/^[A-Za-z0-9_-]{1,96}$/.test(session)) throw new Error("Invalid browser session.");
35109
35111
  return `omb-${createHash7("sha256").update(session).digest("hex")}`;
@@ -35111,18 +35113,18 @@ function browserRestoreKey(session) {
35111
35113
  function browserSessionsDirectory(env) {
35112
35114
  const namespace = env.AGENT_BROWSER_NAMESPACE;
35113
35115
  if (namespace && !/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/.test(namespace)) throw new Error("Invalid browser namespace.");
35114
- const home = (process.platform === "win32" ? env.USERPROFILE : env.HOME) || homedir20();
35115
- const stateRoot = namespace ? join30(home, ".agent-browser", "namespaces", namespace, "state") : join30(home, ".agent-browser");
35116
- return join30(stateRoot, "sessions");
35116
+ const home = (process.platform === "win32" ? env.USERPROFILE : env.HOME) || homedir22();
35117
+ const stateRoot = namespace ? join31(home, ".agent-browser", "namespaces", namespace, "state") : join31(home, ".agent-browser");
35118
+ return join31(stateRoot, "sessions");
35117
35119
  }
35118
35120
  function managedBrowserConfigPath(env) {
35119
- return join30(browserSessionsDirectory(env), "..", "omb-managed-config.json");
35121
+ return join31(browserSessionsDirectory(env), "..", "omb-managed-config.json");
35120
35122
  }
35121
35123
  function ensureManagedBrowserConfig(env) {
35122
35124
  const path3 = managedBrowserConfigPath(env);
35123
- mkdirSync12(dirname9(path3), { recursive: true, mode: 448 });
35125
+ mkdirSync13(dirname10(path3), { recursive: true, mode: 448 });
35124
35126
  try {
35125
- writeFileSync12(path3, "{}\n", { flag: "wx", mode: 384 });
35127
+ writeFileSync13(path3, "{}\n", { flag: "wx", mode: 384 });
35126
35128
  } catch (error51) {
35127
35129
  if (error51.code !== "EEXIST") throw error51;
35128
35130
  if (readFileSync19(path3, "utf8").trim() !== "{}") throw new Error("The managed browser configuration was changed. Restore it to an empty JSON object before connecting.");
@@ -35203,11 +35205,11 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
35203
35205
  ensureManagedBrowserConfig(env);
35204
35206
  if (options.persistent === false) return;
35205
35207
  const directory = browserSessionsDirectory(env);
35206
- const key = join30(directory, session);
35208
+ const key = join31(directory, session);
35207
35209
  let pending = preparedBrowserSessions.get(key);
35208
35210
  if (!pending) {
35209
35211
  pending = (async () => {
35210
- const targetBase = join30(directory, `${restoreKey}-${session}`);
35212
+ const targetBase = join31(directory, `${restoreKey}-${session}`);
35211
35213
  const hasTarget = () => existsSync15(`${targetBase}.json.enc`) || existsSync15(`${targetBase}.json`);
35212
35214
  if (hasTarget()) return;
35213
35215
  check2();
@@ -35217,7 +35219,7 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
35217
35219
  check2();
35218
35220
  if (hasTarget()) return;
35219
35221
  const candidates = [".json.enc", ".json"].flatMap((suffix) => {
35220
- const path3 = join30(directory, `${session}-${session}${suffix}`);
35222
+ const path3 = join31(directory, `${session}-${session}${suffix}`);
35221
35223
  try {
35222
35224
  const stat4 = statSync3(path3);
35223
35225
  if (!stat4.isFile()) throw new Error("Saved browser state is not a file.");
@@ -35252,7 +35254,7 @@ function isMusl(platform = process.platform, exists = existsSync15) {
35252
35254
  }
35253
35255
  function pinnedBinaryPath(dataDir = DATA_DIR, platform = process.platform, arch = process.arch) {
35254
35256
  const version2 = agentBrowserReleaseVersion(resolveAgentBrowserReleaseAsset(platform, arch));
35255
- return join30(dataDir, ENGINE_DIR, version2, executableName(platform));
35257
+ return join31(dataDir, ENGINE_DIR, version2, executableName(platform));
35256
35258
  }
35257
35259
  function onPath(env, platform, exists) {
35258
35260
  const pathValue = platform === "win32" ? Object.entries(env).findLast(([key]) => key.toUpperCase() === "PATH")?.[1] : env.PATH;
@@ -35268,7 +35270,7 @@ function packagedBrowser(options) {
35268
35270
  const resources = (options.env ?? process.env).OMB_RESOURCES_PATH;
35269
35271
  if (!resources) return null;
35270
35272
  try {
35271
- return browserBundlePaths(join30(resolve10(resources), "browser-engine"), `${options.platform ?? process.platform}-${options.arch ?? process.arch}`);
35273
+ return browserBundlePaths(join31(resolve10(resources), "browser-engine"), `${options.platform ?? process.platform}-${options.arch ?? process.arch}`);
35272
35274
  } catch {
35273
35275
  return null;
35274
35276
  }
@@ -35293,8 +35295,8 @@ async function installAgentBrowserBinary(options = {}) {
35293
35295
  const asset = options.asset ?? resolveAgentBrowserReleaseAsset(platform, options.arch ?? process.arch, options.musl ?? isMusl(platform));
35294
35296
  if (!asset) throw new Error(`agent-browser publishes no build for ${platform}-${options.arch ?? process.arch}.`);
35295
35297
  const destination = pinnedBinaryPath(options.dataDir, platform, options.arch);
35296
- const directory = join30(destination, "..");
35297
- mkdirSync12(directory, { recursive: true, mode: 448 });
35298
+ const directory = join31(destination, "..");
35299
+ mkdirSync13(directory, { recursive: true, mode: 448 });
35298
35300
  const url2 = agentBrowserReleaseUrl(asset);
35299
35301
  options.log?.(`downloading agent-browser ${agentBrowserReleaseVersion(asset)} (${Math.round(asset.bytes / 1024 / 1024)} MB, digest pinned)`);
35300
35302
  const controller = new AbortController();
@@ -35312,7 +35314,7 @@ async function installAgentBrowserBinary(options = {}) {
35312
35314
  const digest2 = createHash7("sha256").update(body).digest("hex");
35313
35315
  if (digest2 !== asset.sha256) throw new Error("the agent-browser download failed its SHA-256 check; nothing was installed");
35314
35316
  const staging = `${destination}.${randomBytes3(6).toString("hex")}.part`;
35315
- writeFileSync12(staging, body, { mode: 493 });
35317
+ writeFileSync13(staging, body, { mode: 493 });
35316
35318
  if (platform !== "win32") chmodSync6(staging, 493);
35317
35319
  renameSync5(staging, destination);
35318
35320
  return destination;
@@ -35345,14 +35347,14 @@ function ensureChrome(binaryPath, options = {}) {
35345
35347
  });
35346
35348
  }
35347
35349
  function browserEngineEncryptionKey(dataDir = DATA_DIR) {
35348
- const file2 = join30(dataDir, KEY_FILE);
35350
+ const file2 = join31(dataDir, KEY_FILE);
35349
35351
  try {
35350
35352
  const existing = readFileSync19(file2, "utf8").trim();
35351
35353
  if (/^[0-9a-f]{64}$/u.test(existing)) return existing;
35352
35354
  } catch {
35353
35355
  }
35354
35356
  const key = randomBytes3(32).toString("hex");
35355
- mkdirSync12(dataDir, { recursive: true });
35357
+ mkdirSync13(dataDir, { recursive: true });
35356
35358
  writeFileAtomic(file2, `${key}
35357
35359
  `, { mode: 384 });
35358
35360
  return key;
@@ -35405,7 +35407,7 @@ function agentBrowserIntegration(input) {
35405
35407
  return { command: input.binaryPath, args: ["mcp", "--tools", "core", "--no-webmcp"], env };
35406
35408
  }
35407
35409
  function agentBrowserFrame(input) {
35408
- const file2 = join30(tmpdir5(), `openmausbot-browser-${randomUUID11()}.png`);
35410
+ const file2 = join31(tmpdir5(), `openmausbot-browser-${randomUUID11()}.png`);
35409
35411
  return new Promise((settle, fail) => {
35410
35412
  const child = spawn7(input.binaryPath, ["screenshot", file2], {
35411
35413
  env: browserRuntimeEnv(input.env),
@@ -35475,8 +35477,8 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
35475
35477
  }
35476
35478
  };
35477
35479
  try {
35478
- for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(join30(directory, base2 + suffix));
35479
- const pendingDirectory = join30(directory, ".tmp");
35480
+ for (const base2 of bases) for (const suffix of [".json", ".json.enc", ".json.previous", ".json.enc.previous"]) remove(join31(directory, base2 + suffix));
35481
+ const pendingDirectory = join31(directory, ".tmp");
35480
35482
  let pendingFiles = [];
35481
35483
  try {
35482
35484
  pendingFiles = readdirSync3(pendingDirectory);
@@ -35484,8 +35486,8 @@ async function clearBrowserSessionState(binaryPath, session, options = {}) {
35484
35486
  if (error51.code !== "ENOENT") throw error51;
35485
35487
  }
35486
35488
  const candidates = bases.map((base2) => new RegExp(`^${base2}-candidate-[0-9]+\\.json(?:\\.enc)?$`));
35487
- for (const name of pendingFiles) if (candidates.some((candidate) => candidate.test(name))) remove(join30(pendingDirectory, name));
35488
- preparedBrowserSessions.delete(join30(directory, session));
35489
+ for (const name of pendingFiles) if (candidates.some((candidate) => candidate.test(name))) remove(join31(pendingDirectory, name));
35490
+ preparedBrowserSessions.delete(join31(directory, session));
35489
35491
  return true;
35490
35492
  } catch {
35491
35493
  return false;
@@ -35512,9 +35514,9 @@ var init_browser_engine = __esm({
35512
35514
  // server/cli.ts
35513
35515
  var import_qrcode_terminal = __toESM(require_main(), 1);
35514
35516
  import { spawn as spawn8 } from "node:child_process";
35515
- import { closeSync as closeSync3, existsSync as existsSync16, mkdirSync as mkdirSync13, openSync as openSync3, readFileSync as readFileSync20 } from "node:fs";
35516
- import { homedir as homedir21 } from "node:os";
35517
- import { dirname as dirname10, join as join31, resolve as resolve11 } from "node:path";
35517
+ import { closeSync as closeSync3, existsSync as existsSync16, mkdirSync as mkdirSync14, openSync as openSync3, readFileSync as readFileSync20 } from "node:fs";
35518
+ import { homedir as homedir23 } from "node:os";
35519
+ import { dirname as dirname11, join as join32, resolve as resolve11 } from "node:path";
35518
35520
  import { createInterface as createInterface2 } from "node:readline/promises";
35519
35521
  import { fileURLToPath as fileURLToPath3 } from "node:url";
35520
35522
 
@@ -37111,16 +37113,190 @@ function normalizeDomainOption(raw) {
37111
37113
  return value;
37112
37114
  }
37113
37115
 
37116
+ // server/service-cli.ts
37117
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "node:fs";
37118
+ import { homedir as homedir2 } from "node:os";
37119
+
37120
+ // server/service-unit.ts
37121
+ import { homedir, userInfo } from "node:os";
37122
+ import { posix } from "node:path";
37123
+ var { basename, dirname, join: join2 } = posix;
37124
+ var SYSTEMD_UNIT_NAME = "openmausbot.service";
37125
+ var LAUNCHD_LABEL = "com.openmausbot.serve";
37126
+ function quoteSystemd(value) {
37127
+ return /[\s"\\]/.test(value) ? `"${value.replace(/[\\"]/g, "\\$&")}"` : value;
37128
+ }
37129
+ function needsStripTypes(script) {
37130
+ return script.endsWith(".ts");
37131
+ }
37132
+ function serviceCommand(spec) {
37133
+ return [spec.node, ...needsStripTypes(spec.script) ? ["--experimental-strip-types"] : [], spec.script, "serve", ...spec.serveArgs];
37134
+ }
37135
+ function systemdUnit(spec) {
37136
+ const lines = [
37137
+ "# Written by `openmausbot service install`. Re-run it to change the options.",
37138
+ "[Unit]",
37139
+ `Description=OpenMausBot${spec.label ? ` (${spec.label})` : ""}`,
37140
+ "After=network-online.target",
37141
+ "Wants=network-online.target",
37142
+ "",
37143
+ "[Service]",
37144
+ "Type=simple",
37145
+ `User=${spec.user}`,
37146
+ `WorkingDirectory=${spec.home}`,
37147
+ `Environment=HOME=${spec.home}`,
37148
+ `Environment=OMB_DATA_DIR=${spec.dataDir}`,
37149
+ `ExecStart=${serviceCommand(spec).map(quoteSystemd).join(" ")}`,
37150
+ "Restart=always",
37151
+ "RestartSec=3",
37152
+ "KillMode=mixed",
37153
+ "TimeoutStopSec=30"
37154
+ ];
37155
+ if (spec.bindsLowPorts) {
37156
+ lines.push("# serve --domain: Caddy binds ports 80 and 443 without running as root.");
37157
+ lines.push("AmbientCapabilities=CAP_NET_BIND_SERVICE");
37158
+ lines.push("CapabilityBoundingSet=CAP_NET_BIND_SERVICE");
37159
+ }
37160
+ lines.push("", "[Install]", "WantedBy=multi-user.target", "");
37161
+ return lines.join("\n");
37162
+ }
37163
+ function xml(value) {
37164
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
37165
+ }
37166
+ function launchdPlist(spec) {
37167
+ const args = serviceCommand(spec).map((arg) => ` <string>${xml(arg)}</string>`).join("\n");
37168
+ return [
37169
+ '<?xml version="1.0" encoding="UTF-8"?>',
37170
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
37171
+ '<plist version="1.0">',
37172
+ "<dict>",
37173
+ " <key>Label</key>",
37174
+ ` <string>${LAUNCHD_LABEL}</string>`,
37175
+ " <key>ProgramArguments</key>",
37176
+ " <array>",
37177
+ args,
37178
+ " </array>",
37179
+ " <key>EnvironmentVariables</key>",
37180
+ " <dict>",
37181
+ " <key>HOME</key>",
37182
+ ` <string>${xml(spec.home)}</string>`,
37183
+ " <key>OMB_DATA_DIR</key>",
37184
+ ` <string>${xml(spec.dataDir)}</string>`,
37185
+ " <key>PATH</key>",
37186
+ " <string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>",
37187
+ " </dict>",
37188
+ " <key>WorkingDirectory</key>",
37189
+ ` <string>${xml(spec.home)}</string>`,
37190
+ " <key>RunAtLoad</key>",
37191
+ " <true/>",
37192
+ " <key>KeepAlive</key>",
37193
+ " <true/>",
37194
+ " <key>StandardOutPath</key>",
37195
+ ` <string>${xml(join2(spec.dataDir, "logs", "service.log"))}</string>`,
37196
+ " <key>StandardErrorPath</key>",
37197
+ ` <string>${xml(join2(spec.dataDir, "logs", "service.log"))}</string>`,
37198
+ "</dict>",
37199
+ "</plist>",
37200
+ ""
37201
+ ].join("\n");
37202
+ }
37203
+ function unstableInstallWarning(script) {
37204
+ const normalized = script.replace(/\\/g, "/");
37205
+ if (/\/_npx\//.test(normalized) || /\/\.npm\/_npx\//.test(normalized)) {
37206
+ return `this command runs from an npx cache (${dirname(script)}), which npm may delete at any time. Install it permanently first (npm install -g openmausbot) and run \`openmausbot service install\` from that install.`;
37207
+ }
37208
+ return null;
37209
+ }
37210
+ function servicePlan(platform, dataDir, home = homedir()) {
37211
+ if (platform === "linux") {
37212
+ const installed = `/etc/systemd/system/${SYSTEMD_UNIT_NAME}`;
37213
+ return {
37214
+ file: join2(dataDir, SYSTEMD_UNIT_NAME),
37215
+ installed,
37216
+ activate: [`sudo install -m 644 ${join2(dataDir, SYSTEMD_UNIT_NAME)} ${installed}`, "sudo systemctl daemon-reload", `sudo systemctl enable --now ${basename(SYSTEMD_UNIT_NAME, ".service")}`],
37217
+ deactivate: [`sudo systemctl disable --now ${basename(SYSTEMD_UNIT_NAME, ".service")}`, `sudo rm ${installed}`, "sudo systemctl daemon-reload"]
37218
+ };
37219
+ }
37220
+ if (platform === "darwin") {
37221
+ const installed = join2(home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
37222
+ return {
37223
+ file: join2(dataDir, `${LAUNCHD_LABEL}.plist`),
37224
+ installed,
37225
+ activate: [`mkdir -p ${dirname(installed)} && cp ${join2(dataDir, `${LAUNCHD_LABEL}.plist`)} ${installed}`, `launchctl bootstrap gui/$(id -u) ${installed}`],
37226
+ deactivate: [`launchctl bootout gui/$(id -u)/${LAUNCHD_LABEL}`, `rm ${installed}`]
37227
+ };
37228
+ }
37229
+ return null;
37230
+ }
37231
+ function currentUser() {
37232
+ try {
37233
+ return userInfo().username;
37234
+ } catch {
37235
+ return process.env.USER || process.env.USERNAME || "openmausbot";
37236
+ }
37237
+ }
37238
+
37239
+ // server/service-cli.ts
37240
+ function serviceServeArgs(input) {
37241
+ const args = ["--port", String(input.port), "--data-dir", input.dataDir, "--no-pair"];
37242
+ if (input.domain) args.push("--domain", input.domain);
37243
+ else if (input.tunnel) args.push("--tunnel");
37244
+ else if (input.tailscale) args.push("--tailscale");
37245
+ if (input.label) args.push("--label", input.label);
37246
+ return args;
37247
+ }
37248
+ function runServiceCommand(input, io) {
37249
+ const platform = input.platform ?? process.platform;
37250
+ const home = input.home ?? homedir2();
37251
+ const plan = servicePlan(platform, input.dataDir, home);
37252
+ if (!plan) {
37253
+ io.error("services are written for Linux (systemd) and macOS (launchd); on Windows, use Task Scheduler to run `openmausbot serve` at startup");
37254
+ return 1;
37255
+ }
37256
+ if (input.action === "uninstall") {
37257
+ io.log(`to stop and remove the service:`);
37258
+ for (const line of plan.deactivate) io.log(` ${line}`);
37259
+ return 0;
37260
+ }
37261
+ const warning = unstableInstallWarning(input.script);
37262
+ if (warning) {
37263
+ io.error(warning);
37264
+ return 1;
37265
+ }
37266
+ const spec = {
37267
+ node: input.node,
37268
+ script: input.script,
37269
+ serveArgs: serviceServeArgs(input),
37270
+ dataDir: input.dataDir,
37271
+ user: input.user ?? currentUser(),
37272
+ home,
37273
+ bindsLowPorts: Boolean(input.domain),
37274
+ ...input.label ? { label: input.label } : {}
37275
+ };
37276
+ const rendered = platform === "darwin" ? launchdPlist(spec) : systemdUnit(spec);
37277
+ mkdirSync2(input.dataDir, { recursive: true, mode: 448 });
37278
+ writeFileSync3(plan.file, rendered, { mode: 420 });
37279
+ io.log(`wrote ${plan.file}`);
37280
+ io.log("");
37281
+ io.log(platform === "darwin" ? "to install and start it (runs at login):" : "to install and start it (runs at boot, restarts if it stops):");
37282
+ for (const line of plan.activate) io.log(` ${line}`);
37283
+ io.log("");
37284
+ if (input.domain && platform === "linux") io.log("the unit grants Caddy the capability for ports 80 and 443, so no setcap is needed under the service");
37285
+ io.log(`logs: ${platform === "darwin" ? `${input.dataDir}/logs/service.log` : "journalctl -u openmausbot -f"}`);
37286
+ io.log(`change options later by running \`openmausbot service install\` again with the new ones, then: ${platform === "darwin" ? plan.activate[1] : "sudo systemctl daemon-reload && sudo systemctl restart openmausbot"}`);
37287
+ return 0;
37288
+ }
37289
+
37114
37290
  // server/tailscale.ts
37115
37291
  import { execFile } from "node:child_process";
37116
37292
 
37117
37293
  // companion/src/listener.ts
37118
- import { homedir, networkInterfaces } from "node:os";
37119
- import { delimiter as delimiter2, join as join2 } from "node:path";
37120
- function tailscaleCandidates(home = homedir()) {
37294
+ import { homedir as homedir3, networkInterfaces } from "node:os";
37295
+ import { delimiter as delimiter2, join as join3 } from "node:path";
37296
+ function tailscaleCandidates(home = homedir3()) {
37121
37297
  return [
37122
37298
  "/Applications/Tailscale.app/Contents/MacOS/Tailscale",
37123
- join2(home, "Applications", "Tailscale.app", "Contents", "MacOS", "Tailscale"),
37299
+ join3(home, "Applications", "Tailscale.app", "Contents", "MacOS", "Tailscale"),
37124
37300
  "/opt/homebrew/bin/tailscale",
37125
37301
  "/usr/local/bin/tailscale",
37126
37302
  "/usr/bin/tailscale",
@@ -37333,9 +37509,9 @@ function phonePairingInstructions(phone, input) {
37333
37509
  // server/tunnel.ts
37334
37510
  import { spawn as spawn3 } from "node:child_process";
37335
37511
  import { randomUUID as randomUUID4 } from "node:crypto";
37336
- import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, statSync } from "node:fs";
37512
+ import { chmodSync as chmodSync2, existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync, statSync } from "node:fs";
37337
37513
  import { hostname } from "node:os";
37338
- import { dirname, join as join3, resolve as resolve2 } from "node:path";
37514
+ import { dirname as dirname2, join as join4, resolve as resolve2 } from "node:path";
37339
37515
  import { fileURLToPath } from "node:url";
37340
37516
 
37341
37517
  // electron/secure-credential-state.mjs
@@ -37408,7 +37584,7 @@ function createSecureCredentialState(initialCredentials, persist, { writable = t
37408
37584
 
37409
37585
  // server/tunnel.ts
37410
37586
  init_atomic();
37411
- var HERE = dirname(fileURLToPath(import.meta.url));
37587
+ var HERE = dirname2(fileURLToPath(import.meta.url));
37412
37588
  var TUNNEL_CREDENTIALS_FILE = "tunnel-account.json";
37413
37589
  var TUNNEL_RUNTIME_DIR = "tunnel-runtime";
37414
37590
  function plainDocument(value) {
@@ -37418,7 +37594,7 @@ function plainDocument(value) {
37418
37594
  return document;
37419
37595
  }
37420
37596
  function openTunnelCredentials(dataDir) {
37421
- const file2 = join3(dataDir, TUNNEL_CREDENTIALS_FILE);
37597
+ const file2 = join4(dataDir, TUNNEL_CREDENTIALS_FILE);
37422
37598
  let initial = {};
37423
37599
  let status = "empty";
37424
37600
  if (existsSync2(file2)) {
@@ -37433,7 +37609,7 @@ function openTunnelCredentials(dataDir) {
37433
37609
  }
37434
37610
  }
37435
37611
  const persist = (next) => {
37436
- mkdirSync2(dataDir, { recursive: true, mode: 448 });
37612
+ mkdirSync3(dataDir, { recursive: true, mode: 448 });
37437
37613
  writeFileAtomic(file2, `${JSON.stringify(next, null, 2)}
37438
37614
  `, { mode: 384 });
37439
37615
  };
@@ -37500,11 +37676,11 @@ function cloudflaredPath(dataDir, env = process.env) {
37500
37676
  return resolveCloudflaredBinary({ isPackaged: false, appPath: dataDir, environment: env });
37501
37677
  }
37502
37678
  function prepareEntry(here = HERE) {
37503
- const bundled = join3(here, "prepare-cloudflared.js");
37679
+ const bundled = join4(here, "prepare-cloudflared.js");
37504
37680
  return existsSync2(bundled) ? bundled : resolve2(here, "..", "scripts", "prepare-cloudflared.mjs");
37505
37681
  }
37506
37682
  function guardianEntry(here = HERE) {
37507
- const bundled = join3(here, "tunnel-guardian.js");
37683
+ const bundled = join4(here, "tunnel-guardian.js");
37508
37684
  if (existsSync2(bundled)) return bundled;
37509
37685
  const source = resolve2(here, "..", "electron", "managed-companion-guardian-main.mjs");
37510
37686
  return existsSync2(source) ? source : null;
@@ -37539,7 +37715,7 @@ function startTunnel(options) {
37539
37715
  const tunnel = createManagedCompanionTunnel({
37540
37716
  binaryPath: options.binaryPath,
37541
37717
  guardianEntry: options.guardian,
37542
- runtimeRoot: join3(options.dataDir, TUNNEL_RUNTIME_DIR),
37718
+ runtimeRoot: join4(options.dataDir, TUNNEL_RUNTIME_DIR),
37543
37719
  originPort,
37544
37720
  environment: env,
37545
37721
  fetchImpl: options.fetchImpl,
@@ -37574,8 +37750,8 @@ function describeTunnelState(state, address) {
37574
37750
  }
37575
37751
 
37576
37752
  // server/cli.ts
37577
- var HERE2 = dirname10(fileURLToPath3(import.meta.url));
37578
- var COMMANDS = ["setup", "start", "serve", "pair", "sessions", "status", "login", "logout", "access", "browser", "help", "--help", "-h"];
37753
+ var HERE2 = dirname11(fileURLToPath3(import.meta.url));
37754
+ var COMMANDS = ["setup", "start", "serve", "pair", "sessions", "status", "login", "logout", "access", "service", "browser", "help", "--help", "-h"];
37579
37755
  function parseArgs(argv, env = process.env) {
37580
37756
  const implicitStart = !argv.length || argv[0].startsWith("--") && argv[0] !== "--help";
37581
37757
  const [command = "start", ...rest] = implicitStart ? ["start", ...argv] : argv;
@@ -37585,7 +37761,7 @@ function parseArgs(argv, env = process.env) {
37585
37761
  const options = {
37586
37762
  command: command === "--help" || command === "-h" ? "help" : command,
37587
37763
  port: Number(env.OMB_PORT || 8799),
37588
- dataDir: env.OMB_DATA_DIR || join31(homedir21(), ".openmausbot"),
37764
+ dataDir: env.OMB_DATA_DIR || join32(homedir23(), ".openmausbot"),
37589
37765
  tailscale: false,
37590
37766
  tunnel: false,
37591
37767
  client: false,
@@ -37624,6 +37800,7 @@ function parseArgs(argv, env = process.env) {
37624
37800
  options.accessAction = arg;
37625
37801
  if (arg !== "list") options.email = value();
37626
37802
  } else if (options.command === "access" && arg === "--chat-only") options.chatOnly = true;
37803
+ else if (options.command === "service" && !options.serviceAction && (arg === "install" || arg === "uninstall")) options.serviceAction = arg;
37627
37804
  else if (options.command === "browser" && (arg === "install" || arg === "status")) options.browserAction = arg;
37628
37805
  else if (options.command === "browser" && arg === "--with-deps") options.withDeps = true;
37629
37806
  else return { error: `unknown argument "${arg}"` };
@@ -37635,6 +37812,7 @@ function parseArgs(argv, env = process.env) {
37635
37812
  if (options.publicUrl && !/^https?:\/\//.test(options.publicUrl)) return { error: "--public-url must start with http:// or https://" };
37636
37813
  if (options.tailscale && options.tunnel) return { error: "choose one of --tailscale (your tailnet) and --tunnel (a public address)" };
37637
37814
  if (options.command === "access" && !options.accessAction) return { error: "access needs one of: list, add EMAIL [--chat-only], remove EMAIL" };
37815
+ if (options.command === "service" && !options.serviceAction) return { error: "service needs one of: install [the same options as serve], uninstall" };
37638
37816
  if (options.domain && (options.tailscale || options.tunnel || options.publicUrl)) return { error: "--domain already gives the server its address; drop --tailscale, --tunnel and --public-url" };
37639
37817
  if (options.local && (options.tailscale || options.tunnel || options.publicUrl)) return { error: "--local cannot be combined with a remote-access option" };
37640
37818
  if (options.command === "browser" && !options.browserAction) return { error: "browser needs an action: install or status" };
@@ -37653,6 +37831,7 @@ var USAGE = `openmausbot \u2014 your team of AI bots, ready in a few steps
37653
37831
  openmausbot login [--email you@example.com]
37654
37832
  openmausbot logout
37655
37833
  openmausbot access list | add EMAIL [--chat-only] | remove EMAIL
37834
+ openmausbot service install [--domain HOST | --tunnel | --tailscale] [--port N] [--data-dir DIR] | uninstall
37656
37835
  openmausbot browser install [--with-deps] | status
37657
37836
 
37658
37837
  setup choose AI access and optional phone access; keep existing bots and chats
@@ -37667,6 +37846,10 @@ logout releases that address and signs out
37667
37846
  access who may sign in with an emailed code at /pair: an address or
37668
37847
  @domain; --chat-only gives chat and approvals without settings.
37669
37848
  Takes effect at once, no restart.
37849
+ service keep the server running across reboots: writes a systemd unit
37850
+ (Linux) or a launchd agent (macOS) for the same serve options and
37851
+ prints the commands that install it. Install the package
37852
+ permanently first (npm install -g openmausbot).
37670
37853
  browser install: the bots' browser engine (agent-browser, pinned) into the
37671
37854
  data dir, and Chrome for Testing into the user's browser cache.
37672
37855
  --with-deps also installs
@@ -37733,7 +37916,7 @@ async function isWorkspaceRunning(options) {
37733
37916
  try {
37734
37917
  const { status, body } = await api(options.port, "/api/health");
37735
37918
  if (status !== 200 || body?.app !== "openmausbot") return false;
37736
- const expected = readFileSync20(join31(options.dataDir, "environment-id"), "utf8").trim();
37919
+ const expected = readFileSync20(join32(options.dataDir, "environment-id"), "utf8").trim();
37737
37920
  const descriptor = await api(options.port, "/.well-known/openmausbot/environment");
37738
37921
  return /^[0-9a-f-]{36}$/i.test(expected) && descriptor.status === 200 && descriptor.body?.environmentId === expected;
37739
37922
  } catch {
@@ -37933,7 +38116,7 @@ async function runStatus(options, io = defaultIo()) {
37933
38116
  return code;
37934
38117
  }
37935
38118
  async function runAccess(options, io = defaultIo()) {
37936
- const file2 = join31(options.dataDir, "config.json");
38119
+ const file2 = join32(options.dataDir, "config.json");
37937
38120
  let raw = {};
37938
38121
  if (existsSync16(file2)) {
37939
38122
  try {
@@ -37951,7 +38134,7 @@ async function runAccess(options, io = defaultIo()) {
37951
38134
  const members = list(Reflect.get(current, "members"));
37952
38135
  const overridden = process.env.OMB_SIGNIN_EMAILS !== void 0 || process.env.OMB_SIGNIN_MEMBER_EMAILS !== void 0;
37953
38136
  const write = (next) => {
37954
- mkdirSync13(options.dataDir, { recursive: true, mode: 448 });
38137
+ mkdirSync14(options.dataDir, { recursive: true, mode: 448 });
37955
38138
  writeFileAtomic(file2, `${JSON.stringify({ ...raw, signIn: next }, null, 2)}
37956
38139
  `, { mode: 384 });
37957
38140
  };
@@ -38085,16 +38268,16 @@ async function runBrowser(options, io = defaultIo()) {
38085
38268
  return 0;
38086
38269
  }
38087
38270
  function serverEntry(here = HERE2) {
38088
- const bundled = join31(here, "index.js");
38271
+ const bundled = join32(here, "index.js");
38089
38272
  const root = resolve11(here, "..");
38090
38273
  if (existsSync16(bundled)) {
38091
- const staticDir2 = [join31(root, "dist"), join31(here, "..", "ui")].find((d) => existsSync16(join31(d, "index.html"))) ?? null;
38092
- const skillsDir = existsSync16(join31(root, "skills")) ? join31(root, "skills") : null;
38274
+ const staticDir2 = [join32(root, "dist"), join32(here, "..", "ui")].find((d) => existsSync16(join32(d, "index.html"))) ?? null;
38275
+ const skillsDir = existsSync16(join32(root, "skills")) ? join32(root, "skills") : null;
38093
38276
  return { command: process.execPath, args: [bundled], staticDir: staticDir2, skillsDir };
38094
38277
  }
38095
- const source = join31(here, "index.ts");
38096
- const staticDir = existsSync16(join31(root, "dist", "index.html")) ? join31(root, "dist") : null;
38097
- return { command: process.execPath, args: ["--experimental-strip-types", source], staticDir, skillsDir: existsSync16(join31(root, "skills")) ? join31(root, "skills") : null };
38278
+ const source = join32(here, "index.ts");
38279
+ const staticDir = existsSync16(join32(root, "dist", "index.html")) ? join32(root, "dist") : null;
38280
+ return { command: process.execPath, args: ["--experimental-strip-types", source], staticDir, skillsDir: existsSync16(join32(root, "skills")) ? join32(root, "skills") : null };
38098
38281
  }
38099
38282
  async function planTunnel(options, log) {
38100
38283
  let access2 = null;
@@ -38194,9 +38377,9 @@ async function runServe(options, log = console.log) {
38194
38377
  let child;
38195
38378
  try {
38196
38379
  if (options.guided) {
38197
- const logsDir = join31(options.dataDir, "logs");
38198
- mkdirSync13(logsDir, { recursive: true, mode: 448 });
38199
- logPath = join31(logsDir, `server-${Date.now()}-${process.pid}.log`);
38380
+ const logsDir = join32(options.dataDir, "logs");
38381
+ mkdirSync14(logsDir, { recursive: true, mode: 448 });
38382
+ logPath = join32(logsDir, `server-${Date.now()}-${process.pid}.log`);
38200
38383
  logFd = openSync3(logPath, "wx", 384);
38201
38384
  log("\nStarting your workspace\u2026");
38202
38385
  }
@@ -38388,7 +38571,7 @@ async function runOnboardingCommand(options, io = defaultIo(), startServer = run
38388
38571
  }
38389
38572
  if (options.command === "setup") {
38390
38573
  io.log("\nAll set. Start with: openmausbot (or npx openmausbot without a global install).");
38391
- if (options.dataDir !== join31(homedir21(), ".openmausbot") || options.port !== 8799) {
38574
+ if (options.dataDir !== join32(homedir23(), ".openmausbot") || options.port !== 8799) {
38392
38575
  io.log(`Use the same --data-dir (${options.dataDir}) and --port (${options.port}) options when starting.`);
38393
38576
  }
38394
38577
  return 0;
@@ -38426,6 +38609,18 @@ ${USAGE}`);
38426
38609
  return runLogin(options);
38427
38610
  case "access":
38428
38611
  return runAccess(options);
38612
+ case "service":
38613
+ return runServiceCommand({
38614
+ action: options.serviceAction ?? "install",
38615
+ dataDir: options.dataDir,
38616
+ port: options.port,
38617
+ domain: options.domain,
38618
+ tunnel: options.tunnel,
38619
+ tailscale: options.tailscale,
38620
+ label: options.label,
38621
+ script: process.argv[1] ?? "",
38622
+ node: process.execPath
38623
+ }, { log: (line) => console.log(line), error: (line) => console.error(line) });
38429
38624
  case "logout":
38430
38625
  return runLogout(options);
38431
38626
  case "browser":