openmausbot 0.1.97 → 0.1.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/dist/assets/ActivityPanel-H6D3SD39.js +1 -0
  2. package/dist/assets/BotSettingsDialog-Dqz_AiBZ.js +7 -0
  3. package/dist/assets/CloudBackendPicker-iezpJFpF.js +1 -0
  4. package/dist/assets/ComputerPanel-DZPj-IWu.js +1 -0
  5. package/dist/assets/DesktopViewer-utONms1p.js +5 -0
  6. package/dist/assets/InspectorPanel-OVaBVrzC.js +5 -0
  7. package/dist/assets/KeyboardShortcutsModal-gk9mvHj_.js +1 -0
  8. package/dist/assets/LocalVmWorkspace-DLIvd2kC.js +1 -0
  9. package/dist/assets/MacLocalControl-bJv8yiPB.js +1 -0
  10. package/dist/assets/NewBotDialog-1qGOAdUM.js +2 -0
  11. package/dist/assets/RemoteAgentSettingsPanel-DqKr0HkO.js +1 -0
  12. package/dist/assets/RoutinesPage-BM2D4qGi.js +1 -0
  13. package/dist/assets/RoutinesSection-Dny9ra-c.js +1 -0
  14. package/dist/assets/SettingsModal-gC6XJJmt.js +11 -0
  15. package/dist/assets/TeamMapPage-B6fuu4r_.js +8 -0
  16. package/dist/assets/TriggersPanel-j0qTP2CO.js +1 -0
  17. package/dist/assets/VisibilitySection-DT0FqPvu.js +12 -0
  18. package/dist/assets/WebhooksPanel--YQyCjKO.js +3 -0
  19. package/dist/assets/{abnfDiagram-O67JEVCF-k1UCNXb7.js → abnfDiagram-O67JEVCF-BsjcFX0-.js} +1 -1
  20. package/dist/assets/activity-BxOkayvD.js +1 -0
  21. package/dist/assets/{arc-BUWGa9UU.js → arc-CJNA94c6.js} +1 -1
  22. package/dist/assets/architectureDiagram-NJMV4G6O-dz2ud7lP.js +36 -0
  23. package/dist/assets/{blockDiagram-BEXU5L5S-Ag9nLW4H.js → blockDiagram-BEXU5L5S-ChBEALoR.js} +1 -1
  24. package/dist/assets/brain-CFZ7PEXc.js +1 -0
  25. package/dist/assets/{c4Diagram-YGBWAQC7-Bh0sjgUa.js → c4Diagram-YGBWAQC7-KZNRKDQe.js} +1 -1
  26. package/dist/assets/channel-WhFyujhZ.js +1 -0
  27. package/dist/assets/{chunk-2Q5K7J3B-BFBcIIqu.js → chunk-2Q5K7J3B-BLLwmCET.js} +1 -1
  28. package/dist/assets/{chunk-3FUC2YCW-Dvfpex-f.js → chunk-3FUC2YCW-CQxyreBZ.js} +2 -2
  29. package/dist/assets/{chunk-5VM5RSS4-CMjP2YZW.js → chunk-5VM5RSS4-D6sFMW0u.js} +1 -1
  30. package/dist/assets/{chunk-GTNCS2PH-BryoeTc7.js → chunk-GTNCS2PH-BaZ1YgLF.js} +1 -1
  31. package/dist/assets/{chunk-GWA4HPMP-BhfvcchO.js → chunk-GWA4HPMP-CcalphmC.js} +1 -1
  32. package/dist/assets/{chunk-JWPE2WC7-bcCVp8rp.js → chunk-JWPE2WC7-CSk8R3ve.js} +1 -1
  33. package/dist/assets/{chunk-NETBCI7D-yKIjIlkQ.js → chunk-NETBCI7D-CTmLjA57.js} +1 -1
  34. package/dist/assets/{chunk-WEXAMYUT-D3Zqbmup.js → chunk-WEXAMYUT-CM9Z77Ne.js} +1 -1
  35. package/dist/assets/{chunk-XXDRQBXY-CjVoIMG6.js → chunk-XXDRQBXY-B1fxpMS2.js} +1 -1
  36. package/dist/assets/{classDiagram-v2-NBCMYWYE-mUCWdVyH.js → classDiagram-v2-NBCMYWYE-DCCyoLpj.js} +1 -1
  37. package/dist/assets/computer-control-NLov352p.js +1 -0
  38. package/dist/assets/cose-bilkent-JH36ORCC-KyImBKvE.js +1 -0
  39. package/dist/assets/{cynefin-OW5HDTMX-BpRrxoO4.js → cynefin-OW5HDTMX-fsZc6MGa.js} +1 -1
  40. package/dist/assets/{cynefinDiagram-VND7K2PF-D3VBWCPK.js → cynefinDiagram-VND7K2PF-CGmHsShB.js} +1 -1
  41. package/dist/assets/{dagre-6A5THRUB-DxEIACOO.js → dagre-6A5THRUB-Dah1h-CH.js} +1 -1
  42. package/dist/assets/{diagram-22UHCM2B-BANgZEOS.js → diagram-22UHCM2B-BIu71OyV.js} +1 -1
  43. package/dist/assets/{diagram-3UASUU5V-Cnq7nJir.js → diagram-3UASUU5V-gxhUB_Mu.js} +1 -1
  44. package/dist/assets/{diagram-ATOU4E4O-4TBDIWnz.js → diagram-ATOU4E4O-CBhusUBn.js} +1 -1
  45. package/dist/assets/{diagram-CDSNMT55-CD_B39D4.js → diagram-CDSNMT55-BdPrc86x.js} +1 -1
  46. package/dist/assets/{diagram-MLGK6HIB-CzvnFhEC.js → diagram-MLGK6HIB-Clp110R_.js} +1 -1
  47. package/dist/assets/{diagram-MPIPVDR6-C4w2SNml.js → diagram-MPIPVDR6-DJKfbugw.js} +1 -1
  48. package/dist/assets/{ebnfDiagram-ZINNZB2B-gkUEBm2s.js → ebnfDiagram-ZINNZB2B-DnsltoBu.js} +1 -1
  49. package/dist/assets/{elk-276RUBZZ-B_56X6mw.js → elk-276RUBZZ-jWdq6gqH.js} +1 -1
  50. package/dist/assets/{erDiagram-OPXOYQCR-C_oByoDS.js → erDiagram-OPXOYQCR-DArPVf8u.js} +1 -1
  51. package/dist/assets/{flowDiagram-KWPJA3E3-CR9xXJkC.js → flowDiagram-KWPJA3E3-D2HZ-2f7.js} +1 -1
  52. package/dist/assets/ganttDiagram-FUAMR5RP--o0KIxEo.js +292 -0
  53. package/dist/assets/{gitGraphDiagram-X574FWY7-VJ6-Tp_p.js → gitGraphDiagram-X574FWY7-CsoXr2D7.js} +1 -1
  54. package/dist/assets/image-plus-BbVjGHin.js +1 -0
  55. package/dist/assets/index-BrPW5HDO.css +1 -0
  56. package/dist/assets/index-C3NqfdVU.js +585 -0
  57. package/dist/assets/{index-Cp4S4NOL.js → index-CJTp9JEp.js} +1 -1
  58. package/dist/assets/{infoDiagram-VRGFBTTK-2Lu9te4D.js → infoDiagram-VRGFBTTK-g-6lNp7r.js} +1 -1
  59. package/dist/assets/{ishikawaDiagram-OU5B5YK6-DR49XY-e.js → ishikawaDiagram-OU5B5YK6-B1d6qVTV.js} +1 -1
  60. package/dist/assets/{journeyDiagram-ZHPQQLJL-ZoR0M305.js → journeyDiagram-ZHPQQLJL-BTPuiU6T.js} +1 -1
  61. package/dist/assets/{kanban-definition-PNTS6WVX-DLKHxrO-.js → kanban-definition-PNTS6WVX-bDDuRJvP.js} +1 -1
  62. package/dist/assets/{linear-BASsfWly.js → linear-BVVKbEMy.js} +1 -1
  63. package/dist/assets/{mermaid.core-BVidiPkk.js → mermaid.core-JJdoKyKB.js} +5 -5
  64. package/dist/assets/{mindmap-definition-NLK3R4M7-g_Qx0U5C.js → mindmap-definition-NLK3R4M7-B4aE3T0q.js} +1 -1
  65. package/dist/assets/module-DCUsy2BQ.js +3 -0
  66. package/dist/assets/page-visible-BBo_Zm8g.js +1 -0
  67. package/dist/assets/{pegDiagram-GJSIUBJH-DI9Cjs8w.js → pegDiagram-GJSIUBJH-DFKQWpR7.js} +1 -1
  68. package/dist/assets/{pieDiagram-5QR66LMP-sRI0Uhr5.js → pieDiagram-5QR66LMP-Iv7OKVJi.js} +1 -1
  69. package/dist/assets/{quadrantDiagram-O4NWA36T-CamKTby3.js → quadrantDiagram-O4NWA36T-U2YnfwBk.js} +1 -1
  70. package/dist/assets/{railroadDiagram-XR7U4H2S-DOLlkbf3.js → railroadDiagram-XR7U4H2S-CuPy6RqM.js} +1 -1
  71. package/dist/assets/remote-desktop-panel-BNEj7tUx.js +1 -0
  72. package/dist/assets/{requirementDiagram-PLB6GJNP-DrRQ5r97.js → requirementDiagram-PLB6GJNP-bMI5AK7T.js} +1 -1
  73. package/dist/assets/{sankeyDiagram-IPEJSGJF-DGS_lXcX.js → sankeyDiagram-IPEJSGJF-BKcFE2WQ.js} +1 -1
  74. package/dist/assets/{sequenceDiagram-PO4LG4MO-CkjWqwtf.js → sequenceDiagram-PO4LG4MO-Bn9q88kl.js} +1 -1
  75. package/dist/assets/{sizeCapture-INFHLROL-CepRgful.js → sizeCapture-INFHLROL-Cvmdq9HD.js} +1 -1
  76. package/dist/assets/{stateDiagram-v2-GCMORJYK-hykr-9RS.js → stateDiagram-v2-GCMORJYK-JJQYw3eQ.js} +1 -1
  77. package/dist/assets/{swimlanes-2SLR337P-CKOHEZbI.js → swimlanes-2SLR337P-BkSCBEwp.js} +1 -1
  78. package/dist/assets/swimlanesDiagram-TC7HE7FX-CmTirbVT.js +8 -0
  79. package/dist/assets/table-file.worker-KkMx-SNx.js +10 -0
  80. package/dist/assets/{timeline-definition-EJHVYXUP-CB8pJ6AC.js → timeline-definition-EJHVYXUP-DsaM4GHr.js} +1 -1
  81. package/dist/assets/{usecaseDiagram-POWQR4AR-DrclJHdl.js → usecaseDiagram-POWQR4AR-3FOwLq7B.js} +1 -1
  82. package/dist/assets/user-DcZBnAuv.js +1 -0
  83. package/dist/assets/{vennDiagram-UO4OBE2U-zS23qjv8.js → vennDiagram-UO4OBE2U-DyNjJsKt.js} +1 -1
  84. package/dist/assets/{wardleyDiagram-VNRHLVJA-52jgeKxz.js → wardleyDiagram-VNRHLVJA-Y4ZMcnBC.js} +1 -1
  85. package/dist/assets/{xychartDiagram-PMCCYNJV-DSdl9rQ8.js → xychartDiagram-PMCCYNJV-BXZpg955.js} +1 -1
  86. package/dist/index.html +2 -2
  87. package/dist-server/cloud-home-start.js +19 -8
  88. package/dist-server/container-mcp.js +8 -0
  89. package/dist-server/desktop-entry.mjs +54 -0
  90. package/dist-server/drivers/agents-proxy.js +21 -4
  91. package/dist-server/index.js +1412 -821
  92. package/dist-server/openmausbot.js +837 -571
  93. package/dist-server/pair-cli.js +837 -571
  94. package/dist-server/permission-proxy.js +2 -0
  95. package/dist-server/server/atomic.js +70 -2
  96. package/dist-server/server/cloud-home-start.js +3 -3
  97. package/dist-server/server/config.js +22 -1
  98. package/dist-server/server/connector-card-text.js +15 -0
  99. package/dist-server/server/default-model-selection.js +7 -4
  100. package/dist-server/server/drivers/acp/core.js +10 -0
  101. package/dist-server/server/drivers/acp/grok.js +47 -5
  102. package/dist-server/server/drivers/agents-catalog.js +24 -4
  103. package/dist-server/server/drivers/agents-proxy.js +1 -0
  104. package/dist-server/server/drivers/chat-mcp-tools.js +5 -3
  105. package/dist-server/server/drivers/claude-login-auth.js +1 -1
  106. package/dist-server/server/drivers/claude.js +35 -11
  107. package/dist-server/server/drivers/codex-device-auth.js +27 -319
  108. package/dist-server/server/drivers/device-auth.js +342 -0
  109. package/dist-server/server/drivers/openai-chat.js +12 -4
  110. package/dist-server/server/fleet-agent.js +18 -5
  111. package/dist-server/server/harness/http.js +5 -2
  112. package/dist-server/server/harness/registry.js +19 -0
  113. package/dist-server/server/harness-capabilities.js +7 -4
  114. package/dist-server/server/hosted-models.js +3 -1
  115. package/dist-server/server/index.js +268 -54
  116. package/dist-server/server/key-rejections.js +57 -0
  117. package/dist-server/server/message-file.js +7 -4
  118. package/dist-server/server/permission-proxy.js +4 -0
  119. package/dist-server/server/provider-key-check.js +5 -1
  120. package/dist-server/server/request-auth.js +1 -1
  121. package/dist-server/server/room-turn-timeout.js +28 -0
  122. package/dist-server/server/routes/thread-models.js +36 -0
  123. package/dist-server/server/routines.js +2 -2
  124. package/dist-server/server/skills.js +3 -2
  125. package/dist-server/server/store.js +148 -60
  126. package/dist-server/server/team-backup.js +19 -4
  127. package/dist-server/server/thread-model.js +25 -0
  128. package/dist-server/server/turn-continuation.js +8 -2
  129. package/dist-server/server/webhook-ingress.js +8 -3
  130. package/dist-server/server/webhooks.js +40 -5
  131. package/dist-server/shared/device-sign-in.js +57 -0
  132. package/dist-server/shared/team-backup.js +2 -0
  133. package/dist-server/shared/thread-model.js +13 -0
  134. package/dist-server/vps-container-mcp.js +8 -0
  135. package/dist-server/workspace-backup.worker.js +41 -22
  136. package/package.json +1 -1
  137. package/dist/assets/DesktopViewer-CPidMCtt.js +0 -5
  138. package/dist/assets/architectureDiagram-NJMV4G6O-DiPor3UT.js +0 -36
  139. package/dist/assets/channel-DR8eD6QW.js +0 -1
  140. package/dist/assets/cose-bilkent-JH36ORCC-BBmR1bbX.js +0 -1
  141. package/dist/assets/ganttDiagram-FUAMR5RP-gtYBTu0B.js +0 -292
  142. package/dist/assets/index-CpaIXRLp.js +0 -614
  143. package/dist/assets/index-Z81SMsgR.css +0 -1
  144. package/dist/assets/swimlanesDiagram-TC7HE7FX-D-8vqZ4-.js +0 -8
@@ -16344,7 +16344,18 @@ var init_enterprise = __esm({
16344
16344
 
16345
16345
  // server/atomic.ts
16346
16346
  import { randomUUID as randomUUID3 } from "node:crypto";
16347
- import { closeSync, fsyncSync, openSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
16347
+ import {
16348
+ closeSync,
16349
+ constants,
16350
+ fstatSync,
16351
+ fsyncSync,
16352
+ lstatSync,
16353
+ openSync,
16354
+ readFileSync,
16355
+ renameSync,
16356
+ unlinkSync,
16357
+ writeFileSync
16358
+ } from "node:fs";
16348
16359
  function sleepSync(ms) {
16349
16360
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
16350
16361
  }
@@ -16366,7 +16377,7 @@ function writeFileAtomic(path3, data, options = {}) {
16366
16377
  try {
16367
16378
  fd = openSync(tmp, "w", options.mode);
16368
16379
  writeFileSync(fd, data);
16369
- fsyncSync(fd);
16380
+ if (options.durable !== false) fsyncSync(fd);
16370
16381
  closeSync(fd);
16371
16382
  fd = null;
16372
16383
  renameWithRetry(tmp, path3);
@@ -16384,6 +16395,37 @@ function writeFileAtomic(path3, data, options = {}) {
16384
16395
  throw e;
16385
16396
  }
16386
16397
  }
16398
+ function holdsExactly(path3, data, mode) {
16399
+ let fd = null;
16400
+ try {
16401
+ if (process.platform === "win32") {
16402
+ if (!lstatSync(path3).isFile()) return false;
16403
+ fd = openSync(path3, "r");
16404
+ } else {
16405
+ fd = openSync(path3, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
16406
+ }
16407
+ const stat5 = fstatSync(fd);
16408
+ if (!stat5.isFile() || stat5.size !== Buffer.byteLength(data)) return false;
16409
+ const uid = process.getuid?.();
16410
+ if (uid !== void 0 && stat5.uid !== uid) return false;
16411
+ if (mode !== void 0 && process.platform !== "win32" && (stat5.mode & 511 & ~mode) !== 0) return false;
16412
+ return readFileSync(fd).equals(Buffer.from(data));
16413
+ } catch {
16414
+ return false;
16415
+ } finally {
16416
+ if (fd !== null) {
16417
+ try {
16418
+ closeSync(fd);
16419
+ } catch {
16420
+ }
16421
+ }
16422
+ }
16423
+ }
16424
+ function writeFileAtomicIfChanged(path3, data, options = {}) {
16425
+ if (holdsExactly(path3, data, options.mode)) return false;
16426
+ writeFileAtomic(path3, data, options);
16427
+ return true;
16428
+ }
16387
16429
  var RENAME_RETRY_DELAYS_MS, RETRYABLE_RENAME_CODES;
16388
16430
  var init_atomic = __esm({
16389
16431
  "server/atomic.ts"() {
@@ -18935,7 +18977,7 @@ var init_schema = __esm({
18935
18977
  });
18936
18978
 
18937
18979
  // server/config.ts
18938
- import { readFileSync as readFileSync3, mkdirSync as mkdirSync6, existsSync as existsSync6, renameSync as renameSync3, statSync as statSync2 } from "node:fs";
18980
+ import { readFileSync as readFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync6, renameSync as renameSync3, statSync as statSync2 } from "node:fs";
18939
18981
  import { homedir as homedir4 } from "node:os";
18940
18982
  import { join as join9 } from "node:path";
18941
18983
  function isValidSshAlias(value) {
@@ -19078,7 +19120,7 @@ function parseStoredConfig(value) {
19078
19120
  function loadConfig() {
19079
19121
  let cfg = {};
19080
19122
  try {
19081
- cfg = parseStoredConfig(parseJson(readFileSync3(join9(DATA_DIR, "config.json"), "utf8")));
19123
+ cfg = parseStoredConfig(parseJson(readFileSync4(join9(DATA_DIR, "config.json"), "utf8")));
19082
19124
  lastIgnoredConfigWarning = "";
19083
19125
  } catch (error51) {
19084
19126
  if (error51?.code !== "ENOENT") {
@@ -19161,7 +19203,7 @@ function saveConfig(patch, options = {}) {
19161
19203
  const p2 = join9(DATA_DIR, "config.json");
19162
19204
  let disk = {};
19163
19205
  try {
19164
- const parsed = jsonObjectSchema.safeParse(parseJson(readFileSync3(p2, "utf8")));
19206
+ const parsed = jsonObjectSchema.safeParse(parseJson(readFileSync4(p2, "utf8")));
19165
19207
  if (parsed.success) disk = parsed.data;
19166
19208
  } catch {
19167
19209
  }
@@ -19169,7 +19211,7 @@ function saveConfig(patch, options = {}) {
19169
19211
  const before = configSaveListeners.size ? structuredClone(disk) : disk;
19170
19212
  const storedProfiles = storedBrowserProfilesSchema.safeParse(disk.browserProfiles);
19171
19213
  if (storedProfiles.success) disk.browserProfiles = storedProfiles.data;
19172
- for (const key of ["xai", "anthropic", "mistral", "cerebras", "openai", "openrouter", "openaiCompat", "composio", "box", "opencodeGo", "tts", "decider", "imageGen", "live", "profile", "rooms", "threads", "context", "memory", "localVm", "features", "budgets", "billing", "decisions", "onboarding", "browserEngine", "newBots"]) {
19214
+ for (const key of ["xai", "anthropic", "mistral", "cerebras", "openai", "openrouter", "openaiCompat", "composio", "box", "opencodeGo", "tts", "decider", "imageGen", "live", "profile", "rooms", "mcp", "threads", "context", "memory", "localVm", "features", "budgets", "billing", "decisions", "onboarding", "browserEngine", "newBots"]) {
19173
19215
  const section = checkedPatch[key];
19174
19216
  if (!section) continue;
19175
19217
  const current = jsonObjectSchema.safeParse(disk[key]);
@@ -19394,7 +19436,7 @@ function instanceConfigs(cfg) {
19394
19436
  }
19395
19437
  return map3;
19396
19438
  }
19397
- var optionalText, SSH_ALIAS, LEGACY_BROWSER_PROFILE_ID, BROWSER_PROFILE_ID, FISH_TTS_MODELS, MIN_ROOM_TURN_TIMEOUT_MINUTES, MAX_ROOM_TURN_TIMEOUT_MINUTES, DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES, DEFAULT_ROOM_HANDOFF_MIN_RUNWAY_MINUTES, DEFAULT_ROOM_HANDOFF_HARD_CAP_MINUTES, DEFAULT_MAX_CONCURRENT_BOT_THREADS, MAX_CONCURRENT_BOT_THREADS, MIN_THREAD_EVENT_LOG_BYTES, MAX_THREAD_EVENT_LOG_BYTES, MIN_LOCAL_VM_MAX_INSTANCES, MAX_LOCAL_VM_MAX_INSTANCES, MIN_LOCAL_VM_IDLE_TIMEOUT_MINUTES, MAX_LOCAL_VM_IDLE_TIMEOUT_MINUTES, CDP_PORT, CDP_URL, vpsConfigSchema, browserEngineConfigSchema, roomConfigSchema, localVmConfigSchema, browserProfileSchema, legacyBrowserProfileSchema, legacyBrowserProfilesSchema, storedBrowserProfilesSchema, browserProfilesSchema, featureConfigSchema, onboardingConfigSchema, instanceConfigSchema, instanceConfigMapSchema, defaultModelSelectionSchema, automaticRecoverySchema, threadsConfigSchema, newBotsConfigSchema, newBotsPatchSchema, threadsPatchSchema, appConfigSchema, storedAppConfigSchema, appConfigPatchSchema, jsonObjectSchema, DEFAULT_CAPTURE_QUIET_MS, DATA_DIR, LEGACY_DATA_DIR, EVENTS_DIR, NATIVE_DIR, lastIgnoredConfigWarning, WORKSPACE_CREDENTIAL_ENV, CONTROL_PLANE_ENV, CONTROL_PLANE_ENV_PREFIX, PROVIDER_CREDENTIAL_ENV, OPENCODE_PROVIDER_ENV, OPENCODE_PROVIDER_KEY_LIMIT, OPENCODE_PROVIDER_KEY_NAME, OPENCODE_PROVIDER_KEY_VALUE, configSaveListeners, CLAUDE_ROUTING_ENV, sameUrl, OPENAI_API_INSTANCE, OPENROUTER_API_INSTANCE, XAI_API_INSTANCE, CLAUDE_API_INSTANCE, API_KEY_FLEET;
19439
+ var optionalText, SSH_ALIAS, LEGACY_BROWSER_PROFILE_ID, BROWSER_PROFILE_ID, FISH_TTS_MODELS, MIN_ROOM_TURN_TIMEOUT_MINUTES, MAX_ROOM_TURN_TIMEOUT_MINUTES, MIN_MCP_CALL_TIMEOUT_MINUTES, MAX_MCP_CALL_TIMEOUT_MINUTES, DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES, DEFAULT_ROOM_HANDOFF_MIN_RUNWAY_MINUTES, DEFAULT_ROOM_HANDOFF_HARD_CAP_MINUTES, DEFAULT_MAX_CONCURRENT_BOT_THREADS, MAX_CONCURRENT_BOT_THREADS, MIN_THREAD_EVENT_LOG_BYTES, MAX_THREAD_EVENT_LOG_BYTES, MIN_LOCAL_VM_MAX_INSTANCES, MAX_LOCAL_VM_MAX_INSTANCES, MIN_LOCAL_VM_IDLE_TIMEOUT_MINUTES, MAX_LOCAL_VM_IDLE_TIMEOUT_MINUTES, CDP_PORT, CDP_URL, vpsConfigSchema, browserEngineConfigSchema, roomConfigSchema, mcpConfigSchema, localVmConfigSchema, browserProfileSchema, legacyBrowserProfileSchema, legacyBrowserProfilesSchema, storedBrowserProfilesSchema, browserProfilesSchema, featureConfigSchema, onboardingConfigSchema, instanceConfigSchema, instanceConfigMapSchema, defaultModelSelectionSchema, automaticRecoverySchema, threadsConfigSchema, newBotsConfigSchema, newBotsPatchSchema, threadsPatchSchema, appConfigSchema, storedAppConfigSchema, appConfigPatchSchema, jsonObjectSchema, DEFAULT_CAPTURE_QUIET_MS, DATA_DIR, LEGACY_DATA_DIR, EVENTS_DIR, NATIVE_DIR, lastIgnoredConfigWarning, WORKSPACE_CREDENTIAL_ENV, CONTROL_PLANE_ENV, CONTROL_PLANE_ENV_PREFIX, PROVIDER_CREDENTIAL_ENV, OPENCODE_PROVIDER_ENV, OPENCODE_PROVIDER_KEY_LIMIT, OPENCODE_PROVIDER_KEY_NAME, OPENCODE_PROVIDER_KEY_VALUE, configSaveListeners, CLAUDE_ROUTING_ENV, sameUrl, OPENAI_API_INSTANCE, OPENROUTER_API_INSTANCE, XAI_API_INSTANCE, CLAUDE_API_INSTANCE, API_KEY_FLEET;
19398
19440
  var init_config = __esm({
19399
19441
  "server/config.ts"() {
19400
19442
  "use strict";
@@ -19415,6 +19457,8 @@ var init_config = __esm({
19415
19457
  FISH_TTS_MODELS = ["s2.1-pro", "s2.1-pro-free"];
19416
19458
  MIN_ROOM_TURN_TIMEOUT_MINUTES = 1;
19417
19459
  MAX_ROOM_TURN_TIMEOUT_MINUTES = 1440;
19460
+ MIN_MCP_CALL_TIMEOUT_MINUTES = 1;
19461
+ MAX_MCP_CALL_TIMEOUT_MINUTES = 60;
19418
19462
  DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES = 30;
19419
19463
  DEFAULT_ROOM_HANDOFF_MIN_RUNWAY_MINUTES = 10;
19420
19464
  DEFAULT_ROOM_HANDOFF_HARD_CAP_MINUTES = 240;
@@ -19450,6 +19494,11 @@ var init_config = __esm({
19450
19494
  (rooms) => (rooms.handoffMinRunwayMinutes ?? DEFAULT_ROOM_HANDOFF_MIN_RUNWAY_MINUTES) <= (rooms.handoffLifetimeMinutes ?? DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES) && (rooms.handoffLifetimeMinutes ?? DEFAULT_ROOM_HANDOFF_LIFETIME_MINUTES) <= (rooms.handoffHardCapMinutes ?? DEFAULT_ROOM_HANDOFF_HARD_CAP_MINUTES),
19451
19495
  { message: "rooms handoff bounds must satisfy handoffMinRunwayMinutes <= handoffLifetimeMinutes <= handoffHardCapMinutes" }
19452
19496
  );
19497
+ mcpConfigSchema = external_exports.object({
19498
+ /** Ceiling (minutes) for one bot MCP tool call. Missing values resolve to
19499
+ * 10, preserving the historic chat-mcp-tools constant. */
19500
+ callTimeoutMinutes: external_exports.number().int().min(MIN_MCP_CALL_TIMEOUT_MINUTES).max(MAX_MCP_CALL_TIMEOUT_MINUTES).optional()
19501
+ }).strict();
19453
19502
  localVmConfigSchema = external_exports.object({
19454
19503
  mode: external_exports.enum(["shared", "per-bot", "pool"]).optional(),
19455
19504
  maxInstances: external_exports.number().int().min(MIN_LOCAL_VM_MAX_INSTANCES).max(MAX_LOCAL_VM_MAX_INSTANCES).optional(),
@@ -19711,6 +19760,7 @@ var init_config = __esm({
19711
19760
  * system language. Unknown tags degrade to English in the renderer. */
19712
19761
  language: optionalText,
19713
19762
  rooms: roomConfigSchema.optional(),
19763
+ mcp: mcpConfigSchema.optional(),
19714
19764
  context: external_exports.object({
19715
19765
  rebuildBytes: external_exports.number().int().min(1024).max(1e6).optional(),
19716
19766
  compactAt: external_exports.number().positive().max(1e7).optional(),
@@ -19867,7 +19917,7 @@ var init_config = __esm({
19867
19917
  // electron/data-dir-lease.mjs
19868
19918
  import { execFileSync as execFileSync2 } from "node:child_process";
19869
19919
  import { createHash as createHash2, randomUUID as randomUUID5 } from "node:crypto";
19870
- import { existsSync as existsSync7, linkSync, mkdirSync as mkdirSync7, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
19920
+ import { existsSync as existsSync7, linkSync, mkdirSync as mkdirSync7, readFileSync as readFileSync5, renameSync as renameSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
19871
19921
  import { hostname as hostname4, uptime } from "node:os";
19872
19922
  import { join as join10 } from "node:path";
19873
19923
  function leaseError(message3, cause) {
@@ -19879,7 +19929,7 @@ function isPid(value) {
19879
19929
  function readBootSession() {
19880
19930
  try {
19881
19931
  if (process.platform === "linux") {
19882
- return normalizeBootId(readFileSync4("/proc/sys/kernel/random/boot_id", "utf8"));
19932
+ return normalizeBootId(readFileSync5("/proc/sys/kernel/random/boot_id", "utf8"));
19883
19933
  }
19884
19934
  if (process.platform === "darwin") {
19885
19935
  return normalizeBootId(execFileSync2("/usr/sbin/sysctl", ["-n", "kern.bootsessionuuid"], {
@@ -19934,7 +19984,7 @@ function isReaperOwner(value, targetToken) {
19934
19984
  function parseRecord(path3, invalidMessage, validate) {
19935
19985
  let raw;
19936
19986
  try {
19937
- raw = readFileSync4(path3, "utf8");
19987
+ raw = readFileSync5(path3, "utf8");
19938
19988
  } catch (error51) {
19939
19989
  if (error51?.code === "ENOENT") return null;
19940
19990
  throw leaseError("OpenMausBot cannot read the data-directory lease; refusing to start to protect its state.", error51);
@@ -20240,7 +20290,7 @@ var init_data_dir_lease2 = __esm({
20240
20290
 
20241
20291
  // server/env-path.ts
20242
20292
  import { execFile as execFile2 } from "node:child_process";
20243
- import { closeSync as closeSync3, existsSync as existsSync8, openSync as openSync3, readFileSync as readFileSync5, readSync, statSync as statSync3, readdirSync } from "node:fs";
20293
+ import { closeSync as closeSync3, existsSync as existsSync8, openSync as openSync3, readFileSync as readFileSync6, readSync, statSync as statSync3, readdirSync } from "node:fs";
20244
20294
  import { homedir as homedir5 } from "node:os";
20245
20295
  import { basename as basename4, delimiter as delimiter3, dirname as dirname5, extname, join as join11 } from "node:path";
20246
20296
  function nvmBinDirs() {
@@ -20435,7 +20485,7 @@ function nodeExe(near, env) {
20435
20485
  function parseCmdShim(shim, env) {
20436
20486
  let text4;
20437
20487
  try {
20438
- text4 = readFileSync5(shim, "utf8");
20488
+ text4 = readFileSync6(shim, "utf8");
20439
20489
  } catch {
20440
20490
  return null;
20441
20491
  }
@@ -20834,6 +20884,9 @@ var init_registry = __esm({
20834
20884
  /** decoded per-instance `cli` overrides, for describe() — drivers spawn
20835
20885
  * from their own config; this map only reports what was configured */
20836
20886
  cliByInstance = /* @__PURE__ */ new Map();
20887
+ /** What describe() last read of each live instance. A turn's start checks
20888
+ * a thread's own engine against it without waiting on that engine's CLI. */
20889
+ lastSnapshots = /* @__PURE__ */ new Map();
20837
20890
  driversByKind;
20838
20891
  /** Where Settings-driven npm installs go; the data directory by default. */
20839
20892
  enginesBaseDir;
@@ -20893,6 +20946,16 @@ var init_registry = __esm({
20893
20946
  get(instanceId) {
20894
20947
  return this.byId.get(instanceId)?.live ?? null;
20895
20948
  }
20949
+ /** The snapshot describe() last read of this live instance; absent before
20950
+ * the first read, or for an instance that is not live. */
20951
+ lastSnapshot(instanceId) {
20952
+ return this.byId.get(instanceId)?.live ? this.lastSnapshots.get(instanceId) : void 0;
20953
+ }
20954
+ /** How the instance's driver is presented and paid for (EngineAccess). */
20955
+ access(instanceId) {
20956
+ const entry = this.byId.get(instanceId);
20957
+ return entry ? this.driversByKind.get(entry.shadow?.driverKind ?? entry.live.driverKind)?.metadata.access ?? "subscription" : void 0;
20958
+ }
20896
20959
  /** The configured executable for instance-scoped maintenance actions.
20897
20960
  * This deliberately comes from the registry/config, never an HTTP body. */
20898
20961
  cliTarget(instanceId) {
@@ -20994,6 +21057,7 @@ var init_registry = __esm({
20994
21057
  } catch (e) {
20995
21058
  snapshot = { state: "unavailable", reason: e instanceof Error ? e.message : String(e) };
20996
21059
  }
21060
+ if (this.byId.get(inst.instanceId)?.live === inst) this.lastSnapshots.set(inst.instanceId, snapshot);
20997
21061
  return {
20998
21062
  instanceId: inst.instanceId,
20999
21063
  driverKind: inst.driverKind,
@@ -21035,11 +21099,13 @@ var init_registry = __esm({
21035
21099
  await Promise.allSettled(this.instances().map((i2) => i2.dispose()));
21036
21100
  this.byId.clear();
21037
21101
  this.cliByInstance.clear();
21102
+ this.lastSnapshots.clear();
21038
21103
  }
21039
21104
  async dispose(instanceId) {
21040
21105
  const entry = this.byId.get(instanceId);
21041
21106
  this.byId.delete(instanceId);
21042
21107
  this.cliByInstance.delete(instanceId);
21108
+ this.lastSnapshots.delete(instanceId);
21043
21109
  await entry?.live?.dispose();
21044
21110
  }
21045
21111
  };
@@ -21047,7 +21113,7 @@ var init_registry = __esm({
21047
21113
  });
21048
21114
 
21049
21115
  // server/drivers/local-inject.ts
21050
- import { readFileSync as readFileSync6 } from "node:fs";
21116
+ import { readFileSync as readFileSync7 } from "node:fs";
21051
21117
  import { homedir as homedir6 } from "node:os";
21052
21118
  import { join as join14 } from "node:path";
21053
21119
  function contextWindowsFromPs(extra) {
@@ -21134,7 +21200,7 @@ function firstUnslothToken(row) {
21134
21200
  function readUnslothKey(env) {
21135
21201
  const home = env.HOME || env.USERPROFILE || homedir6();
21136
21202
  try {
21137
- const raw = JSON.parse(readFileSync6(join14(home, ".unsloth", "studio", "auth", "agent_api_key.json"), "utf8"));
21203
+ const raw = JSON.parse(readFileSync7(join14(home, ".unsloth", "studio", "auth", "agent_api_key.json"), "utf8"));
21138
21204
  if (raw.servers && typeof raw.servers === "object") {
21139
21205
  const servers = raw.servers;
21140
21206
  for (const url2 of ["http://127.0.0.1:8888", "http://localhost:8888"]) {
@@ -21334,10 +21400,364 @@ var init_local_inject = __esm({
21334
21400
  }
21335
21401
  });
21336
21402
 
21403
+ // shared/device-sign-in.ts
21404
+ function deviceSignInLink(provider, value, userCode) {
21405
+ const page = value ? RULES[provider].page(value) : null;
21406
+ if (!page) return null;
21407
+ return [...new URL(page).searchParams.values()].every((carried) => carried === userCode) ? page : null;
21408
+ }
21409
+ function isDeviceSignInCode(provider, value) {
21410
+ return typeof value === "string" && RULES[provider].code.test(value);
21411
+ }
21412
+ function deviceSignInPrompt(provider, text4) {
21413
+ const lines = text4.split(/\r?\n/).slice(0, -1).map((line) => line.trim());
21414
+ const at = lines.findIndex((line) => RULES[provider].page(line) !== null);
21415
+ if (at < 0) return null;
21416
+ const userCode = lines.slice(at + 1).map((line) => line.replace(/^code:\s*/i, "")).find((line) => isDeviceSignInCode(provider, line));
21417
+ const authorizationUrl = userCode ? deviceSignInLink(provider, lines[at], userCode) : null;
21418
+ return authorizationUrl && userCode ? { authorizationUrl, userCode } : null;
21419
+ }
21420
+ var CODEX_PAGE, RULES;
21421
+ var init_device_sign_in = __esm({
21422
+ "shared/device-sign-in.ts"() {
21423
+ "use strict";
21424
+ CODEX_PAGE = "https://auth.openai.com/codex/device";
21425
+ RULES = {
21426
+ codex: {
21427
+ page: (value) => /^https:\/\/auth\.openai\.com\/codex\/device\/?$/.test(value) ? CODEX_PAGE : null,
21428
+ code: /^[A-Z0-9]{4,8}-[A-Z0-9]{4,8}$/
21429
+ },
21430
+ grok: {
21431
+ // xAI's own sign-in hosts. The page may carry the code itself (the
21432
+ // provider's "complete" link); nothing else may ride in its query.
21433
+ page: (value) => {
21434
+ if (!/^https:\/\/[^\s#]+$/.test(value)) return null;
21435
+ let url2;
21436
+ try {
21437
+ url2 = new URL(value);
21438
+ } catch {
21439
+ return null;
21440
+ }
21441
+ if (url2.username || url2.password || url2.port || !/(^|\.)(x\.ai|grok\.com)$/.test(url2.hostname)) return null;
21442
+ return [...url2.searchParams.keys()].every((key) => key === "user_code") ? url2.href : null;
21443
+ },
21444
+ code: /^[A-Z0-9]{4,12}(?:-[A-Z0-9]{2,12}){0,3}$/
21445
+ }
21446
+ };
21447
+ }
21448
+ });
21449
+
21450
+ // server/drivers/device-auth.ts
21451
+ import { randomUUID as randomUUID6 } from "node:crypto";
21452
+ import { realpathSync } from "node:fs";
21453
+ import { homedir as homedir7 } from "node:os";
21454
+ import { basename as basename5, dirname as dirname6, isAbsolute, join as join15, resolve as resolve3 } from "node:path";
21455
+ import { stripVTControlCharacters as stripVTControlCharacters4 } from "node:util";
21456
+ function canonicalPath(path3) {
21457
+ try {
21458
+ return realpathSync(path3);
21459
+ } catch {
21460
+ const parent = dirname6(path3);
21461
+ return parent === path3 ? path3 : join15(canonicalPath(parent), basename5(path3));
21462
+ }
21463
+ }
21464
+ var MAX_OUTPUT2, authenticatingHomes, DeviceAuthController;
21465
+ var init_device_auth = __esm({
21466
+ "server/drivers/device-auth.ts"() {
21467
+ "use strict";
21468
+ init_device_sign_in();
21469
+ init_procs();
21470
+ MAX_OUTPUT2 = 16384;
21471
+ authenticatingHomes = /* @__PURE__ */ new Set();
21472
+ DeviceAuthController = class {
21473
+ flow = null;
21474
+ command = null;
21475
+ disposed = false;
21476
+ spec;
21477
+ options;
21478
+ constructor(spec, options) {
21479
+ this.spec = spec;
21480
+ this.options = options;
21481
+ }
21482
+ /** The credential home the lock is held on, for this environment. */
21483
+ home(env, action) {
21484
+ const { homeEnv, homeDir, product } = this.spec;
21485
+ const home = env.HOME || env.USERPROFILE || homedir7();
21486
+ const named = env[homeEnv];
21487
+ if (!isAbsolute(home) || named && !isAbsolute(named)) {
21488
+ throw new Error(`Use an absolute HOME and ${homeEnv} path for this server's ${product} provider before ${action}.`);
21489
+ }
21490
+ return { home, homeKey: canonicalPath(resolve3(named || join15(home, homeDir))) };
21491
+ }
21492
+ async start() {
21493
+ const { product, account, status } = this.spec;
21494
+ if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing in.");
21495
+ if (this.flow?.status.phase === "waiting") {
21496
+ if (this.flow.ready) return { ...this.flow.status, phase: "waiting" };
21497
+ throw new Error(`A ${account} sign-in is already starting. Please wait for the code.`);
21498
+ }
21499
+ await this.flow?.stopping;
21500
+ if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing in.");
21501
+ const env = this.options.environment();
21502
+ const { home, homeKey } = this.home(env, "signing in");
21503
+ if (authenticatingHomes.has(homeKey)) {
21504
+ throw new Error(`A ${account} sign-in is already running for this server account. Finish or cancel it before starting another.`);
21505
+ }
21506
+ authenticatingHomes.add(homeKey);
21507
+ return new Promise((resolveStart, reject) => {
21508
+ const flow = {
21509
+ status: {
21510
+ phase: "waiting",
21511
+ flowId: randomUUID6(),
21512
+ authorizationUrl: null,
21513
+ expiresAt: new Date(Date.now() + (this.options.lifetimeMs ?? 15 * 6e4)).toISOString()
21514
+ },
21515
+ child: null,
21516
+ ready: false,
21517
+ resolve: resolveStart,
21518
+ reject,
21519
+ homeKey,
21520
+ startupTimer: setTimeout(() => this.finish(flow, "failed", `${product} did not provide a sign-in code in time. Check the server connection and update ${product}, then try again.`), this.options.startupTimeoutMs ?? 3e4),
21521
+ expiryTimer: setTimeout(() => this.finish(flow, "expired", `The ${account} sign-in code expired. Start sign-in again.`), this.options.lifetimeMs ?? 15 * 6e4)
21522
+ };
21523
+ flow.startupTimer.unref();
21524
+ flow.expiryTimer.unref();
21525
+ this.flow = flow;
21526
+ const login = () => this.run(flow, [...this.spec.loginArgs], env, home, (loginCode, loginOutput) => {
21527
+ if (loginCode !== 0 || !flow.ready) {
21528
+ this.finish(flow, "failed", this.spec.failure(loginOutput));
21529
+ return;
21530
+ }
21531
+ flow.startupTimer = setTimeout(() => this.finish(flow, "failed", `${product} finished sign-in but could not confirm the account. Refresh Settings and try again.`), this.options.startupTimeoutMs ?? 3e4);
21532
+ flow.startupTimer.unref();
21533
+ this.confirm(flow, env, home, (signedIn) => this.finish(
21534
+ flow,
21535
+ signedIn ? "succeeded" : "failed",
21536
+ `${product} finished sign-in but did not confirm a ${account} account. Refresh Settings and try again.`
21537
+ ));
21538
+ }, true);
21539
+ if (!status) {
21540
+ login();
21541
+ return;
21542
+ }
21543
+ this.run(flow, [...status.args], env, home, (code, output) => {
21544
+ const state = status.read(code, output);
21545
+ if (state === "signed-in") this.finish(flow, "succeeded");
21546
+ else if (state === "other") this.finish(flow, "failed", `${product} already has a different sign-in method on this server. Ask the server administrator to review it before changing accounts.`);
21547
+ else if (state === "signed-out") login();
21548
+ else this.finish(flow, "failed", `${product} could not confirm the existing sign-in on this server. Update ${product} and check its login status before trying again.`);
21549
+ });
21550
+ });
21551
+ }
21552
+ async get(flowId) {
21553
+ if (!flowId || this.flow?.status.flowId !== flowId) throw new Error("This sign-in is no longer available. Start sign-in again.");
21554
+ return { ...this.flow.status };
21555
+ }
21556
+ async cancel() {
21557
+ if (this.flow?.status.phase === "waiting") this.finish(this.flow, "cancelled", `${this.spec.account} sign-in cancelled.`);
21558
+ await this.flow?.stopping;
21559
+ if (this.flow?.terminationFailed) throw new Error(this.flow.status.message);
21560
+ if (this.command && !await this.stopChild(this.command)) {
21561
+ throw new Error(`${this.spec.product} could not be stopped on this server. Ask the server administrator to stop the account command before trying again.`);
21562
+ }
21563
+ }
21564
+ async dispose() {
21565
+ this.disposed = true;
21566
+ await this.cancel();
21567
+ this.flow = null;
21568
+ }
21569
+ /** Remove the sign-in the CLI stores for this server account so a
21570
+ * different account can connect. A sign-in in progress is never pulled
21571
+ * away underneath the browser completing it. */
21572
+ async signOut() {
21573
+ const { product, account, status, logoutArgs } = this.spec;
21574
+ if (!status || !logoutArgs) throw new Error(`Signing out of ${account} is not available from here.`);
21575
+ if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing out.");
21576
+ if (this.flow?.status.phase === "waiting") throw new Error(`Finish or cancel the ${account} sign-in in progress before signing out.`);
21577
+ await this.flow?.stopping;
21578
+ if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing out.");
21579
+ const env = this.options.environment();
21580
+ const { home, homeKey } = this.home(env, "signing out");
21581
+ if (authenticatingHomes.has(homeKey)) {
21582
+ throw new Error(`A ${account} sign-in is running for this server account. Finish or cancel it before signing out.`);
21583
+ }
21584
+ authenticatingHomes.add(homeKey);
21585
+ try {
21586
+ const before = await this.exec([...status.args], env, home);
21587
+ const state = status.read(before.code, before.output);
21588
+ if (state === "signed-out") return;
21589
+ if (state !== "signed-in") {
21590
+ throw new Error(`${product} did not confirm a ${account} sign-in. Other authentication methods will not be removed; ask the server administrator to check the account.`);
21591
+ }
21592
+ const logout = await this.exec([...logoutArgs], env, home);
21593
+ if (logout.code !== 0) throw new Error(`${product} could not remove the sign-in on this server. Check the server's ${product} installation and try again.`);
21594
+ const after = await this.exec([...status.args], env, home);
21595
+ if (status.read(after.code, after.output) !== "signed-out") {
21596
+ throw new Error(`${product} still reports a sign-in on this server. Update ${product} and check its login status before trying again.`);
21597
+ }
21598
+ } finally {
21599
+ const child = this.command;
21600
+ if (child && child.exitCode === null && child.signalCode === null) {
21601
+ child.once("close", () => authenticatingHomes.delete(homeKey));
21602
+ } else authenticatingHomes.delete(homeKey);
21603
+ }
21604
+ }
21605
+ /** After a login command succeeded: the status command when the CLI has
21606
+ * one, else the stored sign-in a turn checks for. */
21607
+ confirm(flow, env, home, done) {
21608
+ const status = this.spec.status;
21609
+ if (status) {
21610
+ this.run(flow, [...status.args], env, home, (code, output) => done(status.read(code, output) === "signed-in"));
21611
+ return;
21612
+ }
21613
+ let signedIn = false;
21614
+ try {
21615
+ signedIn = this.spec.signedIn?.(env) === true;
21616
+ } catch {
21617
+ }
21618
+ done(signedIn);
21619
+ }
21620
+ startFailure(error51) {
21621
+ const { product, install } = this.spec;
21622
+ return error51.code === "ENOENT" ? `${product} is not installed on this server. ${install}` : `${product} could not start on this server. Check the configured CLI path and its executable permissions.`;
21623
+ }
21624
+ /** One bounded, non-interactive command. Its output stays here; callers
21625
+ * see an exit code and a status-line match, never the text. */
21626
+ exec(args, env, cwd) {
21627
+ const { product } = this.spec;
21628
+ return new Promise((resolveExec, rejectExec) => {
21629
+ if (this.disposed) {
21630
+ rejectExec(new Error("This provider was removed. Refresh Settings before signing out."));
21631
+ return;
21632
+ }
21633
+ let child;
21634
+ try {
21635
+ child = spawnCli(this.options.cli, args, { env: { ...env, NO_COLOR: "1" }, cwd, stdio: ["pipe", "pipe", "pipe"] });
21636
+ } catch {
21637
+ rejectExec(new Error(`${product} could not start on this server. Install or update the configured ${product} CLI, then try again.`));
21638
+ return;
21639
+ }
21640
+ this.command = child;
21641
+ child.stdin.end();
21642
+ let output = "";
21643
+ const receive = (chunk) => {
21644
+ if (output.length < MAX_OUTPUT2) output += chunk.toString("utf8").slice(0, MAX_OUTPUT2 - output.length);
21645
+ };
21646
+ child.stdout.on("data", receive);
21647
+ child.stderr.on("data", receive);
21648
+ const timer = setTimeout(() => {
21649
+ void this.stopChild(child).catch(() => false).then((stopped) => {
21650
+ if (!stopped) rejectExec(new Error(`${product} could not be stopped on this server. Ask the server administrator to stop the account command before trying again.`));
21651
+ });
21652
+ }, this.options.startupTimeoutMs ?? 3e4);
21653
+ timer.unref();
21654
+ child.once("error", (error51) => {
21655
+ clearTimeout(timer);
21656
+ rejectExec(new Error(this.startFailure(error51)));
21657
+ });
21658
+ child.once("close", (code) => {
21659
+ clearTimeout(timer);
21660
+ if (this.command === child) this.command = null;
21661
+ resolveExec({ code, output: stripVTControlCharacters4(output) });
21662
+ });
21663
+ });
21664
+ }
21665
+ run(flow, args, env, cwd, done, parsePrompt = false) {
21666
+ const { product, provider } = this.spec;
21667
+ if (flow.status.phase !== "waiting") return;
21668
+ let child;
21669
+ try {
21670
+ child = spawnCli(this.options.cli, args, { env: { ...env, NO_COLOR: "1" }, cwd, stdio: ["pipe", "pipe", "pipe"] });
21671
+ } catch {
21672
+ this.finish(flow, "failed", `${product} could not start on this server. Install or update the configured ${product} CLI, then try again.`);
21673
+ return;
21674
+ }
21675
+ flow.child = child;
21676
+ child.stdin.end();
21677
+ let output = "";
21678
+ const receive = (chunk) => {
21679
+ if (flow.status.phase !== "waiting") return;
21680
+ if (output.length + chunk.length > MAX_OUTPUT2) {
21681
+ this.finish(flow, "failed", `${product} returned an unexpected sign-in response. Update the server's ${product} CLI and try again.`);
21682
+ return;
21683
+ }
21684
+ output += chunk.toString("utf8");
21685
+ const prompt = parsePrompt && !flow.ready ? deviceSignInPrompt(provider, stripVTControlCharacters4(output)) : null;
21686
+ if (prompt) {
21687
+ flow.status = { ...flow.status, ...prompt };
21688
+ flow.ready = true;
21689
+ clearTimeout(flow.startupTimer);
21690
+ flow.resolve({ ...flow.status, phase: "waiting" });
21691
+ }
21692
+ };
21693
+ child.stdout.on("data", receive);
21694
+ child.stderr.on("data", receive);
21695
+ child.once("error", (error51) => this.finish(flow, "failed", this.startFailure(error51)));
21696
+ child.once("close", (code) => {
21697
+ if (flow.child === child) flow.child = null;
21698
+ if (flow.status.phase === "waiting") done(code, stripVTControlCharacters4(output));
21699
+ output = "";
21700
+ });
21701
+ }
21702
+ finish(flow, phase, message3) {
21703
+ const { product, account } = this.spec;
21704
+ if (flow.status.phase !== "waiting") return;
21705
+ clearTimeout(flow.startupTimer);
21706
+ clearTimeout(flow.expiryTimer);
21707
+ flow.status = {
21708
+ phase,
21709
+ flowId: flow.status.flowId,
21710
+ authorizationUrl: null,
21711
+ expiresAt: null,
21712
+ ...phase !== "succeeded" && message3 ? { message: message3 } : {}
21713
+ };
21714
+ if (!flow.ready) {
21715
+ if (phase === "succeeded") flow.resolve({ ...flow.status, phase });
21716
+ else flow.reject(new Error(message3 ?? `${account} sign-in did not finish.`));
21717
+ }
21718
+ flow.stopping = this.stopChild(flow.child).catch(() => false).then((stopped) => {
21719
+ const release = () => authenticatingHomes.delete(flow.homeKey);
21720
+ if (stopped) {
21721
+ release();
21722
+ return;
21723
+ }
21724
+ flow.terminationFailed = true;
21725
+ flow.status = { ...flow.status, phase: "failed", message: `${product} could not be stopped on this server. Ask the server administrator to stop the login process before trying again.` };
21726
+ const child = flow.child;
21727
+ if (!child || child.exitCode !== null || child.signalCode !== null) release();
21728
+ else child.once("close", release);
21729
+ });
21730
+ if (phase === "succeeded") void this.options.onAuthenticated?.().catch(() => {
21731
+ });
21732
+ }
21733
+ async stopChild(child) {
21734
+ if (!child || await killCliTree(child, this.options.terminateTimeoutMs ?? 1500)) return true;
21735
+ if (process.platform !== "win32" && child.pid) {
21736
+ try {
21737
+ process.kill(-child.pid, "SIGKILL");
21738
+ } catch {
21739
+ child.kill("SIGKILL");
21740
+ }
21741
+ } else child.kill("SIGKILL");
21742
+ await new Promise((resolveStop) => {
21743
+ if (child.exitCode !== null || child.signalCode !== null) return resolveStop();
21744
+ const timer = setTimeout(resolveStop, 1500);
21745
+ timer.unref();
21746
+ child.once("close", () => {
21747
+ clearTimeout(timer);
21748
+ resolveStop();
21749
+ });
21750
+ });
21751
+ return child.exitCode !== null || child.signalCode !== null;
21752
+ }
21753
+ };
21754
+ }
21755
+ });
21756
+
21337
21757
  // server/drivers/prompt-split.ts
21338
21758
  import { createHash as createHash3 } from "node:crypto";
21339
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync7, rmSync as rmSync4 } from "node:fs";
21340
- import { join as join15 } from "node:path";
21759
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync8, rmSync as rmSync4 } from "node:fs";
21760
+ import { join as join16 } from "node:path";
21341
21761
  function promptHalves(turn) {
21342
21762
  const { systemStable, systemVolatile } = turn;
21343
21763
  if (typeof systemStable !== "string" || typeof systemVolatile !== "string") {
@@ -21359,7 +21779,7 @@ function promptSplitFingerprints(stable, volatile) {
21359
21779
  }
21360
21780
  function readPromptSplitReceipt(scope, key) {
21361
21781
  try {
21362
- const raw = JSON.parse(readFileSync7(receiptPath(scope, key), "utf8"));
21782
+ const raw = JSON.parse(readFileSync8(receiptPath(scope, key), "utf8"));
21363
21783
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
21364
21784
  const record3 = raw;
21365
21785
  if (typeof record3.stable === "string" && typeof record3.volatile === "string") {
@@ -21377,7 +21797,7 @@ function readPromptSplitReceipt(scope, key) {
21377
21797
  return null;
21378
21798
  }
21379
21799
  function writePromptSplitReceipt(scope, key, receipt) {
21380
- mkdirSync9(join15(DATA_DIR, "prompt-split"), { recursive: true });
21800
+ mkdirSync9(join16(DATA_DIR, "prompt-split"), { recursive: true });
21381
21801
  writeFileAtomic(receiptPath(scope, key), JSON.stringify(receipt), { mode: 384 });
21382
21802
  }
21383
21803
  function deletePromptSplitReceipt(scope, key) {
@@ -21406,13 +21826,13 @@ var init_prompt_split = __esm({
21406
21826
  VOLATILE_CONTEXT_CLEARED_NOTE = "The OpenMausBot context notes from earlier in this conversation have been cleared; the standing instructions still apply.";
21407
21827
  digest = (value) => createHash3("sha256").update(value).digest("hex");
21408
21828
  EMPTY_FINGERPRINT = digest("");
21409
- receiptPath = (scope, key) => join15(DATA_DIR, "prompt-split", digest(JSON.stringify([scope, key])) + ".json");
21829
+ receiptPath = (scope, key) => join16(DATA_DIR, "prompt-split", digest(JSON.stringify([scope, key])) + ".json");
21410
21830
  }
21411
21831
  });
21412
21832
 
21413
21833
  // server/drivers/acp/quiet-status.ts
21414
21834
  import { execFile as execFile4 } from "node:child_process";
21415
- import { closeSync as closeSync4, fstatSync, openSync as openSync4, readSync as readSync2 } from "node:fs";
21835
+ import { closeSync as closeSync4, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync2 } from "node:fs";
21416
21836
  function parseQwenLogLine(line) {
21417
21837
  if (line.includes("[RETRY]") && /\bAttempt \d+ failed\b/.test(line)) {
21418
21838
  const attempt = /\bAttempt (\d+) failed/.exec(line);
@@ -21566,7 +21986,7 @@ var init_quiet_status = __esm({
21566
21986
  return [];
21567
21987
  }
21568
21988
  try {
21569
- const size = fstatSync(fd).size;
21989
+ const size = fstatSync2(fd).size;
21570
21990
  if (this.offset === null) {
21571
21991
  this.offset = size;
21572
21992
  return [];
@@ -21818,7 +22238,7 @@ var init_ask_question = __esm({
21818
22238
  });
21819
22239
 
21820
22240
  // server/thread-log-rotation.ts
21821
- import { closeSync as closeSync5, fstatSync as fstatSync2, openSync as openSync5, readSync as readSync3, renameSync as renameSync5, rmSync as rmSync5, statSync as statSync4, writeSync } from "node:fs";
22241
+ import { closeSync as closeSync5, fstatSync as fstatSync3, openSync as openSync5, readSync as readSync3, renameSync as renameSync5, rmSync as rmSync5, statSync as statSync4, writeSync } from "node:fs";
21822
22242
  function currentThreadLogCap() {
21823
22243
  try {
21824
22244
  return capProvider();
@@ -21842,7 +22262,7 @@ function trimToTail(file2, keepBytes) {
21842
22262
  try {
21843
22263
  const source = openSync5(file2, "r");
21844
22264
  try {
21845
- const size = fstatSync2(source).size;
22265
+ const size = fstatSync3(source).size;
21846
22266
  const start = Math.max(0, size - keepBytes);
21847
22267
  const window = Buffer.allocUnsafe(size - start);
21848
22268
  let filled = 0;
@@ -21885,9 +22305,9 @@ var init_thread_log_rotation = __esm({
21885
22305
 
21886
22306
  // server/drivers/native.ts
21887
22307
  import { appendFileSync as appendFileSync3 } from "node:fs";
21888
- import { join as join16 } from "node:path";
22308
+ import { join as join17 } from "node:path";
21889
22309
  function appendNative(threadId, entry) {
21890
- const file2 = join16(NATIVE_DIR, `${threadId}.ndjson`);
22310
+ const file2 = join17(NATIVE_DIR, `${threadId}.ndjson`);
21891
22311
  try {
21892
22312
  appendFileSync3(
21893
22313
  file2,
@@ -21908,16 +22328,16 @@ var init_native = __esm({
21908
22328
  });
21909
22329
 
21910
22330
  // server/drivers/permission-command.ts
21911
- import { realpathSync } from "node:fs";
21912
- import { isAbsolute } from "node:path";
22331
+ import { realpathSync as realpathSync2 } from "node:fs";
22332
+ import { isAbsolute as isAbsolute2 } from "node:path";
21913
22333
  function permissionCommand(command, cwd) {
21914
22334
  if (typeof command !== "string" || !command.trim() || command.includes("\0")) return void 0;
21915
- if (typeof cwd !== "string" || !isAbsolute(cwd) || cwd.includes("\0")) return void 0;
22335
+ if (typeof cwd !== "string" || !isAbsolute2(cwd) || cwd.includes("\0")) return void 0;
21916
22336
  return { command, cwd };
21917
22337
  }
21918
22338
  function permissionLaunchCwd(cwd) {
21919
22339
  try {
21920
- return realpathSync(cwd);
22340
+ return realpathSync2(cwd);
21921
22341
  } catch {
21922
22342
  return void 0;
21923
22343
  }
@@ -21926,7 +22346,7 @@ function acpPermissionCommand(rawInput, launchCwd) {
21926
22346
  if (!rawInput || typeof rawInput !== "object" || Array.isArray(rawInput)) return void 0;
21927
22347
  const input = rawInput;
21928
22348
  const directories = ["cwd", "workdir", "working_directory", "directory", "dir"].filter((key) => input[key] !== void 0).map((key) => input[key]);
21929
- if (directories.some((value) => typeof value !== "string" || !isAbsolute(value) || value.includes("\0"))) return void 0;
22349
+ if (directories.some((value) => typeof value !== "string" || !isAbsolute2(value) || value.includes("\0"))) return void 0;
21930
22350
  if (new Set(directories).size > 1) return void 0;
21931
22351
  return permissionCommand(input.command, directories[0] ?? launchCwd);
21932
22352
  }
@@ -22201,7 +22621,7 @@ var init_mcp_http = __esm({
22201
22621
  });
22202
22622
 
22203
22623
  // server/mcp-gate-config.ts
22204
- import { join as join17 } from "node:path";
22624
+ import { join as join18 } from "node:path";
22205
22625
  function resultBudget(env = process.env) {
22206
22626
  const raw = env.OMB_MCP_RESULT_BUDGET;
22207
22627
  if (raw === void 0 || raw === "") return DEFAULT_RESULT_BUDGET;
@@ -22209,7 +22629,7 @@ function resultBudget(env = process.env) {
22209
22629
  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : DEFAULT_RESULT_BUDGET;
22210
22630
  }
22211
22631
  function spillDir(threadId) {
22212
- return join17(DATA_DIR, "tool-results", threadId.replace(/[^A-Za-z0-9_-]/g, "-").slice(0, 80) || "thread");
22632
+ return join18(DATA_DIR, "tool-results", threadId.replace(/[^A-Za-z0-9_-]/g, "-").slice(0, 80) || "thread");
22213
22633
  }
22214
22634
  function mcpStdioServer(server, options = {}) {
22215
22635
  if (!server || typeof server !== "object" || Array.isArray(server)) return null;
@@ -22266,9 +22686,9 @@ var init_mcp_gate_config = __esm({
22266
22686
  });
22267
22687
 
22268
22688
  // server/drivers/acp/core.ts
22269
- import { homedir as homedir7 } from "node:os";
22689
+ import { homedir as homedir8 } from "node:os";
22270
22690
  import { lstat, mkdir as mkdir3, readFile, realpath, stat as stat2, writeFile } from "node:fs/promises";
22271
- import { basename as basename5, dirname as dirname6, isAbsolute as isAbsolute2, relative, resolve as resolve3 } from "node:path";
22691
+ import { basename as basename6, dirname as dirname7, isAbsolute as isAbsolute3, relative, resolve as resolve4 } from "node:path";
22272
22692
  import { createHash as createHash4 } from "node:crypto";
22273
22693
  function skipSubscriptionAuthForLocalInject(model) {
22274
22694
  return Boolean(decodeInjectId(model));
@@ -22474,7 +22894,8 @@ function createAcpDriver(support11) {
22474
22894
  }
22475
22895
  };
22476
22896
  if (support11.resolveModelsOnCreate !== false) await refreshModels();
22477
- const listeners = /* @__PURE__ */ new Set();
22897
+ const deviceSignIn = support11.deviceSignIn ? new DeviceAuthController(support11.deviceSignIn, { cli: config2.cli, environment: () => childEnv(), onAuthenticated: refreshModels }) : null;
22898
+ const listeners2 = /* @__PURE__ */ new Set();
22478
22899
  const active = /* @__PURE__ */ new Map();
22479
22900
  const sessions = /* @__PURE__ */ new Map();
22480
22901
  const retiring = /* @__PURE__ */ new Map();
@@ -22519,7 +22940,7 @@ function createAcpDriver(support11) {
22519
22940
  };
22520
22941
  const sessionAllows = /* @__PURE__ */ new Map();
22521
22942
  const emit = (event) => {
22522
- for (const listener of listeners) listener(event);
22943
+ for (const listener of listeners2) listener(event);
22523
22944
  };
22524
22945
  const nativeLogMessage = (msg) => {
22525
22946
  let redacted = msg;
@@ -22801,26 +23222,26 @@ function createAcpDriver(support11) {
22801
23222
  }
22802
23223
  };
22803
23224
  const resolveClientPath = async (requestPath) => {
22804
- if (typeof requestPath !== "string" || !isAbsolute2(requestPath)) {
23225
+ if (typeof requestPath !== "string" || !isAbsolute3(requestPath)) {
22805
23226
  throw new Error("ACP file paths must be absolute.");
22806
23227
  }
22807
- const workspace = await realpath(cwd).catch(() => resolve3(cwd));
22808
- const requested = resolve3(requestPath);
22809
- const lexical = relative(resolve3(cwd), requested);
22810
- if (lexical === ".." || lexical.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute2(lexical)) {
23228
+ const workspace = await realpath(cwd).catch(() => resolve4(cwd));
23229
+ const requested = resolve4(requestPath);
23230
+ const lexical = relative(resolve4(cwd), requested);
23231
+ if (lexical === ".." || lexical.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute3(lexical)) {
22811
23232
  throw new Error("ACP file path is outside the session workspace.");
22812
23233
  }
22813
- const suffix = [basename5(requested)];
22814
- let ancestor = dirname6(requested);
23234
+ const suffix = [basename6(requested)];
23235
+ let ancestor = dirname7(requested);
22815
23236
  while (!await lstat(ancestor).catch(() => null)) {
22816
- const parent = dirname6(ancestor);
23237
+ const parent = dirname7(ancestor);
22817
23238
  if (parent === ancestor) throw new Error("ACP file path has no accessible parent.");
22818
- suffix.unshift(basename5(ancestor));
23239
+ suffix.unshift(basename6(ancestor));
22819
23240
  ancestor = parent;
22820
23241
  }
22821
- const candidate = resolve3(await realpath(ancestor), ...suffix);
23242
+ const candidate = resolve4(await realpath(ancestor), ...suffix);
22822
23243
  const rel = relative(workspace, candidate);
22823
- if (rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute2(rel)) {
23244
+ if (rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute3(rel)) {
22824
23245
  throw new Error("ACP file path is outside the session workspace.");
22825
23246
  }
22826
23247
  const existing = await lstat(candidate).catch(() => null);
@@ -22861,7 +23282,7 @@ function createAcpDriver(support11) {
22861
23282
  if (typeof params.content !== "string" || Buffer.byteLength(params.content) > CLIENT_FILE_MAX_BYTES) {
22862
23283
  throw new Error(`ACP can only write text files under ${CLIENT_FILE_MAX_BYTES} bytes.`);
22863
23284
  }
22864
- await mkdir3(dirname6(path3), { recursive: true });
23285
+ await mkdir3(dirname7(path3), { recursive: true });
22865
23286
  await writeFile(path3, params.content, "utf8");
22866
23287
  send({ jsonrpc: "2.0", id: msg.id, result: {} });
22867
23288
  } catch (error51) {
@@ -23152,7 +23573,7 @@ function createAcpDriver(support11) {
23152
23573
  throw new Error("local computer control requires interactive provider approvals");
23153
23574
  }
23154
23575
  const turnId = newId();
23155
- const cwd = turn.cwd ?? turnConfig.workspace ?? homedir7();
23576
+ const cwd = turn.cwd ?? turnConfig.workspace ?? homedir8();
23156
23577
  const env = childEnv(turnConfig);
23157
23578
  if (support11.requireAuthenticationBeforeSpawn && !skipSubscriptionAuthForLocalInject(turn.model) && !await support11.isAuthenticated(env, turnConfig, instanceId)) {
23158
23579
  emit({ ...base2(threadId, turnId), type: "turn.started" });
@@ -23684,6 +24105,11 @@ ${promptInput.text}` : promptInput.text;
23684
24105
  return models2;
23685
24106
  },
23686
24107
  refreshModels: support11.resolveModels ? refreshModels : void 0,
24108
+ ...deviceSignIn ? {
24109
+ startAuthentication: () => deviceSignIn.start(),
24110
+ getAuthentication: (flowId) => deviceSignIn.get(flowId),
24111
+ cancelAuthentication: () => deviceSignIn.cancel()
24112
+ } : {},
23687
24113
  snapshot,
23688
24114
  adapter: {
23689
24115
  provider: DRIVER_KIND7,
@@ -23720,17 +24146,18 @@ ${promptInput.text}` : promptInput.text;
23720
24146
  }
23721
24147
  },
23722
24148
  onEvent: (listener) => {
23723
- listeners.add(listener);
23724
- return () => listeners.delete(listener);
24149
+ listeners2.add(listener);
24150
+ return () => listeners2.delete(listener);
23725
24151
  }
23726
24152
  },
23727
24153
  dispose: async () => {
24154
+ await deviceSignIn?.dispose();
23728
24155
  for (const { stop } of active.values()) stop();
23729
24156
  for (const threadId of Array.from(sessions.keys())) closeSession(threadId, "dispose");
23730
24157
  for (const [threadId, children] of retiring) {
23731
24158
  for (const child of children) void retireChild(threadId, child);
23732
24159
  }
23733
- listeners.clear();
24160
+ listeners2.clear();
23734
24161
  }
23735
24162
  };
23736
24163
  }
@@ -23742,6 +24169,7 @@ var init_core3 = __esm({
23742
24169
  "use strict";
23743
24170
  init_config();
23744
24171
  init_local_inject();
24172
+ init_device_auth();
23745
24173
  init_prompt_split();
23746
24174
  init_procs();
23747
24175
  init_quiet_status();
@@ -23789,15 +24217,15 @@ var init_core3 = __esm({
23789
24217
  });
23790
24218
 
23791
24219
  // server/drivers/antigravity-temp.ts
23792
- import { createHash as createHash5, randomUUID as randomUUID6 } from "node:crypto";
24220
+ import { createHash as createHash5, randomUUID as randomUUID7 } from "node:crypto";
23793
24221
  import { lstat as lstat2, readdir as readdir2, realpath as realpath2, rename, rm } from "node:fs/promises";
23794
- import { basename as basename6, dirname as dirname7, join as join18, resolve as resolve4 } from "node:path";
24222
+ import { basename as basename7, dirname as dirname8, join as join19, resolve as resolve5 } from "node:path";
23795
24223
  function antigravityTempRoot(dataDir = DATA_DIR) {
23796
- return join18(dataDir, "tmp", "agy");
24224
+ return join19(dataDir, "tmp", "agy");
23797
24225
  }
23798
24226
  function antigravityTempDir(dataDir, instanceId) {
23799
24227
  const key = createHash5("sha256").update(instanceId).digest("hex").slice(0, 12);
23800
- return join18(antigravityTempRoot(dataDir), key);
24228
+ return join19(antigravityTempRoot(dataDir), key);
23801
24229
  }
23802
24230
  function setAntigravityTempEnvironment(environment, directory, platform = process.platform) {
23803
24231
  if (platform !== "win32") return;
@@ -23849,12 +24277,12 @@ async function listDirectory(path3) {
23849
24277
  }
23850
24278
  }
23851
24279
  async function ownedTempDirectories(dataDir, only) {
23852
- const root = resolve4(antigravityTempRoot(dataDir));
24280
+ const root = resolve5(antigravityTempRoot(dataDir));
23853
24281
  let realRoot;
23854
24282
  try {
23855
- const realData = await realpath2(resolve4(dataDir));
24283
+ const realData = await realpath2(resolve5(dataDir));
23856
24284
  realRoot = await realpath2(root);
23857
- if (realRoot !== join18(realData, "tmp", "agy")) return [];
24285
+ if (realRoot !== join19(realData, "tmp", "agy")) return [];
23858
24286
  } catch {
23859
24287
  return [];
23860
24288
  }
@@ -23862,9 +24290,9 @@ async function ownedTempDirectories(dataDir, only) {
23862
24290
  const owned = [];
23863
24291
  for (const name of names) {
23864
24292
  if (!INSTANCE_FOLDER.test(name)) continue;
23865
- const directory = join18(root, name);
24293
+ const directory = join19(root, name);
23866
24294
  try {
23867
- if (await realpath2(directory) !== join18(realRoot, name)) continue;
24295
+ if (await realpath2(directory) !== join19(realRoot, name)) continue;
23868
24296
  } catch {
23869
24297
  continue;
23870
24298
  }
@@ -23877,7 +24305,7 @@ async function abandonedIn(directory, isAlive) {
23877
24305
  for (const entry of await listDirectory(directory)) {
23878
24306
  if (!entry.isDirectory()) continue;
23879
24307
  if (entry.name.startsWith(CLAIM_PREFIX) || ownedByDeadProcess(entry.name, isAlive)) {
23880
- found.push(join18(directory, entry.name));
24308
+ found.push(join19(directory, entry.name));
23881
24309
  }
23882
24310
  }
23883
24311
  return found;
@@ -23889,7 +24317,7 @@ async function folderBytes(directory, deadline = Infinity) {
23889
24317
  if (Date.now() > deadline) return { bytes, complete: false };
23890
24318
  const current = pending.pop();
23891
24319
  for (const entry of await listDirectory(current)) {
23892
- const path3 = join18(current, entry.name);
24320
+ const path3 = join19(current, entry.name);
23893
24321
  if (entry.isDirectory()) pending.push(path3);
23894
24322
  else if (entry.isFile()) {
23895
24323
  try {
@@ -23902,12 +24330,12 @@ async function folderBytes(directory, deadline = Infinity) {
23902
24330
  return { bytes, complete: true };
23903
24331
  }
23904
24332
  async function claimAndRemove(directory, countBytes) {
23905
- const name = basename6(directory);
24333
+ const name = basename7(directory);
23906
24334
  const kind = await entryKind(directory);
23907
24335
  if (kind !== "directory") return kind === "gone" ? "gone" : null;
23908
24336
  let claimed = directory;
23909
24337
  if (!name.startsWith(CLAIM_PREFIX)) {
23910
- claimed = join18(dirname7(directory), `${CLAIM_PREFIX}${name}-${randomUUID6().slice(0, 8)}`);
24338
+ claimed = join19(dirname8(directory), `${CLAIM_PREFIX}${name}-${randomUUID7().slice(0, 8)}`);
23911
24339
  try {
23912
24340
  await rename(directory, claimed);
23913
24341
  } catch (error51) {
@@ -23931,7 +24359,7 @@ function sweepAntigravityTemp(instanceId, options = {}) {
23931
24359
  const sweep = (async () => {
23932
24360
  const isAlive = options.isAlive ?? processIsAlive3;
23933
24361
  let removed = 0;
23934
- for (const directory of await ownedTempDirectories(dataDir, basename6(key))) {
24362
+ for (const directory of await ownedTempDirectories(dataDir, basename7(key))) {
23935
24363
  for (const folder of await abandonedIn(directory, isAlive)) {
23936
24364
  if (typeof await claimAndRemove(folder, false) === "number") removed++;
23937
24365
  }
@@ -23961,11 +24389,11 @@ var init_antigravity_temp = __esm({
23961
24389
  });
23962
24390
 
23963
24391
  // server/drivers/antigravity-acp.ts
23964
- import { createHash as createHash6, randomUUID as randomUUID7 } from "node:crypto";
24392
+ import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
23965
24393
  import { mkdir as mkdir4, mkdtemp, readFile as readFile2, rm as rm2, stat as stat3 } from "node:fs/promises";
23966
24394
  import { request as httpRequest } from "node:http";
23967
24395
  import { tmpdir as tmpdir2 } from "node:os";
23968
- import { delimiter as delimiter4, join as join19, resolve as resolve5 } from "node:path";
24396
+ import { delimiter as delimiter4, join as join20, resolve as resolve6 } from "node:path";
23969
24397
  function quoteBrowserArgument(value) {
23970
24398
  return `'${value.replaceAll("'", `'"'"'`)}'`;
23971
24399
  }
@@ -24000,11 +24428,11 @@ function authorizationUrlFromLine(line) {
24000
24428
  }
24001
24429
  }
24002
24430
  function antigravityProfileDirectory(instanceId, baseDir = DATA_DIR) {
24003
- return join19(baseDir, "providers", "antigravity", createHash6("sha256").update(instanceId).digest("hex"));
24431
+ return join20(baseDir, "providers", "antigravity", createHash6("sha256").update(instanceId).digest("hex"));
24004
24432
  }
24005
24433
  async function prepareAntigravityProfile(input) {
24006
- const directory = resolve5(input.profileDirectory ?? antigravityProfileDirectory(input.instanceId, input.baseDir));
24007
- const acpDirectory = join19(directory, "antigravity-acp");
24434
+ const directory = resolve6(input.profileDirectory ?? antigravityProfileDirectory(input.instanceId, input.baseDir));
24435
+ const acpDirectory = join20(directory, "antigravity-acp");
24008
24436
  await mkdir4(acpDirectory, { recursive: true, mode: 448 });
24009
24437
  if (process.platform !== "win32") {
24010
24438
  const { chmod: chmod2 } = await import("node:fs/promises");
@@ -24012,7 +24440,7 @@ async function prepareAntigravityProfile(input) {
24012
24440
  await chmod2(acpDirectory, 448);
24013
24441
  }
24014
24442
  writeFileAtomic(
24015
- join19(acpDirectory, "settings.json"),
24443
+ join20(acpDirectory, "settings.json"),
24016
24444
  `${JSON.stringify({ auth: { type: "oauth-personal" } })}
24017
24445
  `,
24018
24446
  { mode: 384 }
@@ -24037,13 +24465,13 @@ async function prepareAntigravityProfile(input) {
24037
24465
  });
24038
24466
  let tempDirectory;
24039
24467
  if ((input.platform ?? process.platform) === "win32") {
24040
- tempDirectory = resolve5(input.tempDirectory ?? antigravityTempDir(input.baseDir ?? DATA_DIR, input.instanceId));
24468
+ tempDirectory = resolve6(input.tempDirectory ?? antigravityTempDir(input.baseDir ?? DATA_DIR, input.instanceId));
24041
24469
  await mkdir4(tempDirectory, { recursive: true });
24042
24470
  setAntigravityTempEnvironment(environment, tempDirectory, "win32");
24043
24471
  }
24044
24472
  return {
24045
24473
  directory,
24046
- tokenPath: join19(acpDirectory, "acp_token.json"),
24474
+ tokenPath: join20(acpDirectory, "acp_token.json"),
24047
24475
  environment,
24048
24476
  ...tempDirectory ? { tempDirectory } : {}
24049
24477
  };
@@ -24175,14 +24603,14 @@ function isValidAntigravityInitializeResult(initialized, expectedVersion) {
24175
24603
  return true;
24176
24604
  }
24177
24605
  async function validateAntigravityRuntime(runtime, expectedVersion) {
24178
- const profileDirectory = await mkdtemp(join19(tmpdir2(), "openmaus-antigravity-verify-"));
24606
+ const profileDirectory = await mkdtemp(join20(tmpdir2(), "openmaus-antigravity-verify-"));
24179
24607
  let client;
24180
24608
  let tempDirectory;
24181
24609
  let failed2 = false;
24182
24610
  let failure;
24183
24611
  try {
24184
24612
  const profile = await prepareAntigravityProfile({
24185
- instanceId: `verify-${randomUUID7()}`,
24613
+ instanceId: `verify-${randomUUID8()}`,
24186
24614
  runtime,
24187
24615
  profileDirectory,
24188
24616
  tempDirectory: antigravityTempDir(DATA_DIR, VERIFICATION_TEMP_KEY)
@@ -24472,7 +24900,7 @@ var init_antigravity_acp = __esm({
24472
24900
  return { phase: "succeeded", flowId: null, authorizationUrl: null, expiresAt: null };
24473
24901
  }
24474
24902
  const flow = {
24475
- id: randomUUID7(),
24903
+ id: randomUUID8(),
24476
24904
  client,
24477
24905
  completed: authenticated,
24478
24906
  pending: { redirectUri: outcome.request.redirectUri, state: outcome.request.state },
@@ -24581,7 +25009,7 @@ var init_antigravity_release = __esm({
24581
25009
  });
24582
25010
 
24583
25011
  // server/drivers/antigravity-runtime.ts
24584
- import { createHash as createHash7, randomUUID as randomUUID8 } from "node:crypto";
25012
+ import { createHash as createHash7, randomUUID as randomUUID9 } from "node:crypto";
24585
25013
  import { createReadStream, createWriteStream } from "node:fs";
24586
25014
  import {
24587
25015
  access,
@@ -24596,8 +25024,8 @@ import {
24596
25024
  statfs,
24597
25025
  writeFile as writeFile2
24598
25026
  } from "node:fs/promises";
24599
- import { constants } from "node:fs";
24600
- import { delimiter as delimiter5, dirname as dirname8, isAbsolute as isAbsolute3, join as join20, resolve as resolve6 } from "node:path";
25027
+ import { constants as constants2 } from "node:fs";
25028
+ import { delimiter as delimiter5, dirname as dirname9, isAbsolute as isAbsolute4, join as join21, resolve as resolve7 } from "node:path";
24601
25029
  import { Transform } from "node:stream";
24602
25030
  import { pipeline } from "node:stream/promises";
24603
25031
  import { createInflateRaw } from "node:zlib";
@@ -24618,7 +25046,7 @@ async function retryOnWindowsFileLock(step, options = {}) {
24618
25046
  }
24619
25047
  }
24620
25048
  function runtimeRoot(baseDir = DATA_DIR) {
24621
- return join20(baseDir, "tools", "antigravity-acp", `${process.platform}-${process.arch}`);
25049
+ return join21(baseDir, "tools", "antigravity-acp", `${process.platform}-${process.arch}`);
24622
25050
  }
24623
25051
  function executableNames(platform = process.platform) {
24624
25052
  return platform === "win32" ? { executable: "agy_acp_server.exe", harness: "localharness_external.exe" } : { executable: "agy_acp_server.par", harness: "localharness_external" };
@@ -24627,7 +25055,7 @@ async function executableFile(path3, bytes) {
24627
25055
  try {
24628
25056
  const info = await stat4(path3);
24629
25057
  if (!info.isFile() || bytes !== void 0 && info.size !== bytes) return false;
24630
- if (process.platform !== "win32") await access(path3, constants.X_OK);
25058
+ if (process.platform !== "win32") await access(path3, constants2.X_OK);
24631
25059
  return true;
24632
25060
  } catch {
24633
25061
  return false;
@@ -24635,10 +25063,10 @@ async function executableFile(path3, bytes) {
24635
25063
  }
24636
25064
  async function completedRelease(directory, asset) {
24637
25065
  try {
24638
- const record3 = JSON.parse(await readFile3(join20(directory, INSTALL_RECORD), "utf8"));
25066
+ const record3 = JSON.parse(await readFile3(join21(directory, INSTALL_RECORD), "utf8"));
24639
25067
  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;
24640
- const executablePath = join20(directory, asset.executable.name);
24641
- const harnessPath = join20(directory, asset.harness.name);
25068
+ const executablePath = join21(directory, asset.executable.name);
25069
+ const harnessPath = join21(directory, asset.harness.name);
24642
25070
  if (!await executableFile(executablePath, asset.executable.bytes)) return null;
24643
25071
  if (!await executableFile(harnessPath, asset.harness.bytes)) return null;
24644
25072
  return { executablePath, harnessPath, source: "managed", version: asset.version };
@@ -24647,14 +25075,14 @@ async function completedRelease(directory, asset) {
24647
25075
  }
24648
25076
  }
24649
25077
  function pathCandidates(binary2, env) {
24650
- if (isAbsolute3(binary2) || binary2.includes("/") || binary2.includes("\\")) return [resolve6(binary2)];
25078
+ if (isAbsolute4(binary2) || binary2.includes("/") || binary2.includes("\\")) return [resolve7(binary2)];
24651
25079
  const pathValue = process.platform === "win32" ? Object.entries(env).findLast(([key]) => key.toUpperCase() === "PATH")?.[1] : env.PATH;
24652
- return (pathValue ?? augmentedPath()).split(delimiter5).map((part) => part.trim().replace(/^"|"$/gu, "")).filter(Boolean).map((part) => resolve6(part, binary2));
25080
+ return (pathValue ?? augmentedPath()).split(delimiter5).map((part) => part.trim().replace(/^"|"$/gu, "")).filter(Boolean).map((part) => resolve7(part, binary2));
24653
25081
  }
24654
25082
  async function externalRuntime(candidate, source) {
24655
25083
  const names = executableNames();
24656
- const executablePath = resolve6(candidate);
24657
- const harnessPath = join20(dirname8(executablePath), names.harness);
25084
+ const executablePath = resolve7(candidate);
25085
+ const harnessPath = join21(dirname9(executablePath), names.harness);
24658
25086
  if (!await executableFile(executablePath) || !await executableFile(harnessPath)) return null;
24659
25087
  return { executablePath, harnessPath, source, version: null };
24660
25088
  }
@@ -24674,7 +25102,7 @@ async function resolveAntigravityRuntime(binaryPath, env = process.env, baseDir
24674
25102
  }
24675
25103
  const asset = resolveAntigravityReleaseAsset();
24676
25104
  if (asset) {
24677
- const managed = await completedRelease(join20(runtimeRoot(baseDir), "versions", asset.sha256), asset);
25105
+ const managed = await completedRelease(join21(runtimeRoot(baseDir), "versions", asset.sha256), asset);
24678
25106
  if (managed) return managed;
24679
25107
  }
24680
25108
  const names = executableNames();
@@ -24796,7 +25224,7 @@ async function extractPinnedArchive(archivePath, outputDirectory, asset) {
24796
25224
  start: dataOffset,
24797
25225
  end: dataOffset + entry.compressedBytes - 1
24798
25226
  });
24799
- const destination = createWriteStream(join20(outputDirectory, entry.name), { flags: "wx", mode: 448 });
25227
+ const destination = createWriteStream(join21(outputDirectory, entry.name), { flags: "wx", mode: 448 });
24800
25228
  if (entry.compression === 8) await pipeline(source, createInflateRaw(), counter, destination);
24801
25229
  else await pipeline(source, counter, destination);
24802
25230
  if (bytes !== expectedBytes) throw new Error("An extracted file was truncated.");
@@ -24807,8 +25235,8 @@ async function installOnce2(options) {
24807
25235
  const asset = options.asset === void 0 ? resolveAntigravityReleaseAsset() : options.asset;
24808
25236
  if (!asset) throw new Error(`Google does not publish an Antigravity runtime for ${process.platform}-${process.arch}.`);
24809
25237
  const root = runtimeRoot(options.baseDir);
24810
- const versions = join20(root, "versions");
24811
- const destination = join20(versions, asset.sha256);
25238
+ const versions = join21(root, "versions");
25239
+ const destination = join21(versions, asset.sha256);
24812
25240
  await mkdir5(versions, { recursive: true, mode: 448 });
24813
25241
  const existing = await completedRelease(destination, asset);
24814
25242
  if (existing) {
@@ -24824,11 +25252,11 @@ async function installOnce2(options) {
24824
25252
  } catch (error51) {
24825
25253
  if (error51 instanceof Error && error51.message.includes("Antigravity needs")) throw error51;
24826
25254
  }
24827
- const staging = await mkdtemp2(join20(versions, `${STAGING_PREFIX}${randomUUID8()}-`));
25255
+ const staging = await mkdtemp2(join21(versions, `${STAGING_PREFIX}${randomUUID9()}-`));
24828
25256
  let failed2 = false;
24829
25257
  try {
24830
- const archivePath = join20(staging, "download.zip");
24831
- const runtimeDirectory = join20(staging, "runtime");
25258
+ const archivePath = join21(staging, "download.zip");
25259
+ const runtimeDirectory = join21(staging, "runtime");
24832
25260
  await mkdir5(runtimeDirectory, { mode: 448 });
24833
25261
  const controller = new AbortController();
24834
25262
  const timeout = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS2);
@@ -24874,12 +25302,12 @@ async function installOnce2(options) {
24874
25302
  }
24875
25303
  await extractPinnedArchive(archivePath, runtimeDirectory, asset);
24876
25304
  if (process.platform !== "win32") {
24877
- await chmod(join20(runtimeDirectory, asset.executable.name), 493);
24878
- await chmod(join20(runtimeDirectory, asset.harness.name), 493);
25305
+ await chmod(join21(runtimeDirectory, asset.executable.name), 493);
25306
+ await chmod(join21(runtimeDirectory, asset.harness.name), 493);
24879
25307
  }
24880
25308
  const candidate = {
24881
- executablePath: join20(runtimeDirectory, asset.executable.name),
24882
- harnessPath: join20(runtimeDirectory, asset.harness.name),
25309
+ executablePath: join21(runtimeDirectory, asset.executable.name),
25310
+ harnessPath: join21(runtimeDirectory, asset.harness.name),
24883
25311
  source: "managed",
24884
25312
  version: asset.version
24885
25313
  };
@@ -24890,7 +25318,7 @@ async function installOnce2(options) {
24890
25318
  executable: asset.executable,
24891
25319
  harness: asset.harness
24892
25320
  };
24893
- await writeFile2(join20(runtimeDirectory, INSTALL_RECORD), `${JSON.stringify(record3)}
25321
+ await writeFile2(join21(runtimeDirectory, INSTALL_RECORD), `${JSON.stringify(record3)}
24894
25322
  `, { flag: "wx", mode: 384 });
24895
25323
  try {
24896
25324
  await retryOnWindowsFileLock(() => rename2(runtimeDirectory, destination));
@@ -25115,304 +25543,6 @@ var init_antigravity = __esm({
25115
25543
  }
25116
25544
  });
25117
25545
 
25118
- // server/drivers/codex-device-auth.ts
25119
- import { randomUUID as randomUUID9 } from "node:crypto";
25120
- import { realpathSync as realpathSync2 } from "node:fs";
25121
- import { homedir as homedir8 } from "node:os";
25122
- import { basename as basename7, dirname as dirname9, isAbsolute as isAbsolute4, join as join21, resolve as resolve7 } from "node:path";
25123
- import { stripVTControlCharacters as stripVTControlCharacters4 } from "node:util";
25124
- function canonicalPath(path3) {
25125
- try {
25126
- return realpathSync2(path3);
25127
- } catch {
25128
- const parent = dirname9(path3);
25129
- return parent === path3 ? path3 : join21(canonicalPath(parent), basename7(path3));
25130
- }
25131
- }
25132
- function codexDevicePrompt(output) {
25133
- const clean = stripVTControlCharacters4(output);
25134
- const lines = clean.split(/\r?\n/).slice(0, -1).map((line) => line.trim());
25135
- if (!lines.some((line) => /^https:\/\/auth\.openai\.com\/codex\/device\/?$/.test(line))) return null;
25136
- const userCode = lines.find((line) => /^[A-Z0-9]{4,8}-[A-Z0-9]{4,8}$/.test(line));
25137
- return userCode ? { authorizationUrl: DEVICE_URL, userCode } : null;
25138
- }
25139
- function loginFailure(output) {
25140
- if (/(unexpected argument|unrecognized (argument|option)|unknown option).*device-auth/i.test(output)) {
25141
- return "This server's Codex CLI needs updating for browser sign-in. Run npm install -g @openai/codex@latest on the server, then try again.";
25142
- }
25143
- if (/device.{0,40}(disabled|not enabled|not allowed)|enable.{0,40}device/i.test(output)) {
25144
- return "Enable device-code login in your ChatGPT security settings or ask your workspace admin to allow it, then try again.";
25145
- }
25146
- if (/expired|expiration|timed out/i.test(output)) {
25147
- return "The ChatGPT sign-in code expired. Start sign-in again for a new code.";
25148
- }
25149
- return "ChatGPT sign-in did not finish. Check the server's connection and that device-code login is enabled in ChatGPT, then try again.";
25150
- }
25151
- var DEVICE_URL, MAX_OUTPUT2, authenticatingHomes, CodexDeviceAuthController;
25152
- var init_codex_device_auth = __esm({
25153
- "server/drivers/codex-device-auth.ts"() {
25154
- "use strict";
25155
- init_procs();
25156
- DEVICE_URL = "https://auth.openai.com/codex/device";
25157
- MAX_OUTPUT2 = 16384;
25158
- authenticatingHomes = /* @__PURE__ */ new Set();
25159
- CodexDeviceAuthController = class {
25160
- flow = null;
25161
- command = null;
25162
- disposed = false;
25163
- options;
25164
- constructor(options) {
25165
- this.options = options;
25166
- }
25167
- async start() {
25168
- if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing in.");
25169
- if (this.flow?.status.phase === "waiting") {
25170
- if (this.flow.ready) return { ...this.flow.status, phase: "waiting" };
25171
- throw new Error("A ChatGPT sign-in is already starting. Please wait for the code.");
25172
- }
25173
- await this.flow?.stopping;
25174
- if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing in.");
25175
- const env = this.options.environment();
25176
- const home = env.HOME || env.USERPROFILE || homedir8();
25177
- if (!isAbsolute4(home) || env.CODEX_HOME && !isAbsolute4(env.CODEX_HOME)) {
25178
- throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing in.");
25179
- }
25180
- const homeKey = canonicalPath(resolve7(env.CODEX_HOME || join21(home, ".codex")));
25181
- if (authenticatingHomes.has(homeKey)) {
25182
- throw new Error("A ChatGPT sign-in is already running for this server account. Finish or cancel it before starting another.");
25183
- }
25184
- authenticatingHomes.add(homeKey);
25185
- return new Promise((resolveStart, reject) => {
25186
- const flow = {
25187
- status: {
25188
- phase: "waiting",
25189
- flowId: randomUUID9(),
25190
- authorizationUrl: null,
25191
- expiresAt: new Date(Date.now() + (this.options.lifetimeMs ?? 15 * 6e4)).toISOString()
25192
- },
25193
- child: null,
25194
- ready: false,
25195
- resolve: resolveStart,
25196
- reject,
25197
- homeKey,
25198
- startupTimer: setTimeout(() => this.finish(flow, "failed", "Codex did not provide a sign-in code in time. Check the server connection and update Codex, then try again."), this.options.startupTimeoutMs ?? 3e4),
25199
- expiryTimer: setTimeout(() => this.finish(flow, "expired", "The ChatGPT sign-in code expired. Start sign-in again."), this.options.lifetimeMs ?? 15 * 6e4)
25200
- };
25201
- flow.startupTimer.unref();
25202
- flow.expiryTimer.unref();
25203
- this.flow = flow;
25204
- this.run(flow, ["login", "status"], env, home, (code, output) => {
25205
- if (code === 0 && /^logged in.*chatgpt\b/im.test(output)) {
25206
- this.finish(flow, "succeeded");
25207
- } else if (code === 0 && /^logged in\b/im.test(output)) {
25208
- this.finish(flow, "failed", "Codex already has a different sign-in method on this server. Ask the server administrator to review it before changing accounts.");
25209
- } else if (code !== 0 && /^not logged in\b/im.test(output)) {
25210
- this.run(flow, ["login", "--device-auth"], env, home, (loginCode, loginOutput) => {
25211
- if (loginCode !== 0 || !flow.ready) {
25212
- this.finish(flow, "failed", loginFailure(loginOutput));
25213
- return;
25214
- }
25215
- flow.startupTimer = setTimeout(() => this.finish(flow, "failed", "Codex finished sign-in but could not confirm the account. Refresh Settings and try again."), this.options.startupTimeoutMs ?? 3e4);
25216
- flow.startupTimer.unref();
25217
- this.run(flow, ["login", "status"], env, home, (statusCode, statusOutput) => {
25218
- this.finish(
25219
- flow,
25220
- statusCode === 0 && /^logged in.*chatgpt\b/im.test(statusOutput) ? "succeeded" : "failed",
25221
- "Codex finished sign-in but did not confirm a ChatGPT account. Refresh Settings and try again."
25222
- );
25223
- });
25224
- }, true);
25225
- } else this.finish(flow, "failed", "Codex could not confirm the existing sign-in on this server. Update Codex and check its login status before trying again.");
25226
- });
25227
- });
25228
- }
25229
- async get(flowId) {
25230
- if (!flowId || this.flow?.status.flowId !== flowId) throw new Error("This sign-in is no longer available. Start sign-in again.");
25231
- return { ...this.flow.status };
25232
- }
25233
- async cancel() {
25234
- if (this.flow?.status.phase === "waiting") this.finish(this.flow, "cancelled", "ChatGPT sign-in cancelled.");
25235
- await this.flow?.stopping;
25236
- if (this.flow?.terminationFailed) throw new Error(this.flow.status.message);
25237
- if (this.command && !await this.stopChild(this.command)) {
25238
- throw new Error("Codex could not be stopped on this server. Ask the server administrator to stop the account command before trying again.");
25239
- }
25240
- }
25241
- async dispose() {
25242
- this.disposed = true;
25243
- await this.cancel();
25244
- this.flow = null;
25245
- }
25246
- /** Remove the ChatGPT sign-in Codex stores for this server account so a
25247
- * different account can connect. A sign-in in progress is never pulled
25248
- * away underneath the browser completing it. */
25249
- async signOut() {
25250
- if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing out.");
25251
- if (this.flow?.status.phase === "waiting") throw new Error("Finish or cancel the ChatGPT sign-in in progress before signing out.");
25252
- await this.flow?.stopping;
25253
- if (this.disposed) throw new Error("This provider was removed. Refresh Settings before signing out.");
25254
- const env = this.options.environment();
25255
- const home = env.HOME || env.USERPROFILE || homedir8();
25256
- if (!isAbsolute4(home) || env.CODEX_HOME && !isAbsolute4(env.CODEX_HOME)) {
25257
- throw new Error("Use an absolute HOME and CODEX_HOME path for this server's Codex provider before signing out.");
25258
- }
25259
- const homeKey = canonicalPath(resolve7(env.CODEX_HOME || join21(home, ".codex")));
25260
- if (authenticatingHomes.has(homeKey)) {
25261
- throw new Error("A ChatGPT sign-in is running for this server account. Finish or cancel it before signing out.");
25262
- }
25263
- authenticatingHomes.add(homeKey);
25264
- try {
25265
- const before = await this.exec(["login", "status"], env, home);
25266
- if (before.code === 1 && /^not logged in\b/im.test(before.output)) return;
25267
- if (before.code !== 0 || !/^logged in.*chatgpt\b/im.test(before.output)) {
25268
- throw new Error("Codex did not confirm a ChatGPT sign-in. Other authentication methods will not be removed; ask the server administrator to check the account.");
25269
- }
25270
- const logout = await this.exec(["logout"], env, home);
25271
- if (logout.code !== 0) throw new Error("Codex could not remove the sign-in on this server. Check the server's Codex installation and try again.");
25272
- const status = await this.exec(["login", "status"], env, home);
25273
- if (status.code !== 1 || !/^not logged in\b/im.test(status.output)) {
25274
- throw new Error("Codex still reports a sign-in on this server. Update Codex and check its login status before trying again.");
25275
- }
25276
- } finally {
25277
- const child = this.command;
25278
- if (child && child.exitCode === null && child.signalCode === null) {
25279
- child.once("close", () => authenticatingHomes.delete(homeKey));
25280
- } else authenticatingHomes.delete(homeKey);
25281
- }
25282
- }
25283
- /** One bounded, non-interactive Codex command. Its output stays here;
25284
- * callers see an exit code and a status-line match, never the text. */
25285
- exec(args, env, cwd) {
25286
- return new Promise((resolveExec, rejectExec) => {
25287
- if (this.disposed) {
25288
- rejectExec(new Error("This provider was removed. Refresh Settings before signing out."));
25289
- return;
25290
- }
25291
- let child;
25292
- try {
25293
- child = spawnCli(this.options.cli, args, { env: { ...env, NO_COLOR: "1" }, cwd, stdio: ["pipe", "pipe", "pipe"] });
25294
- } catch {
25295
- rejectExec(new Error("Codex could not start on this server. Install or update the configured Codex CLI, then try again."));
25296
- return;
25297
- }
25298
- this.command = child;
25299
- child.stdin.end();
25300
- let output = "";
25301
- const receive = (chunk) => {
25302
- if (output.length < MAX_OUTPUT2) output += chunk.toString("utf8").slice(0, MAX_OUTPUT2 - output.length);
25303
- };
25304
- child.stdout.on("data", receive);
25305
- child.stderr.on("data", receive);
25306
- const timer = setTimeout(() => {
25307
- void this.stopChild(child).catch(() => false).then((stopped) => {
25308
- if (!stopped) rejectExec(new Error("Codex could not be stopped on this server. Ask the server administrator to stop the account command before trying again."));
25309
- });
25310
- }, this.options.startupTimeoutMs ?? 3e4);
25311
- timer.unref();
25312
- child.once("error", (error51) => {
25313
- clearTimeout(timer);
25314
- rejectExec(new Error(error51.code === "ENOENT" ? "Codex is not installed on this server. Run npm install -g @openai/codex@latest on the server, then try again." : "Codex could not start on this server. Check the configured CLI path and its executable permissions."));
25315
- });
25316
- child.once("close", (code) => {
25317
- clearTimeout(timer);
25318
- if (this.command === child) this.command = null;
25319
- resolveExec({ code, output: stripVTControlCharacters4(output) });
25320
- });
25321
- });
25322
- }
25323
- run(flow, args, env, cwd, done, parsePrompt = false) {
25324
- if (flow.status.phase !== "waiting") return;
25325
- let child;
25326
- try {
25327
- child = spawnCli(this.options.cli, args, { env: { ...env, NO_COLOR: "1" }, cwd, stdio: ["pipe", "pipe", "pipe"] });
25328
- } catch {
25329
- this.finish(flow, "failed", "Codex could not start on this server. Install or update the configured Codex CLI, then try again.");
25330
- return;
25331
- }
25332
- flow.child = child;
25333
- child.stdin.end();
25334
- let output = "";
25335
- const receive = (chunk) => {
25336
- if (flow.status.phase !== "waiting") return;
25337
- if (output.length + chunk.length > MAX_OUTPUT2) {
25338
- this.finish(flow, "failed", "Codex returned an unexpected sign-in response. Update the server's Codex CLI and try again.");
25339
- return;
25340
- }
25341
- output += chunk.toString("utf8");
25342
- const prompt = parsePrompt && !flow.ready ? codexDevicePrompt(output) : null;
25343
- if (prompt) {
25344
- flow.status = { ...flow.status, ...prompt };
25345
- flow.ready = true;
25346
- clearTimeout(flow.startupTimer);
25347
- flow.resolve({ ...flow.status, phase: "waiting" });
25348
- }
25349
- };
25350
- child.stdout.on("data", receive);
25351
- child.stderr.on("data", receive);
25352
- child.once("error", (error51) => {
25353
- const message3 = error51.code === "ENOENT" ? "Codex is not installed on this server. Run npm install -g @openai/codex@latest on the server, then try again." : "Codex could not start on this server. Check the configured CLI path and its executable permissions.";
25354
- this.finish(flow, "failed", message3);
25355
- });
25356
- child.once("close", (code) => {
25357
- if (flow.child === child) flow.child = null;
25358
- if (flow.status.phase === "waiting") done(code, stripVTControlCharacters4(output));
25359
- output = "";
25360
- });
25361
- }
25362
- finish(flow, phase, message3) {
25363
- if (flow.status.phase !== "waiting") return;
25364
- clearTimeout(flow.startupTimer);
25365
- clearTimeout(flow.expiryTimer);
25366
- flow.status = {
25367
- phase,
25368
- flowId: flow.status.flowId,
25369
- authorizationUrl: null,
25370
- expiresAt: null,
25371
- ...phase !== "succeeded" && message3 ? { message: message3 } : {}
25372
- };
25373
- if (!flow.ready) {
25374
- if (phase === "succeeded") flow.resolve({ ...flow.status, phase });
25375
- else flow.reject(new Error(message3 ?? "ChatGPT sign-in did not finish."));
25376
- }
25377
- flow.stopping = this.stopChild(flow.child).catch(() => false).then((stopped) => {
25378
- const release = () => authenticatingHomes.delete(flow.homeKey);
25379
- if (stopped) {
25380
- release();
25381
- return;
25382
- }
25383
- flow.terminationFailed = true;
25384
- flow.status = { ...flow.status, phase: "failed", message: "Codex could not be stopped on this server. Ask the server administrator to stop the login process before trying again." };
25385
- const child = flow.child;
25386
- if (!child || child.exitCode !== null || child.signalCode !== null) release();
25387
- else child.once("close", release);
25388
- });
25389
- if (phase === "succeeded") void this.options.onAuthenticated?.().catch(() => {
25390
- });
25391
- }
25392
- async stopChild(child) {
25393
- if (!child || await killCliTree(child, this.options.terminateTimeoutMs ?? 1500)) return true;
25394
- if (process.platform !== "win32" && child.pid) {
25395
- try {
25396
- process.kill(-child.pid, "SIGKILL");
25397
- } catch {
25398
- child.kill("SIGKILL");
25399
- }
25400
- } else child.kill("SIGKILL");
25401
- await new Promise((resolveStop) => {
25402
- if (child.exitCode !== null || child.signalCode !== null) return resolveStop();
25403
- const timer = setTimeout(resolveStop, 1500);
25404
- timer.unref();
25405
- child.once("close", () => {
25406
- clearTimeout(timer);
25407
- resolveStop();
25408
- });
25409
- });
25410
- return child.exitCode !== null || child.signalCode !== null;
25411
- }
25412
- };
25413
- }
25414
- });
25415
-
25416
25546
  // server/drivers/claude-login-auth.ts
25417
25547
  import { randomUUID as randomUUID10 } from "node:crypto";
25418
25548
  import { homedir as homedir9 } from "node:os";
@@ -25447,7 +25577,7 @@ var init_claude_login_auth = __esm({
25447
25577
  "server/drivers/claude-login-auth.ts"() {
25448
25578
  "use strict";
25449
25579
  init_procs();
25450
- init_codex_device_auth();
25580
+ init_device_auth();
25451
25581
  MAX_OUTPUT3 = 32768;
25452
25582
  SIGN_IN_HOSTS = ["claude.com", "claude.ai", "console.anthropic.com", "platform.claude.com"];
25453
25583
  authenticatingHomes2 = /* @__PURE__ */ new Set();
@@ -25819,7 +25949,7 @@ var init_tool_scope_support = __esm({
25819
25949
 
25820
25950
  // server/drivers/claude.ts
25821
25951
  import { createHash as createHash8, randomBytes as randomBytes2, randomUUID as randomUUID11 } from "node:crypto";
25822
- import { chmodSync as chmodSync4, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, readFileSync as readFileSync8, rmSync as rmSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
25952
+ import { chmodSync as chmodSync4, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, readFileSync as readFileSync9, rmSync as rmSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
25823
25953
  import { createServer as createNetServer } from "node:net";
25824
25954
  import { homedir as homedir10, tmpdir as tmpdir3 } from "node:os";
25825
25955
  import { join as join23, dirname as dirname10, isAbsolute as isAbsolute6, normalize } from "node:path";
@@ -25897,7 +26027,7 @@ function claudeInheritWarning(env) {
25897
26027
  function readClaudeAuthSettings(env, instanceEnvironment = {}) {
25898
26028
  if (CLAUDE_ACCOUNT_ENV_KEYS.some((key) => instanceEnvironment[key])) return {};
25899
26029
  try {
25900
- const settings2 = JSON.parse(readFileSync8(join23(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
26030
+ const settings2 = JSON.parse(readFileSync9(join23(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
25901
26031
  const authEnv = {};
25902
26032
  for (const key of CLAUDE_ACCOUNT_ENV_KEYS) {
25903
26033
  if (!key.endsWith("_FILE_DESCRIPTOR") && typeof settings2?.env?.[key] === "string") {
@@ -25915,7 +26045,7 @@ function readClaudeAuthSettings(env, instanceEnvironment = {}) {
25915
26045
  function projectMcpServers(cwd) {
25916
26046
  let parsed;
25917
26047
  try {
25918
- parsed = JSON.parse(readFileSync8(join23(cwd, ".mcp.json"), "utf8"));
26048
+ parsed = JSON.parse(readFileSync9(join23(cwd, ".mcp.json"), "utf8"));
25919
26049
  } catch {
25920
26050
  return {};
25921
26051
  }
@@ -25980,7 +26110,7 @@ function extrasFromUnknown(value) {
25980
26110
  function readClaudeModelCatalog(env = process.env) {
25981
26111
  let settings2 = {};
25982
26112
  try {
25983
- settings2 = JSON.parse(readFileSync8(join23(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
26113
+ settings2 = JSON.parse(readFileSync9(join23(resolveClaudeConfigDir(void 0, env), "settings.json"), "utf8"));
25984
26114
  } catch {
25985
26115
  settings2 = {};
25986
26116
  }
@@ -26070,6 +26200,7 @@ async function createPermissionBroker(opts) {
26070
26200
  const connectionHandler = (conn) => {
26071
26201
  conn.on("error", () => {
26072
26202
  });
26203
+ conn.setEncoding("utf8");
26073
26204
  let buf = "";
26074
26205
  conn.on("data", (chunk) => {
26075
26206
  buf += chunk;
@@ -26299,7 +26430,7 @@ function isCostSnapshot(value) {
26299
26430
  }
26300
26431
  function readCostHistory() {
26301
26432
  try {
26302
- const parsed = JSON.parse(readFileSync8(COST_HISTORY_FILE, "utf8"));
26433
+ const parsed = JSON.parse(readFileSync9(COST_HISTORY_FILE, "utf8"));
26303
26434
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
26304
26435
  return Object.fromEntries(Object.entries(parsed).map(([id, states]) => [id, Array.isArray(states) ? states.filter(isCostSnapshot) : []]));
26305
26436
  } catch {
@@ -26314,7 +26445,7 @@ function recordCostState(sessionId, state) {
26314
26445
  const ids = Object.keys(history);
26315
26446
  for (const id of ids.slice(0, Math.max(0, ids.length - COST_HISTORY_SESSIONS))) delete history[id];
26316
26447
  try {
26317
- writeFileAtomic(COST_HISTORY_FILE, JSON.stringify(history), { mode: 384 });
26448
+ writeFileAtomic(COST_HISTORY_FILE, JSON.stringify(history), { mode: 384, durable: false });
26318
26449
  } catch {
26319
26450
  }
26320
26451
  }
@@ -26336,7 +26467,7 @@ function claudeUserMessage(text4, images) {
26336
26467
  source: {
26337
26468
  type: "base64",
26338
26469
  media_type: image.mime,
26339
- data: readFileSync8(image.path).toString("base64")
26470
+ data: readFileSync9(image.path).toString("base64")
26340
26471
  }
26341
26472
  }));
26342
26473
  if (text4) content.push({ type: "text", text: text4 });
@@ -26497,7 +26628,23 @@ var init_claude = __esm({
26497
26628
  (err, stdout2) => resolve14(err ? null : stdout2.trim() || null)
26498
26629
  );
26499
26630
  });
26500
- const listeners = /* @__PURE__ */ new Set();
26631
+ let helpRead = null;
26632
+ const readCliHelp = (version2, env) => {
26633
+ if (helpRead?.version === version2) return helpRead.done;
26634
+ const read = {
26635
+ version: version2,
26636
+ done: new Promise((resolve14) => {
26637
+ execCli(config2.cli, ["--help"], { timeout: 8e3, env }, (err, stdout2) => resolve14(err ? null : stdout2));
26638
+ }).then((help) => {
26639
+ cliHasAutocompact = help === null ? null : /^\s*--autocompact\b/m.test(help);
26640
+ cliHelpVersion = version2;
26641
+ if (helpRead === read) helpRead = null;
26642
+ })
26643
+ };
26644
+ helpRead = read;
26645
+ return read.done;
26646
+ };
26647
+ const listeners2 = /* @__PURE__ */ new Set();
26501
26648
  const active = /* @__PURE__ */ new Map();
26502
26649
  const sessions = /* @__PURE__ */ new Map();
26503
26650
  const { idleMs: SESSION_IDLE_MS } = sessionIdlePolicy("CLAUDE");
@@ -26550,7 +26697,7 @@ var init_claude = __esm({
26550
26697
  });
26551
26698
  };
26552
26699
  const emit = (event) => {
26553
- for (const l of Array.from(listeners)) l(event);
26700
+ for (const l of Array.from(listeners2)) l(event);
26554
26701
  };
26555
26702
  const base2 = (threadId, turnId) => ({
26556
26703
  eventId: newEventId(),
@@ -26728,7 +26875,7 @@ var init_claude = __esm({
26728
26875
  const hookTokenPath = hooks ? hookTokenFile(threadId, botId) : null;
26729
26876
  if (hooks && hookTokenPath) {
26730
26877
  mkdirSync10(dirname10(hookTokenPath), { recursive: true, mode: 448 });
26731
- writeFileAtomic(hookTokenPath, hooks.token, { mode: 384 });
26878
+ writeFileAtomicIfChanged(hookTokenPath, hooks.token, { mode: 384, durable: false });
26732
26879
  env.OMB_HOOK_URL = hooks.url;
26733
26880
  env.OMB_HOOK_TOKEN_FILE = hookTokenPath;
26734
26881
  env.OMB_HOOK_NODE = process.execPath;
@@ -27360,13 +27507,7 @@ var init_claude = __esm({
27360
27507
  if (!version2) return { state: "unavailable", reason: `\`${config2.cli}\` CLI not found` };
27361
27508
  cliVersion = parseClaudeCliVersion(version2);
27362
27509
  cliVersionChecked = true;
27363
- if (version2 !== cliHelpVersion) {
27364
- const help = await new Promise((resolve14) => {
27365
- execCli(config2.cli, ["--help"], { timeout: 8e3, env }, (err, stdout2) => resolve14(err ? null : stdout2));
27366
- });
27367
- cliHasAutocompact = help === null ? null : /^\s*--autocompact\b/m.test(help);
27368
- cliHelpVersion = version2;
27369
- }
27510
+ if (version2 !== cliHelpVersion) await readCliHelp(version2, env);
27370
27511
  const update = claudeCliUpdate(version2, config2.cli);
27371
27512
  const warning = claudeInheritWarning(env);
27372
27513
  if (config2.requireApiKey) {
@@ -27519,8 +27660,8 @@ var init_claude = __esm({
27519
27660
  for (const threadId of Array.from(sessions.keys())) closeSession(threadId, "stopAll");
27520
27661
  },
27521
27662
  onEvent: (listener) => {
27522
- listeners.add(listener);
27523
- return () => listeners.delete(listener);
27663
+ listeners2.add(listener);
27664
+ return () => listeners2.delete(listener);
27524
27665
  }
27525
27666
  },
27526
27667
  generateText: (prompt, options) => generateReview(prompt, options?.signal, options?.onUsage),
@@ -27531,7 +27672,7 @@ var init_claude = __esm({
27531
27672
  } finally {
27532
27673
  for (const { stop } of active.values()) stop();
27533
27674
  for (const threadId of Array.from(sessions.keys())) closeSession(threadId, "dispose");
27534
- listeners.clear();
27675
+ listeners2.clear();
27535
27676
  }
27536
27677
  }
27537
27678
  };
@@ -27565,7 +27706,7 @@ var init_codex_tool_surface = __esm({
27565
27706
  });
27566
27707
 
27567
27708
  // server/drivers/codex-mcp-names.ts
27568
- import { readFileSync as readFileSync9 } from "node:fs";
27709
+ import { readFileSync as readFileSync10 } from "node:fs";
27569
27710
  import { homedir as homedir11 } from "node:os";
27570
27711
  import { join as join24 } from "node:path";
27571
27712
  function mcpServerNamesInToml(toml) {
@@ -27579,7 +27720,7 @@ function mcpServerNamesInToml(toml) {
27579
27720
  function codexConfigMcpServerNames(env) {
27580
27721
  const codexHome2 = env.CODEX_HOME || join24(env.HOME || env.USERPROFILE || homedir11(), ".codex");
27581
27722
  try {
27582
- return mcpServerNamesInToml(readFileSync9(join24(codexHome2, "config.toml"), "utf8"));
27723
+ return mcpServerNamesInToml(readFileSync10(join24(codexHome2, "config.toml"), "utf8"));
27583
27724
  } catch {
27584
27725
  return /* @__PURE__ */ new Set();
27585
27726
  }
@@ -27677,7 +27818,7 @@ import {
27677
27818
  linkSync as linkSync2,
27678
27819
  mkdirSync as mkdirSync11,
27679
27820
  openSync as openSync6,
27680
- readFileSync as readFileSync10,
27821
+ readFileSync as readFileSync11,
27681
27822
  unlinkSync as unlinkSync4,
27682
27823
  writeFileSync as writeFileSync6
27683
27824
  } from "node:fs";
@@ -27686,7 +27827,7 @@ function serverVersion() {
27686
27827
  const fromEnv = process.env.OMB_APP_VERSION?.trim();
27687
27828
  if (fromEnv) return fromEnv;
27688
27829
  try {
27689
- const pkg = JSON.parse(readFileSync10(join25(SERVER_ROOT, "..", "package.json"), "utf8"));
27830
+ const pkg = JSON.parse(readFileSync11(join25(SERVER_ROOT, "..", "package.json"), "utf8"));
27690
27831
  const version2 = Reflect.get(Object(pkg), "version");
27691
27832
  if (typeof version2 === "string" && version2) return version2;
27692
27833
  } catch {
@@ -28669,7 +28810,7 @@ var init_webapi = __esm({
28669
28810
 
28670
28811
  // server/drivers/chatgpt-plan-auth.ts
28671
28812
  import { createHash as createHash9, randomBytes as randomBytes3, randomUUID as randomUUID12, timingSafeEqual } from "node:crypto";
28672
- import { chmodSync as chmodSync5, closeSync as closeSync7, lstatSync as lstatSync2, mkdirSync as mkdirSync12, openSync as openSync7, readFileSync as readFileSync11, unlinkSync as unlinkSync5, writeFileSync as writeFileSync7 } from "node:fs";
28813
+ import { chmodSync as chmodSync5, closeSync as closeSync7, lstatSync as lstatSync3, mkdirSync as mkdirSync12, openSync as openSync7, readFileSync as readFileSync12, unlinkSync as unlinkSync5, writeFileSync as writeFileSync7 } from "node:fs";
28673
28814
  import { createServer as createServer2 } from "node:http";
28674
28815
  import { dirname as dirname12, isAbsolute as isAbsolute7, join as join26 } from "node:path";
28675
28816
  import { setTimeout as delay } from "node:timers/promises";
@@ -28682,15 +28823,15 @@ function string4(value, max = 65536) {
28682
28823
  }
28683
28824
  function privateDirectory(path3) {
28684
28825
  mkdirSync12(path3, { recursive: true, mode: 448 });
28685
- if (!lstatSync2(path3).isDirectory() || lstatSync2(path3).isSymbolicLink()) throw new Error("ChatGPT credential storage must be a private directory.");
28826
+ if (!lstatSync3(path3).isDirectory() || lstatSync3(path3).isSymbolicLink()) throw new Error("ChatGPT credential storage must be a private directory.");
28686
28827
  chmodSync5(path3, 448);
28687
28828
  }
28688
28829
  function readJson(path3) {
28689
28830
  try {
28690
- const stat5 = lstatSync2(path3);
28831
+ const stat5 = lstatSync3(path3);
28691
28832
  if (!stat5.isFile() || stat5.isSymbolicLink() || stat5.size > 512e3) throw new Error("Invalid credential storage.");
28692
28833
  chmodSync5(path3, 384);
28693
- return JSON.parse(readFileSync11(path3, "utf8"));
28834
+ return JSON.parse(readFileSync12(path3, "utf8"));
28694
28835
  } catch (error51) {
28695
28836
  if (error51.code === "ENOENT") return null;
28696
28837
  throw new Error("ChatGPT credential storage could not be read safely. Check its permissions before signing in.");
@@ -29234,7 +29375,7 @@ var init_codex_identity = __esm({
29234
29375
  });
29235
29376
 
29236
29377
  // server/drivers/codex-catalog.ts
29237
- import { readdirSync as readdirSync2, readFileSync as readFileSync12 } from "node:fs";
29378
+ import { readdirSync as readdirSync2, readFileSync as readFileSync13 } from "node:fs";
29238
29379
  import { homedir as homedir13 } from "node:os";
29239
29380
  import { basename as basename8, join as join28 } from "node:path";
29240
29381
  function encodeCodexSelection(provider, model) {
@@ -29411,7 +29552,7 @@ function parseCodexToml(text4) {
29411
29552
  }
29412
29553
  function readText(path3) {
29413
29554
  try {
29414
- return readFileSync12(path3, "utf8");
29555
+ return readFileSync13(path3, "utf8");
29415
29556
  } catch {
29416
29557
  return null;
29417
29558
  }
@@ -29581,7 +29722,7 @@ var init_codex_catalog = __esm({
29581
29722
 
29582
29723
  // server/drivers/codex-instructions.ts
29583
29724
  import { createHash as createHash10 } from "node:crypto";
29584
- import { mkdirSync as mkdirSync13, readFileSync as readFileSync13 } from "node:fs";
29725
+ import { mkdirSync as mkdirSync13, readFileSync as readFileSync14 } from "node:fs";
29585
29726
  import { join as join29 } from "node:path";
29586
29727
  function parseReceipt(raw) {
29587
29728
  if (!raw) return null;
@@ -29617,16 +29758,16 @@ ${configured}` : botInstructions;
29617
29758
  async function syncCodexInstructions(key, nativeThreadId, instructions, volatile, resumed, request, mentionTurn = false) {
29618
29759
  const directory = join29(DATA_DIR, "codex-instructions");
29619
29760
  const path3 = join29(directory, `${digest2(JSON.stringify([key, nativeThreadId]))}.sha256`);
29620
- const fingerprint = digest2(instructions);
29761
+ const fingerprint2 = digest2(instructions);
29621
29762
  const volatileFingerprint = digest2(volatile);
29622
29763
  let previous;
29623
29764
  try {
29624
- previous = parseReceipt(readFileSync13(path3, "utf8"));
29765
+ previous = parseReceipt(readFileSync14(path3, "utf8"));
29625
29766
  } catch (error51) {
29626
29767
  if (error51.code !== "ENOENT") throw error51;
29627
29768
  previous = null;
29628
29769
  }
29629
- if (resumed && previous?.instructions !== fingerprint) {
29770
+ if (resumed && previous?.instructions !== fingerprint2) {
29630
29771
  try {
29631
29772
  await request("thread/inject_items", {
29632
29773
  threadId: nativeThreadId,
@@ -29648,13 +29789,13 @@ async function syncCodexInstructions(key, nativeThreadId, instructions, volatile
29648
29789
  }
29649
29790
  const deliverVolatile = !resumed || previous?.volatile !== volatileFingerprint || mentionTurn;
29650
29791
  const hadVolatile = typeof previous?.volatile === "string" && previous.volatile !== digest2("");
29651
- if (!resumed || previous?.instructions !== fingerprint) {
29792
+ if (!resumed || previous?.instructions !== fingerprint2) {
29652
29793
  mkdirSync13(directory, { recursive: true });
29653
- writeFileAtomic(path3, JSON.stringify({ instructions: fingerprint }), { mode: 384 });
29794
+ writeFileAtomic(path3, JSON.stringify({ instructions: fingerprint2 }), { mode: 384 });
29654
29795
  }
29655
- const commitVolatile = previous?.instructions === fingerprint && previous?.volatile === volatileFingerprint ? null : () => {
29796
+ const commitVolatile = previous?.instructions === fingerprint2 && previous?.volatile === volatileFingerprint ? null : () => {
29656
29797
  mkdirSync13(directory, { recursive: true });
29657
- writeFileAtomic(path3, JSON.stringify({ instructions: fingerprint, volatile: volatileFingerprint }), { mode: 384 });
29798
+ writeFileAtomic(path3, JSON.stringify({ instructions: fingerprint2, volatile: volatileFingerprint }), { mode: 384 });
29658
29799
  };
29659
29800
  return { deliverVolatile, hadVolatile, commitVolatile };
29660
29801
  }
@@ -29669,6 +29810,48 @@ var init_codex_instructions = __esm({
29669
29810
  }
29670
29811
  });
29671
29812
 
29813
+ // server/drivers/codex-device-auth.ts
29814
+ function loginFailure(output) {
29815
+ if (/(unexpected argument|unrecognized (argument|option)|unknown option).*device-auth/i.test(output)) {
29816
+ return "This server's Codex CLI needs updating for browser sign-in. Run npm install -g @openai/codex@latest on the server, then try again.";
29817
+ }
29818
+ if (/device.{0,40}(disabled|not enabled|not allowed)|enable.{0,40}device/i.test(output)) {
29819
+ return "Enable device-code login in your ChatGPT security settings or ask your workspace admin to allow it, then try again.";
29820
+ }
29821
+ if (/expired|expiration|timed out/i.test(output)) {
29822
+ return "The ChatGPT sign-in code expired. Start sign-in again for a new code.";
29823
+ }
29824
+ return "ChatGPT sign-in did not finish. Check the server's connection and that device-code login is enabled in ChatGPT, then try again.";
29825
+ }
29826
+ var CODEX_DEVICE_SIGN_IN, CodexDeviceAuthController;
29827
+ var init_codex_device_auth = __esm({
29828
+ "server/drivers/codex-device-auth.ts"() {
29829
+ "use strict";
29830
+ init_device_sign_in();
29831
+ init_device_auth();
29832
+ CODEX_DEVICE_SIGN_IN = {
29833
+ provider: "codex",
29834
+ product: "Codex",
29835
+ account: "ChatGPT",
29836
+ homeEnv: "CODEX_HOME",
29837
+ homeDir: ".codex",
29838
+ loginArgs: ["login", "--device-auth"],
29839
+ install: "Run npm install -g @openai/codex@latest on the server, then try again.",
29840
+ failure: loginFailure,
29841
+ status: {
29842
+ args: ["login", "status"],
29843
+ read: (code, output) => code === 0 && /^logged in.*chatgpt\b/im.test(output) ? "signed-in" : code === 0 && /^logged in\b/im.test(output) ? "other" : code !== 0 && /^not logged in\b/im.test(output) ? "signed-out" : "unknown"
29844
+ },
29845
+ logoutArgs: ["logout"]
29846
+ };
29847
+ CodexDeviceAuthController = class extends DeviceAuthController {
29848
+ constructor(options) {
29849
+ super(CODEX_DEVICE_SIGN_IN, options);
29850
+ }
29851
+ };
29852
+ }
29853
+ });
29854
+
29672
29855
  // server/drivers/codex-release.ts
29673
29856
  function parseCodexReleaseVersion(value) {
29674
29857
  const match = /^(?:.*\bcodex-cli\s+)?v?(\d+)\.(\d+)\.(\d+)(-[0-9a-z.-]+)?(?:\+[0-9a-z.-]+)?$/i.exec(value.trim());
@@ -30213,10 +30396,10 @@ var init_codex = __esm({
30213
30396
  environment: childEnv,
30214
30397
  onAuthenticated: refreshModels
30215
30398
  });
30216
- const listeners = /* @__PURE__ */ new Set();
30399
+ const listeners2 = /* @__PURE__ */ new Set();
30217
30400
  const active = /* @__PURE__ */ new Map();
30218
30401
  const emit = (event) => {
30219
- for (const l of Array.from(listeners)) l(event);
30402
+ for (const l of Array.from(listeners2)) l(event);
30220
30403
  };
30221
30404
  const base2 = (threadId, turnId) => ({
30222
30405
  eventId: newEventId(),
@@ -31223,8 +31406,8 @@ ${stderr ?? ""}`))
31223
31406
  await Promise.all([...active.values()].map(({ stop }) => stop()));
31224
31407
  },
31225
31408
  onEvent: (listener) => {
31226
- listeners.add(listener);
31227
- return () => listeners.delete(listener);
31409
+ listeners2.add(listener);
31410
+ return () => listeners2.delete(listener);
31228
31411
  }
31229
31412
  },
31230
31413
  dispose: async () => {
@@ -31233,7 +31416,7 @@ ${stderr ?? ""}`))
31233
31416
  await authentication.dispose();
31234
31417
  await planAuth?.dispose();
31235
31418
  await Promise.all([...active.values()].map(({ stop }) => stop()));
31236
- listeners.clear();
31419
+ listeners2.clear();
31237
31420
  }
31238
31421
  };
31239
31422
  }
@@ -31618,11 +31801,11 @@ var require_codegen = __commonJS({
31618
31801
  const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
31619
31802
  return `${varKind} ${this.name}${rhs};` + _n;
31620
31803
  }
31621
- optimizeNames(names, constants3) {
31804
+ optimizeNames(names, constants4) {
31622
31805
  if (!names[this.name.str])
31623
31806
  return;
31624
31807
  if (this.rhs)
31625
- this.rhs = optimizeExpr(this.rhs, names, constants3);
31808
+ this.rhs = optimizeExpr(this.rhs, names, constants4);
31626
31809
  return this;
31627
31810
  }
31628
31811
  get names() {
@@ -31639,10 +31822,10 @@ var require_codegen = __commonJS({
31639
31822
  render({ _n }) {
31640
31823
  return `${this.lhs} = ${this.rhs};` + _n;
31641
31824
  }
31642
- optimizeNames(names, constants3) {
31825
+ optimizeNames(names, constants4) {
31643
31826
  if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
31644
31827
  return;
31645
- this.rhs = optimizeExpr(this.rhs, names, constants3);
31828
+ this.rhs = optimizeExpr(this.rhs, names, constants4);
31646
31829
  return this;
31647
31830
  }
31648
31831
  get names() {
@@ -31703,8 +31886,8 @@ var require_codegen = __commonJS({
31703
31886
  optimizeNodes() {
31704
31887
  return `${this.code}` ? this : void 0;
31705
31888
  }
31706
- optimizeNames(names, constants3) {
31707
- this.code = optimizeExpr(this.code, names, constants3);
31889
+ optimizeNames(names, constants4) {
31890
+ this.code = optimizeExpr(this.code, names, constants4);
31708
31891
  return this;
31709
31892
  }
31710
31893
  get names() {
@@ -31733,12 +31916,12 @@ var require_codegen = __commonJS({
31733
31916
  }
31734
31917
  return nodes.length > 0 ? this : void 0;
31735
31918
  }
31736
- optimizeNames(names, constants3) {
31919
+ optimizeNames(names, constants4) {
31737
31920
  const { nodes } = this;
31738
31921
  let i2 = nodes.length;
31739
31922
  while (i2--) {
31740
31923
  const n3 = nodes[i2];
31741
- if (n3.optimizeNames(names, constants3))
31924
+ if (n3.optimizeNames(names, constants4))
31742
31925
  continue;
31743
31926
  subtractNames(names, n3.names);
31744
31927
  nodes.splice(i2, 1);
@@ -31791,12 +31974,12 @@ var require_codegen = __commonJS({
31791
31974
  return void 0;
31792
31975
  return this;
31793
31976
  }
31794
- optimizeNames(names, constants3) {
31977
+ optimizeNames(names, constants4) {
31795
31978
  var _a3;
31796
- this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants3);
31797
- if (!(super.optimizeNames(names, constants3) || this.else))
31979
+ this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants4);
31980
+ if (!(super.optimizeNames(names, constants4) || this.else))
31798
31981
  return;
31799
- this.condition = optimizeExpr(this.condition, names, constants3);
31982
+ this.condition = optimizeExpr(this.condition, names, constants4);
31800
31983
  return this;
31801
31984
  }
31802
31985
  get names() {
@@ -31819,10 +32002,10 @@ var require_codegen = __commonJS({
31819
32002
  render(opts) {
31820
32003
  return `for(${this.iteration})` + super.render(opts);
31821
32004
  }
31822
- optimizeNames(names, constants3) {
31823
- if (!super.optimizeNames(names, constants3))
32005
+ optimizeNames(names, constants4) {
32006
+ if (!super.optimizeNames(names, constants4))
31824
32007
  return;
31825
- this.iteration = optimizeExpr(this.iteration, names, constants3);
32008
+ this.iteration = optimizeExpr(this.iteration, names, constants4);
31826
32009
  return this;
31827
32010
  }
31828
32011
  get names() {
@@ -31858,10 +32041,10 @@ var require_codegen = __commonJS({
31858
32041
  render(opts) {
31859
32042
  return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
31860
32043
  }
31861
- optimizeNames(names, constants3) {
31862
- if (!super.optimizeNames(names, constants3))
32044
+ optimizeNames(names, constants4) {
32045
+ if (!super.optimizeNames(names, constants4))
31863
32046
  return;
31864
- this.iterable = optimizeExpr(this.iterable, names, constants3);
32047
+ this.iterable = optimizeExpr(this.iterable, names, constants4);
31865
32048
  return this;
31866
32049
  }
31867
32050
  get names() {
@@ -31903,11 +32086,11 @@ var require_codegen = __commonJS({
31903
32086
  (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
31904
32087
  return this;
31905
32088
  }
31906
- optimizeNames(names, constants3) {
32089
+ optimizeNames(names, constants4) {
31907
32090
  var _a3, _b;
31908
- super.optimizeNames(names, constants3);
31909
- (_a3 = this.catch) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants3);
31910
- (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants3);
32091
+ super.optimizeNames(names, constants4);
32092
+ (_a3 = this.catch) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants4);
32093
+ (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants4);
31911
32094
  return this;
31912
32095
  }
31913
32096
  get names() {
@@ -32208,7 +32391,7 @@ var require_codegen = __commonJS({
32208
32391
  function addExprNames(names, from) {
32209
32392
  return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
32210
32393
  }
32211
- function optimizeExpr(expr, names, constants3) {
32394
+ function optimizeExpr(expr, names, constants4) {
32212
32395
  if (expr instanceof code_1.Name)
32213
32396
  return replaceName(expr);
32214
32397
  if (!canOptimize(expr))
@@ -32223,14 +32406,14 @@ var require_codegen = __commonJS({
32223
32406
  return items;
32224
32407
  }, []));
32225
32408
  function replaceName(n3) {
32226
- const c2 = constants3[n3.str];
32409
+ const c2 = constants4[n3.str];
32227
32410
  if (c2 === void 0 || names[n3.str] !== 1)
32228
32411
  return n3;
32229
32412
  delete names[n3.str];
32230
32413
  return c2;
32231
32414
  }
32232
32415
  function canOptimize(e) {
32233
- return e instanceof code_1._Code && e._items.some((c2) => c2 instanceof code_1.Name && names[c2.str] === 1 && constants3[c2.str] !== void 0);
32416
+ return e instanceof code_1._Code && e._items.some((c2) => c2 instanceof code_1.Name && names[c2.str] === 1 && constants4[c2.str] !== void 0);
32234
32417
  }
32235
32418
  }
32236
32419
  function subtractNames(names, from) {
@@ -32306,11 +32489,11 @@ var require_util = __commonJS({
32306
32489
  return false;
32307
32490
  }
32308
32491
  exports.schemaHasRules = schemaHasRules;
32309
- function schemaHasRulesButRef(schema4, RULES) {
32492
+ function schemaHasRulesButRef(schema4, RULES2) {
32310
32493
  if (typeof schema4 == "boolean")
32311
32494
  return !schema4;
32312
32495
  for (const key in schema4)
32313
- if (key !== "$ref" && RULES.all[key])
32496
+ if (key !== "$ref" && RULES2.all[key])
32314
32497
  return true;
32315
32498
  return false;
32316
32499
  }
@@ -33704,17 +33887,17 @@ var require_validate = __commonJS({
33704
33887
  }
33705
33888
  function schemaKeywords(it, types, typeErrors, errsCount) {
33706
33889
  const { gen, schema: schema4, data, allErrors, opts, self } = it;
33707
- const { RULES } = self;
33708
- if (schema4.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema4, RULES))) {
33709
- gen.block(() => keywordCode(it, "$ref", RULES.all.$ref.definition));
33890
+ const { RULES: RULES2 } = self;
33891
+ if (schema4.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema4, RULES2))) {
33892
+ gen.block(() => keywordCode(it, "$ref", RULES2.all.$ref.definition));
33710
33893
  return;
33711
33894
  }
33712
33895
  if (!opts.jtd)
33713
33896
  checkStrictTypes(it, types);
33714
33897
  gen.block(() => {
33715
- for (const group of RULES.rules)
33898
+ for (const group of RULES2.rules)
33716
33899
  groupKeywords(group);
33717
- groupKeywords(RULES.post);
33900
+ groupKeywords(RULES2.post);
33718
33901
  });
33719
33902
  function groupKeywords(group) {
33720
33903
  if (!(0, applicability_1.shouldUseGroup)(schema4, group))
@@ -35529,10 +35712,10 @@ var require_core = __commonJS({
35529
35712
  }
35530
35713
  // Remove keyword
35531
35714
  removeKeyword(keyword) {
35532
- const { RULES } = this;
35533
- delete RULES.keywords[keyword];
35534
- delete RULES.all[keyword];
35535
- for (const group of RULES.rules) {
35715
+ const { RULES: RULES2 } = this;
35716
+ delete RULES2.keywords[keyword];
35717
+ delete RULES2.all[keyword];
35718
+ for (const group of RULES2.rules) {
35536
35719
  const i2 = group.rules.findIndex((rule) => rule.keyword === keyword);
35537
35720
  if (i2 >= 0)
35538
35721
  group.rules.splice(i2, 1);
@@ -35700,9 +35883,9 @@ var require_core = __commonJS({
35700
35883
  }
35701
35884
  var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
35702
35885
  function checkKeyword(keyword, def) {
35703
- const { RULES } = this;
35886
+ const { RULES: RULES2 } = this;
35704
35887
  (0, util_1.eachItem)(keyword, (kwd) => {
35705
- if (RULES.keywords[kwd])
35888
+ if (RULES2.keywords[kwd])
35706
35889
  throw new Error(`Keyword ${kwd} is already defined`);
35707
35890
  if (!KEYWORD_NAME.test(kwd))
35708
35891
  throw new Error(`Keyword ${kwd} has invalid name`);
@@ -35718,13 +35901,13 @@ var require_core = __commonJS({
35718
35901
  const post = definition === null || definition === void 0 ? void 0 : definition.post;
35719
35902
  if (dataType && post)
35720
35903
  throw new Error('keyword with "post" flag cannot have "type"');
35721
- const { RULES } = this;
35722
- let ruleGroup = post ? RULES.post : RULES.rules.find(({ type: t2 }) => t2 === dataType);
35904
+ const { RULES: RULES2 } = this;
35905
+ let ruleGroup = post ? RULES2.post : RULES2.rules.find(({ type: t2 }) => t2 === dataType);
35723
35906
  if (!ruleGroup) {
35724
35907
  ruleGroup = { type: dataType, rules: [] };
35725
- RULES.rules.push(ruleGroup);
35908
+ RULES2.rules.push(ruleGroup);
35726
35909
  }
35727
- RULES.keywords[keyword] = true;
35910
+ RULES2.keywords[keyword] = true;
35728
35911
  if (!definition)
35729
35912
  return;
35730
35913
  const rule = {
@@ -35739,7 +35922,7 @@ var require_core = __commonJS({
35739
35922
  addBeforeRule.call(this, ruleGroup, rule, definition.before);
35740
35923
  else
35741
35924
  ruleGroup.rules.push(rule);
35742
- RULES.all[keyword] = rule;
35925
+ RULES2.all[keyword] = rule;
35743
35926
  (_a3 = definition.implements) === null || _a3 === void 0 ? void 0 : _a3.forEach((kwd) => this.addKeyword(kwd));
35744
35927
  }
35745
35928
  function addBeforeRule(ruleGroup, rule, before) {
@@ -38939,7 +39122,7 @@ var require_dist = __commonJS({
38939
39122
  });
38940
39123
 
38941
39124
  // server/drivers/chat-images.ts
38942
- import { readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
39125
+ import { readFileSync as readFileSync15, statSync as statSync5 } from "node:fs";
38943
39126
  function chatImageBudget() {
38944
39127
  let used = 0;
38945
39128
  return (parts) => {
@@ -38971,7 +39154,7 @@ function chatUserContent(turn) {
38971
39154
  const budget = chatImageBudget();
38972
39155
  return [{ type: "text", text: turn.text }, ...turn.images.map((image) => {
38973
39156
  if (statSync5(image.path).size > IMAGE_BYTES) throw new Error("Image exceeds 20 MB");
38974
- const part = chatImage({ mimeType: image.mime, data: readFileSync14(image.path).toString("base64") });
39157
+ const part = chatImage({ mimeType: image.mime, data: readFileSync15(image.path).toString("base64") });
38975
39158
  budget([part]);
38976
39159
  return part;
38977
39160
  })];
@@ -39031,7 +39214,7 @@ function omitBlankDefaults(builtInBrowser, tool, args) {
39031
39214
  if (typeof value === "string" && !value.trim()) delete args[field];
39032
39215
  }
39033
39216
  }
39034
- async function mountChatTools(integrations, signal, computerUse = false, toolScope) {
39217
+ async function mountChatTools(integrations, signal, computerUse = false, toolScope, callTimeoutMs = DEFAULT_CALL_MS) {
39035
39218
  const parsed = parseToolScope(toolScope);
39036
39219
  if (!parsed.ok) throw new Error(parsed.error);
39037
39220
  const scope = parsed.scope;
@@ -39135,7 +39318,7 @@ async function mountChatTools(integrations, signal, computerUse = false, toolSco
39135
39318
  }
39136
39319
  const tool = registered.get(name);
39137
39320
  try {
39138
- const result = await tool.client.call("tools/call", { name: tool.name, arguments: args }, AbortSignal.any([signal, callSignal]), CALL_MS);
39321
+ const result = await tool.client.call("tools/call", { name: tool.name, arguments: args }, AbortSignal.any([signal, callSignal]), callTimeoutMs);
39139
39322
  if (signal.aborted || callSignal.aborted) throw aborted2();
39140
39323
  if (!object3(result) || !Array.isArray(result.content) || result.isError !== void 0 && typeof result.isError !== "boolean") throw new Error("MCP tool returned an invalid result; execution outcome may be uncertain");
39141
39324
  const parts = [];
@@ -39161,7 +39344,7 @@ async function mountChatTools(integrations, signal, computerUse = false, toolSco
39161
39344
  }
39162
39345
  };
39163
39346
  }
39164
- var import_ajv, import__, import_ajv_formats, ChatToolSessionError, STARTUP_MS, CALL_MS, FRAME_BYTES, OUTPUT_BYTES, TOOL_COUNT, MAX_PAGES, SCHEMA_BYTES, CATALOG_BYTES, MAX_FRAMES, ChatMcpClient, validatorOptions, BUILT_IN_BROWSER_BLANK_MEANS_OMITTED;
39347
+ var import_ajv, import__, import_ajv_formats, ChatToolSessionError, STARTUP_MS, DEFAULT_CALL_MS, FRAME_BYTES, OUTPUT_BYTES, TOOL_COUNT, MAX_PAGES, SCHEMA_BYTES, CATALOG_BYTES, MAX_FRAMES, ChatMcpClient, validatorOptions, BUILT_IN_BROWSER_BLANK_MEANS_OMITTED;
39165
39348
  var init_chat_mcp_tools = __esm({
39166
39349
  "server/drivers/chat-mcp-tools.ts"() {
39167
39350
  "use strict";
@@ -39177,7 +39360,7 @@ var init_chat_mcp_tools = __esm({
39177
39360
  ChatToolSessionError = class extends Error {
39178
39361
  };
39179
39362
  STARTUP_MS = 8e3;
39180
- CALL_MS = 10 * 6e4;
39363
+ DEFAULT_CALL_MS = 10 * 6e4;
39181
39364
  FRAME_BYTES = 2 * 1024 * 1024;
39182
39365
  OUTPUT_BYTES = 50 * 1024;
39183
39366
  TOOL_COUNT = 128;
@@ -39603,7 +39786,7 @@ function writeTurnHandoff(input) {
39603
39786
  function classifyContinuable(_stopReason, failure) {
39604
39787
  if (!failure) return null;
39605
39788
  if (NOT_RESUMABLE.test(failure)) return null;
39606
- if (/limit reached|stopped after \d+ steps|without a final answer/i.test(failure)) return "cap";
39789
+ if (/stopped after \d+ steps without a final answer|model-call limit reached/i.test(failure)) return "cap";
39607
39790
  return null;
39608
39791
  }
39609
39792
  var NOT_RESUMABLE;
@@ -39614,6 +39797,41 @@ var init_turn_continuation = __esm({
39614
39797
  }
39615
39798
  });
39616
39799
 
39800
+ // server/key-rejections.ts
39801
+ import { createHash as createHash12 } from "node:crypto";
39802
+ function keyRejected(url2, key) {
39803
+ return rejected.size > 0 && rejected.has(markOf(url2, key));
39804
+ }
39805
+ function noteKeyRejected(url2, key) {
39806
+ const mark = markOf(url2, key);
39807
+ if (rejected.has(mark)) return;
39808
+ rejected.add(mark);
39809
+ changed();
39810
+ }
39811
+ function noteKeyAccepted(url2, key) {
39812
+ if (rejected.size > 0 && rejected.delete(markOf(url2, key))) changed();
39813
+ }
39814
+ function rejectsKey(status, body) {
39815
+ if (/\b(?:quota|billing|credits?|rate.?limit|insufficient.?(?:balance|funds))\b/i.test(body)) return false;
39816
+ if (status === 401) return true;
39817
+ if (status !== 400 && status !== 403) return false;
39818
+ return /\binvalid_api_key\b|\b(?:invalid|incorrect|revoked|expired|disabled|deactivated)[ _-]?(?:api[ _-]?)?key\b|\bapi[ _-]?key (?:is |was |has been )?(?:invalid|incorrect|revoked|expired|disabled|not valid)\b/i.test(body);
39819
+ }
39820
+ var KEY_REJECTED_REASON, rejected, listeners, fingerprint, markOf, changed;
39821
+ var init_key_rejections = __esm({
39822
+ "server/key-rejections.ts"() {
39823
+ "use strict";
39824
+ KEY_REJECTED_REASON = "The provider rejected this key. Change it in Settings \u2192 API keys.";
39825
+ rejected = /* @__PURE__ */ new Set();
39826
+ listeners = /* @__PURE__ */ new Set();
39827
+ fingerprint = (key) => createHash12("sha256").update(key.trim()).digest("hex").slice(0, 32);
39828
+ markOf = (url2, key) => `${url2.trim().replace(/\/+$/, "")} ${fingerprint(key)}`;
39829
+ changed = () => {
39830
+ for (const listener of listeners) listener();
39831
+ };
39832
+ }
39833
+ });
39834
+
39617
39835
  // server/drivers/openai-chat.ts
39618
39836
  function providerError(json2) {
39619
39837
  if (typeof json2.error === "string") return json2.error;
@@ -39679,11 +39897,11 @@ function refusedToolCallFailure(error51, earlierSteps) {
39679
39897
  }
39680
39898
  function createOpenAIChatRuntime(options) {
39681
39899
  const { input } = options;
39682
- const listeners = /* @__PURE__ */ new Set();
39900
+ const listeners2 = /* @__PURE__ */ new Set();
39683
39901
  const active = /* @__PURE__ */ new Map();
39684
39902
  const reasoningReplayRejected = /* @__PURE__ */ new Set();
39685
39903
  const emit = (event) => {
39686
- for (const listener of Array.from(listeners)) listener(event);
39904
+ for (const listener of Array.from(listeners2)) listener(event);
39687
39905
  };
39688
39906
  const base2 = (threadId, turnId) => ({
39689
39907
  eventId: newEventId(),
@@ -39716,6 +39934,7 @@ function createOpenAIChatRuntime(options) {
39716
39934
  });
39717
39935
  if (!response.ok) {
39718
39936
  const body = await response.text().catch(() => "");
39937
+ if (rejectsKey(response.status, body)) noteKeyRejected(options.apiUrl, options.apiKey);
39719
39938
  const message3 = `${options.httpErrorLabel} HTTP ${response.status}${body ? `: ${body.slice(0, 200)}` : ""}`;
39720
39939
  if (rejectsToolsParameter(response.status, body)) throw new UnsupportedChatToolsError(message3);
39721
39940
  const refusal = refusedToolCall(response.status, body);
@@ -39725,6 +39944,7 @@ function createOpenAIChatRuntime(options) {
39725
39944
  }
39726
39945
  throw new Error(message3);
39727
39946
  }
39947
+ noteKeyAccepted(options.apiUrl, options.apiKey);
39728
39948
  if (!stream || response.headers.get("content-type")?.includes("application/json")) {
39729
39949
  const json2 = await response.json();
39730
39950
  if (onUsage) {
@@ -39963,7 +40183,7 @@ ${TEXT_ONLY_ATTACHMENT_NOTE}` : userTurn.text }
39963
40183
  const parsed = parseToolScope(turn.toolScope);
39964
40184
  if (!parsed.ok) throw new Error(parsed.error);
39965
40185
  const scope = parsed.scope;
39966
- tools = await mountChatTools(options.tools === false ? void 0 : turn.integrations, abort.signal, options.computerUse, scope);
40186
+ tools = await mountChatTools(options.tools === false ? void 0 : turn.integrations, abort.signal, options.computerUse, scope, turn.mcpCallTimeoutMs);
39967
40187
  const questionAllowed = options.tools !== false && allowsTool(scope, { kind: "native", name: ASK_USER_TOOL });
39968
40188
  let optionalQuestionOnly = questionAllowed && tools.definitions.length === 0;
39969
40189
  if (questionAllowed && !tools.definitions.some((definition) => definition.function.name === ASK_USER_TOOL)) {
@@ -40225,7 +40445,11 @@ ${TEXT_ONLY_SCREENSHOT_NOTE}` };
40225
40445
  return options.models();
40226
40446
  },
40227
40447
  ...options.refreshModels ? { refreshModels: options.refreshModels } : {},
40228
- snapshot: async () => options.apiKey ? { state: "available", authenticated: true, version: null, ...options.billing ? { billing: options.billing } : {} } : { state: "unavailable", reason: options.unavailableReason },
40448
+ snapshot: async () => {
40449
+ if (!options.apiKey) return { state: "unavailable", reason: options.unavailableReason };
40450
+ if (keyRejected(options.apiUrl, options.apiKey)) return { state: "available", authenticated: false, reason: KEY_REJECTED_REASON, version: null };
40451
+ return { state: "available", authenticated: true, version: null, ...options.billing ? { billing: options.billing } : {} };
40452
+ },
40229
40453
  adapter: {
40230
40454
  provider: options.driverKind,
40231
40455
  capabilities: {
@@ -40269,8 +40493,8 @@ ${TEXT_ONLY_SCREENSHOT_NOTE}` };
40269
40493
  await Promise.all(turns.map((turn) => turn.done));
40270
40494
  },
40271
40495
  onEvent: (listener) => {
40272
- listeners.add(listener);
40273
- return () => listeners.delete(listener);
40496
+ listeners2.add(listener);
40497
+ return () => listeners2.delete(listener);
40274
40498
  }
40275
40499
  },
40276
40500
  generateText: async (prompt, { signal, onUsage } = {}) => {
@@ -40283,7 +40507,7 @@ ${TEXT_ONLY_SCREENSHOT_NOTE}` };
40283
40507
  const turns = [...active.values()];
40284
40508
  for (const turn of turns) turn.abort.abort();
40285
40509
  await Promise.all(turns.map((turn) => turn.done));
40286
- listeners.clear();
40510
+ listeners2.clear();
40287
40511
  }
40288
40512
  };
40289
40513
  }
@@ -40304,6 +40528,7 @@ var init_openai_chat = __esm({
40304
40528
  init_native();
40305
40529
  init_retry();
40306
40530
  init_turn_continuation();
40531
+ init_key_rejections();
40307
40532
  usageFrom = (usage) => usage ? { input: usage.prompt_tokens ?? 0, output: usage.completion_tokens ?? 0 } : null;
40308
40533
  asError = (value) => value instanceof Error ? value : new Error(String(value));
40309
40534
  UnsupportedChatToolsError = class extends Error {
@@ -47234,9 +47459,26 @@ var init_browser = __esm({
47234
47459
  });
47235
47460
 
47236
47461
  // server/drivers/acp/grok.ts
47237
- import { existsSync as existsSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "node:fs";
47462
+ import { existsSync as existsSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
47238
47463
  import { dirname as dirname13, join as join32, resolve as resolve10 } from "node:path";
47239
47464
  import { setTimeout as delay2 } from "node:timers/promises";
47465
+ function grokHome(env = process.env) {
47466
+ return env.GROK_HOME || harnessHome("grok", env);
47467
+ }
47468
+ function grokSignedIn(env) {
47469
+ return existsSync10(join32(grokHome(env), "auth.json"));
47470
+ }
47471
+ function grokLoginFailure(output) {
47472
+ if (/(unexpected argument|unrecognized (argument|option)|unknown option).*device-(auth|code)/i.test(output)) {
47473
+ return "This server's Grok is too old to sign in with a code. Update Grok on the server, then try again.";
47474
+ }
47475
+ if (/device-code login is not available/i.test(output)) {
47476
+ return "This Grok account can't sign in with a code. Use an xAI API key instead.";
47477
+ }
47478
+ if (/expired/i.test(output)) return "The Grok sign-in code expired. Start sign-in again for a new code.";
47479
+ if (/denied|rejected/i.test(output)) return "Grok sign-in was declined in the browser. Start sign-in again to try once more.";
47480
+ return "Grok sign-in did not finish. Check the server's connection, then try again.";
47481
+ }
47240
47482
  function unquote2(raw) {
47241
47483
  const value = raw.trim();
47242
47484
  if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
@@ -47245,10 +47487,10 @@ function unquote2(raw) {
47245
47487
  return value;
47246
47488
  }
47247
47489
  function readGrokModelCatalog(env = process.env) {
47248
- const path3 = join32(env.GROK_HOME || harnessHome("grok", env), "config.toml");
47490
+ const path3 = join32(grokHome(env), "config.toml");
47249
47491
  let text4 = "";
47250
47492
  try {
47251
- text4 = readFileSync15(path3, "utf8");
47493
+ text4 = readFileSync16(path3, "utf8");
47252
47494
  } catch {
47253
47495
  return STATIC_GROK_MODELS;
47254
47496
  }
@@ -47318,10 +47560,10 @@ function ensureGrokInjectSlug(modelId, env = process.env) {
47318
47560
  if (!inject) return modelId;
47319
47561
  const host = localHost(inject.host);
47320
47562
  if (!host) return modelId;
47321
- const path3 = join32(env.GROK_HOME || harnessHome("grok", env), "config.toml");
47563
+ const path3 = join32(grokHome(env), "config.toml");
47322
47564
  let text4 = "";
47323
47565
  try {
47324
- text4 = readFileSync15(path3, "utf8");
47566
+ text4 = readFileSync16(path3, "utf8");
47325
47567
  } catch {
47326
47568
  text4 = "";
47327
47569
  }
@@ -47393,7 +47635,7 @@ function grokInheritedProfile(cli, env, cwd) {
47393
47635
  const flag = flags[0] ?? -1;
47394
47636
  let path3 = flag >= 0 ? args[flag].includes("=") ? args[flag].slice(args[flag].indexOf("=") + 1) : args[flag + 1] : void 0;
47395
47637
  if (flag >= 0 && (!path3 || path3.startsWith("-"))) return unsupported();
47396
- const files = [join32(env.GROK_HOME || harnessHome("grok", env), "config.toml")];
47638
+ const files = [join32(grokHome(env), "config.toml")];
47397
47639
  for (let directory = resolve10(cwd); ; ) {
47398
47640
  const file2 = join32(directory, ".grok", "config.toml");
47399
47641
  if (!files.includes(file2)) files.push(file2);
@@ -47403,7 +47645,7 @@ function grokInheritedProfile(cli, env, cwd) {
47403
47645
  }
47404
47646
  for (const file2 of files) {
47405
47647
  if (!existsSync10(file2)) continue;
47406
- const text5 = readFileSync15(file2, "utf8");
47648
+ const text5 = readFileSync16(file2, "utf8");
47407
47649
  if (text5.length > 262144) return unsupported();
47408
47650
  let section = "";
47409
47651
  for (const line of text5.split(/\r?\n/).map((line2) => line2.trim()).filter((line2) => line2 && !line2.startsWith("#"))) {
@@ -47433,7 +47675,7 @@ function grokInheritedProfile(cli, env, cwd) {
47433
47675
  }
47434
47676
  }
47435
47677
  if (!path3) return;
47436
- const text4 = readFileSync15(resolve10(cwd, path3), "utf8");
47678
+ const text4 = readFileSync16(resolve10(cwd, path3), "utf8");
47437
47679
  if (text4.length > 262144) return unsupported();
47438
47680
  const match = /^\s*---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?$/.exec(text4);
47439
47681
  if (!match) return unsupported();
@@ -47469,7 +47711,7 @@ function grokToolScopeProfile(scope, init, inherited, hasMcp = false) {
47469
47711
  disallowedTools: [.../* @__PURE__ */ new Set([...inherited?.disallowedTools ?? [], ...selected.length ? [] : ["read_file"]])]
47470
47712
  };
47471
47713
  }
47472
- var STATIC_GROK_MODELS, SLUG, GROK_NATIVE_TOOLS, support2, GrokAgentDriver;
47714
+ var STATIC_GROK_MODELS, SLUG, GROK_DEVICE_SIGN_IN, GROK_NATIVE_TOOLS, support2, GrokAgentDriver;
47473
47715
  var init_grok2 = __esm({
47474
47716
  "server/drivers/acp/grok.ts"() {
47475
47717
  "use strict";
@@ -47487,6 +47729,17 @@ var init_grok2 = __esm({
47487
47729
  ]
47488
47730
  };
47489
47731
  SLUG = /^[a-z0-9][a-z0-9._-]*$/i;
47732
+ GROK_DEVICE_SIGN_IN = {
47733
+ provider: "grok",
47734
+ product: "Grok",
47735
+ account: "Grok",
47736
+ homeEnv: "GROK_HOME",
47737
+ homeDir: ".grok",
47738
+ loginArgs: ["login", "--device-auth"],
47739
+ install: "Install it with xAI's installer (https://x.ai/cli), then try again.",
47740
+ failure: grokLoginFailure,
47741
+ signedIn: grokSignedIn
47742
+ };
47490
47743
  GROK_NATIVE_TOOLS = [
47491
47744
  ["read_file", "GrokBuild:read_file"],
47492
47745
  ["search_replace", "GrokBuild:search_replace"],
@@ -47527,7 +47780,7 @@ var init_grok2 = __esm({
47527
47780
  effortLevels: ["low", "medium", "high"],
47528
47781
  defaultCli: "grok",
47529
47782
  nativeSource: "grok.acp",
47530
- loginNote: "Grok CLI is not signed in \u2014 run `grok login` in a terminal",
47783
+ loginNote: "Grok is not signed in to your grok.com account \u2014 choose Sign in to Grok in engine setup",
47531
47784
  // No Windows one-liner: the installer is a POSIX shell script, and offering
47532
47785
  // `curl … | bash` there would be advice that cannot run. Windows falls back
47533
47786
  // to docsUrl, which is honest rather than broken.
@@ -47537,8 +47790,11 @@ var init_grok2 = __esm({
47537
47790
  linux: "curl -fsSL https://x.ai/cli/install.sh | bash"
47538
47791
  },
47539
47792
  docsUrl: "https://x.ai/cli",
47793
+ // The terminal route, for people who prefer it; the app's own sign-in
47794
+ // is the device code below, which needs no terminal.
47540
47795
  signInCommand: "grok login"
47541
47796
  },
47797
+ deviceSignIn: GROK_DEVICE_SIGN_IN,
47542
47798
  // Write the [model.slug] block with the instance HOME/GROK_HOME, then pass
47543
47799
  // the slug on argv. spawnArgs must not call ensureGrokInjectSlug itself —
47544
47800
  // that helper defaults to process.env and would miss the instance override.
@@ -47639,7 +47895,7 @@ var init_grok2 = __esm({
47639
47895
  // an unauthenticated CLI is a user action, not something to paper over.
47640
47896
  pickAuthMethod: (methods) => methods.some((m2) => m2.id === "cached_token") ? "cached_token" : null,
47641
47897
  authFailure: "fail",
47642
- isAuthenticated: (env) => existsSync10(join32(harnessHome("grok", env), "auth.json")),
47898
+ isAuthenticated: (env) => grokSignedIn(env),
47643
47899
  // `--append-system-prompt`/`--rules` are accepted by the CLI but do NOT
47644
47900
  // reach the agent-stdio system prompt (verified against 1.0.0), so the
47645
47901
  // persona is prepended codex-style.
@@ -47652,11 +47908,11 @@ ${turn.text}` : turn.text
47652
47908
  });
47653
47909
 
47654
47910
  // server/drivers/acp/gemini.ts
47655
- import { existsSync as existsSync11, readFileSync as readFileSync16 } from "node:fs";
47911
+ import { existsSync as existsSync11, readFileSync as readFileSync17 } from "node:fs";
47656
47912
  import { join as join33 } from "node:path";
47657
47913
  function liveOauthCredential(env) {
47658
47914
  try {
47659
- const creds = JSON.parse(readFileSync16(join33(userHome(env), ".gemini", "oauth_creds.json"), "utf8"));
47915
+ const creds = JSON.parse(readFileSync17(join33(userHome(env), ".gemini", "oauth_creds.json"), "utf8"));
47660
47916
  if (typeof creds.access_token !== "string" || creds.access_token.trim().length === 0) return false;
47661
47917
  const hasRefresh = typeof creds.refresh_token === "string" && creds.refresh_token.trim().length > 0;
47662
47918
  if (creds.expiry_date !== void 0) {
@@ -47717,7 +47973,7 @@ ${turn.text}` : turn.text
47717
47973
  });
47718
47974
 
47719
47975
  // server/drivers/acp/kimi.ts
47720
- import { existsSync as existsSync12, mkdirSync as mkdirSync16, readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "node:fs";
47976
+ import { existsSync as existsSync12, mkdirSync as mkdirSync16, readFileSync as readFileSync18, writeFileSync as writeFileSync10 } from "node:fs";
47721
47977
  import { homedir as homedir16 } from "node:os";
47722
47978
  import { join as join34 } from "node:path";
47723
47979
  function isLocalInjectAlias(slug) {
@@ -48018,7 +48274,7 @@ function ensureKimiInjectAlias(modelId, env = process.env) {
48018
48274
  const path3 = join34(dataRoot, "config.toml");
48019
48275
  let text4 = "";
48020
48276
  try {
48021
- text4 = readFileSync17(path3, "utf8");
48277
+ text4 = readFileSync18(path3, "utf8");
48022
48278
  } catch {
48023
48279
  text4 = "";
48024
48280
  }
@@ -48080,7 +48336,7 @@ function readKimiModelCatalog(env) {
48080
48336
  const dataRoot = kimiDataRoot(env);
48081
48337
  let text4 = "";
48082
48338
  try {
48083
- text4 = readFileSync17(join34(dataRoot, "config.toml"), "utf8");
48339
+ text4 = readFileSync18(join34(dataRoot, "config.toml"), "utf8");
48084
48340
  } catch {
48085
48341
  return STATIC_KIMI_MODELS;
48086
48342
  }
@@ -48199,7 +48455,7 @@ ${turn.text}` : turn.text
48199
48455
  });
48200
48456
 
48201
48457
  // server/drivers/acp/droid.ts
48202
- import { existsSync as existsSync13, mkdirSync as mkdirSync17, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "node:fs";
48458
+ import { existsSync as existsSync13, mkdirSync as mkdirSync17, readFileSync as readFileSync19, writeFileSync as writeFileSync11 } from "node:fs";
48203
48459
  import { join as join35 } from "node:path";
48204
48460
  function authFilePaths(env) {
48205
48461
  const home = env.FACTORY_HOME_OVERRIDE || userHome(env);
@@ -48220,7 +48476,7 @@ function ensureDroidInjectModel(modelId, env = process.env) {
48220
48476
  const path3 = join35(dir, "settings.json");
48221
48477
  let settings2 = {};
48222
48478
  try {
48223
- settings2 = JSON.parse(readFileSync18(path3, "utf8"));
48479
+ settings2 = JSON.parse(readFileSync19(path3, "utf8"));
48224
48480
  } catch (error51) {
48225
48481
  if (existsSync13(path3)) throw error51;
48226
48482
  }
@@ -48257,7 +48513,7 @@ function applyDroidLocalAuthEnv(env, modelId) {
48257
48513
  env.FACTORY_API_KEY = "openmausbot-local";
48258
48514
  }
48259
48515
  function readSettings(env) {
48260
- return JSON.parse(readFileSync18(join35(env.FACTORY_HOME_OVERRIDE || userHome(env), ".factory", "settings.json"), "utf8"));
48516
+ return JSON.parse(readFileSync19(join35(env.FACTORY_HOME_OVERRIDE || userHome(env), ".factory", "settings.json"), "utf8"));
48261
48517
  }
48262
48518
  async function resolveModels(env) {
48263
48519
  let catalog = MODELS2;
@@ -48716,7 +48972,7 @@ var init_attachments = __esm({
48716
48972
 
48717
48973
  // server/drivers/acp/opencode-go.ts
48718
48974
  import { execFile as execFile5 } from "node:child_process";
48719
- import { existsSync as existsSync14, mkdirSync as mkdirSync18, readFileSync as readFileSync19, statSync as statSync6, writeFileSync as writeFileSync12 } from "node:fs";
48975
+ import { existsSync as existsSync14, mkdirSync as mkdirSync18, readFileSync as readFileSync20, statSync as statSync6, writeFileSync as writeFileSync12 } from "node:fs";
48720
48976
  import { homedir as homedir17 } from "node:os";
48721
48977
  import { dirname as dirname14, isAbsolute as isAbsolute9, join as join37, relative as relative2, resolve as resolvePath, sep } from "node:path";
48722
48978
  function labelForModel(id) {
@@ -49035,7 +49291,7 @@ function ensureOpenCodeInjectModel(modelId, env = process.env) {
49035
49291
  let config2 = { $schema: "https://opencode.ai/config.json" };
49036
49292
  if (existsSync14(path3)) {
49037
49293
  try {
49038
- config2 = JSON.parse(readFileSync19(path3, "utf8"));
49294
+ config2 = JSON.parse(readFileSync20(path3, "utf8"));
49039
49295
  } catch {
49040
49296
  }
49041
49297
  }
@@ -49086,7 +49342,7 @@ function hasStoredOpenCodeAuth(env) {
49086
49342
  if (env.OPENCODE_AUTH_CONTENT) candidates.push(env.OPENCODE_AUTH_CONTENT);
49087
49343
  for (const path3 of storedAuthPaths(env)) {
49088
49344
  try {
49089
- candidates.push(readFileSync19(path3, "utf8"));
49345
+ candidates.push(readFileSync20(path3, "utf8"));
49090
49346
  } catch {
49091
49347
  }
49092
49348
  }
@@ -49139,7 +49395,7 @@ function parseJsonc(text4) {
49139
49395
  }
49140
49396
  function readText2(path3) {
49141
49397
  try {
49142
- return readFileSync19(path3, "utf8");
49398
+ return readFileSync20(path3, "utf8");
49143
49399
  } catch {
49144
49400
  return void 0;
49145
49401
  }
@@ -49371,8 +49627,8 @@ ${turn.text}` : turn.text
49371
49627
  });
49372
49628
 
49373
49629
  // server/drivers/acp/qwen.ts
49374
- import { chmodSync as chmodSync6, existsSync as existsSync15, mkdirSync as mkdirSync19, readFileSync as readFileSync20, writeFileSync as writeFileSync13 } from "node:fs";
49375
- import { createHash as createHash12 } from "node:crypto";
49630
+ import { chmodSync as chmodSync6, existsSync as existsSync15, mkdirSync as mkdirSync19, readFileSync as readFileSync21, writeFileSync as writeFileSync13 } from "node:fs";
49631
+ import { createHash as createHash13 } from "node:crypto";
49376
49632
  import { join as join38 } from "node:path";
49377
49633
  function isRecord3(value) {
49378
49634
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -49380,7 +49636,7 @@ function isRecord3(value) {
49380
49636
  function readQwenRoutes(env) {
49381
49637
  let settings2;
49382
49638
  try {
49383
- settings2 = JSON.parse(readFileSync20(join38(harnessHome("qwen", env), "settings.json"), "utf8"));
49639
+ settings2 = JSON.parse(readFileSync21(join38(harnessHome("qwen", env), "settings.json"), "utf8"));
49384
49640
  } catch {
49385
49641
  return [];
49386
49642
  }
@@ -49438,7 +49694,7 @@ function readQwenRoutes(env) {
49438
49694
  }
49439
49695
  }
49440
49696
  const identity = [route.id, route.label, route.envKey ?? null, route.baseUrl === void 0, publicEndpoint];
49441
- route.id = `qwen-route:v1:${createHash12("sha256").update(JSON.stringify(identity)).digest("base64url").slice(0, 16)}`;
49697
+ route.id = `qwen-route:v1:${createHash13("sha256").update(JSON.stringify(identity)).digest("base64url").slice(0, 16)}`;
49442
49698
  }
49443
49699
  if (new Set(routes.map((route) => route.id)).size !== routes.length) {
49444
49700
  throw new Error("Qwen has indistinguishable model routes. Give them distinct names, envKey values, or public endpoints in Qwen settings.");
@@ -49470,7 +49726,7 @@ function ensureQwenInjectModel(modelId, env = process.env) {
49470
49726
  let settings2 = {};
49471
49727
  if (existsSync15(path3)) {
49472
49728
  try {
49473
- settings2 = JSON.parse(readFileSync20(path3, "utf8"));
49729
+ settings2 = JSON.parse(readFileSync21(path3, "utf8"));
49474
49730
  } catch {
49475
49731
  }
49476
49732
  }
@@ -49647,7 +49903,7 @@ var init_custom = __esm({
49647
49903
 
49648
49904
  // server/drivers/acp/hermes.ts
49649
49905
  import { spawn as spawn6 } from "node:child_process";
49650
- import { mkdirSync as mkdirSync20, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "node:fs";
49906
+ import { mkdirSync as mkdirSync20, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "node:fs";
49651
49907
  import { homedir as homedir18 } from "node:os";
49652
49908
  import { join as join39 } from "node:path";
49653
49909
  function bindHermesScreenshotCompat(env, modelId) {
@@ -49701,7 +49957,7 @@ function ensureHermesInjectProvider(modelId, env = process.env) {
49701
49957
  const path3 = join39(dir, "config.yaml");
49702
49958
  let text4 = "";
49703
49959
  try {
49704
- text4 = readFileSync21(path3, "utf8");
49960
+ text4 = readFileSync22(path3, "utf8");
49705
49961
  } catch {
49706
49962
  text4 = "";
49707
49963
  }
@@ -49769,13 +50025,13 @@ function hermesConfiguredModel(env = process.env) {
49769
50025
  const dir = env.HERMES_HOME || harnessHome("hermes", env);
49770
50026
  let secrets = "";
49771
50027
  try {
49772
- secrets = readFileSync21(join39(dir, ".env"), "utf8");
50028
+ secrets = readFileSync22(join39(dir, ".env"), "utf8");
49773
50029
  } catch {
49774
50030
  }
49775
50031
  const hasHostedProviderKey = HERMES_HOSTED_PROVIDER_KEYS.some((name) => nonEmptyDotenvValue(secrets, name));
49776
50032
  let configuredDefault = null;
49777
50033
  try {
49778
- configuredDefault = hermesConfigDefault(readFileSync21(join39(dir, "config.yaml"), "utf8"));
50034
+ configuredDefault = hermesConfigDefault(readFileSync22(join39(dir, "config.yaml"), "utf8"));
49779
50035
  } catch {
49780
50036
  }
49781
50037
  const configuredProvider = configuredDefault?.provider.toLowerCase() ?? "";
@@ -50150,13 +50406,13 @@ var init_openai_compat = __esm({
50150
50406
  });
50151
50407
 
50152
50408
  // server/drivers/pi.ts
50153
- import { chmodSync as chmodSync7, existsSync as existsSync16, mkdirSync as mkdirSync21, mkdtempSync as mkdtempSync3, readFileSync as readFileSync22, rmSync as rmSync7, writeFileSync as writeFileSync15 } from "node:fs";
50409
+ import { chmodSync as chmodSync7, existsSync as existsSync16, mkdirSync as mkdirSync21, mkdtempSync as mkdtempSync3, readFileSync as readFileSync23, rmSync as rmSync7, writeFileSync as writeFileSync15 } from "node:fs";
50154
50410
  import { homedir as homedir19, tmpdir as tmpdir4 } from "node:os";
50155
50411
  import { join as join40 } from "node:path";
50156
50412
  function readPiPromptImages(turn) {
50157
50413
  return (turn.images ?? []).map((image) => ({
50158
50414
  type: "image",
50159
- data: readFileSync22(image.path).toString("base64"),
50415
+ data: readFileSync23(image.path).toString("base64"),
50160
50416
  mimeType: image.mime
50161
50417
  }));
50162
50418
  }
@@ -50304,7 +50560,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
50304
50560
  let root = { providers: {} };
50305
50561
  if (existsSync16(path3)) {
50306
50562
  try {
50307
- const parsed = JSON.parse(readFileSync22(path3, "utf8"));
50563
+ const parsed = JSON.parse(readFileSync23(path3, "utf8"));
50308
50564
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
50309
50565
  root = parsed;
50310
50566
  } else {
@@ -50356,7 +50612,7 @@ function ensurePiInjectModel(modelId, env = process.env) {
50356
50612
  function readPiDefaultModel(env) {
50357
50613
  const home = env.HOME || env.USERPROFILE || homedir19();
50358
50614
  try {
50359
- const s2 = JSON.parse(readFileSync22(`${home}/.pi/agent/settings.json`, "utf8"));
50615
+ const s2 = JSON.parse(readFileSync23(`${home}/.pi/agent/settings.json`, "utf8"));
50360
50616
  return s2.defaultProvider && s2.defaultModel ? `${s2.defaultProvider}/${s2.defaultModel}` : "";
50361
50617
  } catch {
50362
50618
  return "";
@@ -50553,10 +50809,10 @@ var init_pi = __esm({
50553
50809
  await readModels();
50554
50810
  };
50555
50811
  await readModels();
50556
- const listeners = /* @__PURE__ */ new Set();
50812
+ const listeners2 = /* @__PURE__ */ new Set();
50557
50813
  const active = /* @__PURE__ */ new Map();
50558
50814
  const emit = (event) => {
50559
- for (const l of Array.from(listeners)) l(event);
50815
+ for (const l of Array.from(listeners2)) l(event);
50560
50816
  };
50561
50817
  const base2 = (threadId, turnId) => ({
50562
50818
  eventId: newEventId(),
@@ -50937,7 +51193,7 @@ var init_pi = __esm({
50937
51193
  if (scopeReadyPath) {
50938
51194
  let ready = false;
50939
51195
  try {
50940
- const receipt = JSON.parse(readFileSync22(scopeReadyPath, "utf8"));
51196
+ const receipt = JSON.parse(readFileSync23(scopeReadyPath, "utf8"));
50941
51197
  ready = receipt.ok === true && JSON.stringify(receipt.toolScope) === JSON.stringify(toolScope);
50942
51198
  } catch {
50943
51199
  }
@@ -51095,13 +51351,13 @@ ${turn.text}` : turn.text;
51095
51351
  for (const { stop } of active.values()) stop();
51096
51352
  },
51097
51353
  onEvent: (listener) => {
51098
- listeners.add(listener);
51099
- return () => listeners.delete(listener);
51354
+ listeners2.add(listener);
51355
+ return () => listeners2.delete(listener);
51100
51356
  }
51101
51357
  },
51102
51358
  dispose: async () => {
51103
51359
  for (const { stop } of active.values()) stop();
51104
- listeners.clear();
51360
+ listeners2.clear();
51105
51361
  }
51106
51362
  };
51107
51363
  }
@@ -51347,7 +51603,7 @@ var init_cerebras = __esm({
51347
51603
  });
51348
51604
 
51349
51605
  // server/drivers/minimax.ts
51350
- import { readFileSync as readFileSync23 } from "node:fs";
51606
+ import { readFileSync as readFileSync24 } from "node:fs";
51351
51607
  import { homedir as homedir20 } from "node:os";
51352
51608
  import { join as join41 } from "node:path";
51353
51609
  function normalizedApiUrl(value) {
@@ -51356,7 +51612,7 @@ function normalizedApiUrl(value) {
51356
51612
  }
51357
51613
  function loadLocalMiniMaxConfig(home = homedir20()) {
51358
51614
  try {
51359
- const raw = localConfigSchema.parse(JSON.parse(readFileSync23(join41(home, ".mmx", "config.json"), "utf8")));
51615
+ const raw = localConfigSchema.parse(JSON.parse(readFileSync24(join41(home, ".mmx", "config.json"), "utf8")));
51360
51616
  const configuredUrl = raw.base_url?.trim() ? raw.base_url : raw.region === "cn" ? CN_URL : DEFAULT_URL4;
51361
51617
  return {
51362
51618
  apiKey: raw.api_key?.trim() ?? "",
@@ -51632,7 +51888,7 @@ __export(cli_setup_exports, {
51632
51888
  saveCliStartup: () => saveCliStartup
51633
51889
  });
51634
51890
  import { spawn as spawn7 } from "node:child_process";
51635
- import { readFileSync as readFileSync24 } from "node:fs";
51891
+ import { readFileSync as readFileSync25 } from "node:fs";
51636
51892
  import { join as join42, resolve as resolve11 } from "node:path";
51637
51893
  import { randomUUID as randomUUID13 } from "node:crypto";
51638
51894
  async function inspect(id, entry) {
@@ -51670,7 +51926,7 @@ function assertDataDir(dataDir) {
51670
51926
  }
51671
51927
  function checkStoredConfig(dataDir) {
51672
51928
  try {
51673
- parseStoredConfig(JSON.parse(readFileSync24(join42(dataDir, "config.json"), "utf8")));
51929
+ parseStoredConfig(JSON.parse(readFileSync25(join42(dataDir, "config.json"), "utf8")));
51674
51930
  } catch (error51) {
51675
51931
  if (error51.code === "ENOENT") return;
51676
51932
  throw new Error("Existing config.json could not be read or validated. Setup has not changed it; repair it before continuing.");
@@ -52106,13 +52362,13 @@ __export(browser_engine_exports, {
52106
52362
  setBrowserViewport: () => setBrowserViewport
52107
52363
  });
52108
52364
  import { spawn as spawn8 } from "node:child_process";
52109
- import { createHash as createHash13, randomBytes as randomBytes4, randomUUID as randomUUID14 } from "node:crypto";
52110
- import { chmodSync as chmodSync8, constants as constants2, copyFileSync, existsSync as existsSync17, mkdirSync as mkdirSync22, readFileSync as readFileSync25, readdirSync as readdirSync3, renameSync as renameSync6, rmSync as rmSync8, statSync as statSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync16 } from "node:fs";
52365
+ import { createHash as createHash14, randomBytes as randomBytes4, randomUUID as randomUUID14 } from "node:crypto";
52366
+ import { chmodSync as chmodSync8, constants as constants3, copyFileSync, existsSync as existsSync17, mkdirSync as mkdirSync22, readFileSync as readFileSync26, readdirSync as readdirSync3, renameSync as renameSync6, rmSync as rmSync8, statSync as statSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync16 } from "node:fs";
52111
52367
  import { homedir as homedir21, tmpdir as tmpdir5 } from "node:os";
52112
52368
  import { delimiter as delimiter6, dirname as dirname15, join as join45, resolve as resolve12 } from "node:path";
52113
52369
  function browserRestoreKey(session) {
52114
52370
  if (!/^[A-Za-z0-9_.-]{1,96}$/.test(session)) throw new Error("Invalid browser session.");
52115
- return `omb-${createHash13("sha256").update(session).digest("hex")}`;
52371
+ return `omb-${createHash14("sha256").update(session).digest("hex")}`;
52116
52372
  }
52117
52373
  function browserSessionsDirectory(env) {
52118
52374
  const namespace = env.AGENT_BROWSER_NAMESPACE;
@@ -52131,7 +52387,7 @@ function ensureManagedBrowserConfig(env) {
52131
52387
  writeFileSync16(path3, "{}\n", { flag: "wx", mode: 384 });
52132
52388
  } catch (error51) {
52133
52389
  if (error51.code !== "EEXIST") throw error51;
52134
- if (readFileSync25(path3, "utf8").trim() !== "{}") throw new Error("The managed browser configuration was changed. Restore it to an empty JSON object before connecting.");
52390
+ if (readFileSync26(path3, "utf8").trim() !== "{}") throw new Error("The managed browser configuration was changed. Restore it to an empty JSON object before connecting.");
52135
52391
  }
52136
52392
  return path3;
52137
52393
  }
@@ -52246,7 +52502,7 @@ async function prepareBrowserSessionState(binaryPath, session, options = {}) {
52246
52502
  const source = candidates[0];
52247
52503
  if (!source) return;
52248
52504
  try {
52249
- copyFileSync(source.path, targetBase + source.suffix, constants2.COPYFILE_EXCL);
52505
+ copyFileSync(source.path, targetBase + source.suffix, constants3.COPYFILE_EXCL);
52250
52506
  chmodSync8(targetBase + source.suffix, 384);
52251
52507
  } catch (error51) {
52252
52508
  if (error51.code !== "EEXIST") throw error51;
@@ -52347,7 +52603,7 @@ async function installAgentBrowserBinary(options = {}) {
52347
52603
  clearTimeout(timer);
52348
52604
  }
52349
52605
  if (body.length !== asset.bytes) throw new Error("the agent-browser download did not match its pinned size; nothing was installed");
52350
- const digest3 = createHash13("sha256").update(body).digest("hex");
52606
+ const digest3 = createHash14("sha256").update(body).digest("hex");
52351
52607
  if (digest3 !== asset.sha256) throw new Error("the agent-browser download failed its SHA-256 check; nothing was installed");
52352
52608
  try {
52353
52609
  for (const name of readdirSync3(directory)) {
@@ -52414,7 +52670,7 @@ function ensureChrome(binaryPath, options = {}) {
52414
52670
  function browserEngineEncryptionKey(dataDir = DATA_DIR) {
52415
52671
  const file2 = join45(dataDir, KEY_FILE);
52416
52672
  try {
52417
- const existing = readFileSync25(file2, "utf8").trim();
52673
+ const existing = readFileSync26(file2, "utf8").trim();
52418
52674
  if (/^[0-9a-f]{64}$/u.test(existing)) return existing;
52419
52675
  } catch {
52420
52676
  }
@@ -52536,7 +52792,7 @@ function agentBrowserFrame(input) {
52536
52792
  fail(error51);
52537
52793
  return;
52538
52794
  }
52539
- settle({ png: readFileSync25(file2).toString("base64"), format: "png" });
52795
+ settle({ png: readFileSync26(file2).toString("base64"), format: "png" });
52540
52796
  } catch {
52541
52797
  fail(new Error("the browser reported a picture it did not write"));
52542
52798
  } finally {
@@ -52623,7 +52879,7 @@ var init_browser_engine = __esm({
52623
52879
  var import_qrcode_terminal = __toESM(require_main(), 1);
52624
52880
  import { spawn as spawn9 } from "node:child_process";
52625
52881
  import { randomBytes as randomBytes5 } from "node:crypto";
52626
- import { closeSync as closeSync8, existsSync as existsSync18, mkdirSync as mkdirSync23, openSync as openSync8, readFileSync as readFileSync26 } from "node:fs";
52882
+ import { closeSync as closeSync8, existsSync as existsSync18, mkdirSync as mkdirSync23, openSync as openSync8, readFileSync as readFileSync27 } from "node:fs";
52627
52883
  import { homedir as homedir22 } from "node:os";
52628
52884
  import { dirname as dirname16, join as join46, resolve as resolve13 } from "node:path";
52629
52885
  import { createInterface as createInterface2 } from "node:readline/promises";
@@ -54882,7 +55138,7 @@ function runServiceCommand(input, io) {
54882
55138
  // server/fleet-cli.ts
54883
55139
  init_atomic();
54884
55140
  import { execFileSync, spawn as spawn3 } from "node:child_process";
54885
- import { appendFileSync, closeSync as closeSync2, existsSync as existsSync3, fsyncSync as fsyncSync2, lstatSync, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync, rmSync as rmSync2 } from "node:fs";
55141
+ import { appendFileSync, closeSync as closeSync2, existsSync as existsSync3, fsyncSync as fsyncSync2, lstatSync as lstatSync2, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync as readFileSync2, rmSync as rmSync2 } from "node:fs";
54886
55142
  import { posix as posix3 } from "node:path";
54887
55143
 
54888
55144
  // server/fleet.ts
@@ -55458,14 +55714,14 @@ function defaultFleetDeps() {
55458
55714
  }),
55459
55715
  pathExists: (path3) => {
55460
55716
  try {
55461
- lstatSync(path3);
55717
+ lstatSync2(path3);
55462
55718
  return true;
55463
55719
  } catch (error51) {
55464
55720
  if (error51.code === "ENOENT") return false;
55465
55721
  throw error51;
55466
55722
  }
55467
55723
  },
55468
- readText: (path3, owner) => owner ? tenantIo(owner, path3, "read") : existsSync3(path3) ? readFileSync(path3, "utf8") : null,
55724
+ readText: (path3, owner) => owner ? tenantIo(owner, path3, "read") : existsSync3(path3) ? readFileSync2(path3, "utf8") : null,
55469
55725
  usage: (dataDir, owner, now) => {
55470
55726
  const at = now.toISOString();
55471
55727
  return tenantIo(owner, posix3.join(dataDir, "usage", `${at.slice(0, 7)}.jsonl`), "usage", at);
@@ -55488,7 +55744,7 @@ function defaultFleetDeps() {
55488
55744
  else mkdirSync3(path3, { recursive: true, mode });
55489
55745
  },
55490
55746
  appendOnce: (path3, line) => {
55491
- const current = existsSync3(path3) ? readFileSync(path3, "utf8") : "";
55747
+ const current = existsSync3(path3) ? readFileSync2(path3, "utf8") : "";
55492
55748
  if (!current.split("\n").some((existing) => existing.trim() === line)) appendFileSync(path3, `${current.endsWith("\n") || !current ? "" : "\n"}
55493
55749
  ${line}
55494
55750
  `);
@@ -55722,12 +55978,22 @@ import { dirname as dirname3 } from "node:path";
55722
55978
  var MAX_BODY = 256 * 1024;
55723
55979
  function readBody(req) {
55724
55980
  return new Promise((resolve14, reject) => {
55725
- let text4 = "";
55981
+ const chunks = [];
55982
+ let bytes = 0;
55983
+ let tooLarge = false;
55726
55984
  req.on("data", (chunk) => {
55727
- text4 += chunk.toString("utf8");
55728
- if (text4.length > MAX_BODY) reject(Object.assign(new Error("request too large"), { status: 413 }));
55985
+ if (tooLarge) return;
55986
+ bytes += chunk.length;
55987
+ if (bytes > MAX_BODY) {
55988
+ tooLarge = true;
55989
+ chunks.length = 0;
55990
+ return reject(Object.assign(new Error("request too large"), { status: 413 }));
55991
+ }
55992
+ chunks.push(chunk);
55729
55993
  });
55730
55994
  req.on("end", () => {
55995
+ if (tooLarge) return;
55996
+ const text4 = Buffer.concat(chunks).toString("utf8");
55731
55997
  if (!text4.trim()) return resolve14({});
55732
55998
  try {
55733
55999
  const value = JSON.parse(text4);
@@ -56142,7 +56408,7 @@ function phonePairingInstructions(phone, input) {
56142
56408
  // server/tunnel.ts
56143
56409
  import { spawn as spawn4 } from "node:child_process";
56144
56410
  import { randomUUID as randomUUID4 } from "node:crypto";
56145
- import { chmodSync as chmodSync3, existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync2, statSync } from "node:fs";
56411
+ import { chmodSync as chmodSync3, existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync3, statSync } from "node:fs";
56146
56412
  import { hostname as hostname3 } from "node:os";
56147
56413
  import { dirname as dirname4, join as join8, resolve as resolve2 } from "node:path";
56148
56414
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -56233,7 +56499,7 @@ function openTunnelCredentials(dataDir) {
56233
56499
  if (existsSync5(file2)) {
56234
56500
  try {
56235
56501
  if (process.platform !== "win32" && (statSync(file2).mode & 63) !== 0) chmodSync3(file2, 384);
56236
- const parsed = plainDocument(JSON.parse(readFileSync2(file2, "utf8")));
56502
+ const parsed = plainDocument(JSON.parse(readFileSync3(file2, "utf8")));
56237
56503
  if (!parsed) throw new Error("not a JSON object");
56238
56504
  initial = parsed;
56239
56505
  status = "ok";
@@ -56583,7 +56849,7 @@ function defaultIo() {
56583
56849
  }
56584
56850
  function serverVersion2(here = HERE2) {
56585
56851
  try {
56586
- const parsed = JSON.parse(readFileSync26(resolve13(here, "..", "package.json"), "utf8"));
56852
+ const parsed = JSON.parse(readFileSync27(resolve13(here, "..", "package.json"), "utf8"));
56587
56853
  const version2 = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "version") : void 0;
56588
56854
  return typeof version2 === "string" && version2 ? version2 : "0.0.0";
56589
56855
  } catch {
@@ -56614,7 +56880,7 @@ async function isWorkspaceRunning(options) {
56614
56880
  try {
56615
56881
  const { status, body } = await api(options.port, "/api/health");
56616
56882
  if (status !== 200 || body?.app !== "openmausbot") return false;
56617
- const expected = readFileSync26(join46(options.dataDir, "environment-id"), "utf8").trim();
56883
+ const expected = readFileSync27(join46(options.dataDir, "environment-id"), "utf8").trim();
56618
56884
  const descriptor = await api(options.port, "/.well-known/openmausbot/environment");
56619
56885
  return /^[0-9a-f-]{36}$/i.test(expected) && descriptor.status === 200 && descriptor.body?.environmentId === expected;
56620
56886
  } catch {
@@ -56849,7 +57115,7 @@ async function runAccess(options, io = defaultIo()) {
56849
57115
  let raw = {};
56850
57116
  if (existsSync18(file2)) {
56851
57117
  try {
56852
- const parsed = JSON.parse(readFileSync26(file2, "utf8"));
57118
+ const parsed = JSON.parse(readFileSync27(file2, "utf8"));
56853
57119
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("not an object");
56854
57120
  raw = Object.fromEntries(Object.entries(parsed));
56855
57121
  } catch (error51) {
@@ -56869,15 +57135,15 @@ async function runAccess(options, io = defaultIo()) {
56869
57135
  if (!sharedSignIn({ admins, members }) && !sharedSignIn(next) && !hostedWorkspaceConfigured()) return;
56870
57136
  const decisions = raw.decisions && typeof raw.decisions === "object" ? raw.decisions.retentionDays : void 0;
56871
57137
  bindDecisionRetention(() => decisionRetentionDays(typeof decisions === "number" ? decisions : void 0));
56872
- const changed = ["admins", "members"].filter((key) => next[key].join(",") !== (key === "admins" ? admins : members).join(","));
57138
+ const changed2 = ["admins", "members"].filter((key) => next[key].join(",") !== (key === "admins" ? admins : members).join(","));
56873
57139
  appendAdminAction(options.dataDir, {
56874
57140
  category: "people",
56875
57141
  action: "people.update",
56876
57142
  target: { kind: "settings" },
56877
57143
  actor: { kind: "cli" },
56878
- changed: changed.map((key) => `signIn.${key}`),
56879
- before: Object.fromEntries(changed.map((key) => [`signIn.${key}`, key === "admins" ? admins : members])),
56880
- after: Object.fromEntries(changed.map((key) => [`signIn.${key}`, next[key]]))
57144
+ changed: changed2.map((key) => `signIn.${key}`),
57145
+ before: Object.fromEntries(changed2.map((key) => [`signIn.${key}`, key === "admins" ? admins : members])),
57146
+ after: Object.fromEntries(changed2.map((key) => [`signIn.${key}`, next[key]]))
56881
57147
  });
56882
57148
  await flushAdminActivity(options.dataDir);
56883
57149
  };