openmausbot 0.1.91 → 0.1.92

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 (150) hide show
  1. package/dist/assets/DesktopViewer-Do6lBa4C.js +5 -0
  2. package/dist/assets/{abnfDiagram-O67JEVCF-yP-w8pRq.js → abnfDiagram-O67JEVCF-Cd3X7Hxc.js} +1 -1
  3. package/dist/assets/{arc-DsivfZEh.js → arc-DIYc7Ymx.js} +1 -1
  4. package/dist/assets/{architectureDiagram-NJMV4G6O-CILw7lzT.js → architectureDiagram-NJMV4G6O-DBmRGVyP.js} +1 -1
  5. package/dist/assets/{blockDiagram-BEXU5L5S-DEQUV3HF.js → blockDiagram-BEXU5L5S-CPFePSEE.js} +1 -1
  6. package/dist/assets/{c4Diagram-YGBWAQC7-lSXCZoKA.js → c4Diagram-YGBWAQC7-DD_DO1qy.js} +1 -1
  7. package/dist/assets/channel-Dtk5Mtic.js +1 -0
  8. package/dist/assets/{chunk-2Q5K7J3B-QCD5bgWP.js → chunk-2Q5K7J3B-BNEeNSjX.js} +1 -1
  9. package/dist/assets/{chunk-3FUC2YCW-C5-iZJuL.js → chunk-3FUC2YCW-BbinLaqH.js} +2 -2
  10. package/dist/assets/{chunk-5VM5RSS4-cxsawMpY.js → chunk-5VM5RSS4-BBgYrgDR.js} +1 -1
  11. package/dist/assets/{chunk-GTNCS2PH-DV8tV8H7.js → chunk-GTNCS2PH-ezXj-kA-.js} +1 -1
  12. package/dist/assets/{chunk-GWA4HPMP-CJFZyfbW.js → chunk-GWA4HPMP-9FxZ3diV.js} +1 -1
  13. package/dist/assets/{chunk-JWPE2WC7-BigbMXco.js → chunk-JWPE2WC7-CO-40JpI.js} +1 -1
  14. package/dist/assets/{chunk-NETBCI7D-oVxTfpDy.js → chunk-NETBCI7D-BEhjslYE.js} +1 -1
  15. package/dist/assets/{chunk-WEXAMYUT-CH4BiroW.js → chunk-WEXAMYUT-CNeq4n7_.js} +1 -1
  16. package/dist/assets/{chunk-XXDRQBXY-KEDa6gax.js → chunk-XXDRQBXY-OsCY7KSi.js} +1 -1
  17. package/dist/assets/{classDiagram-v2-NBCMYWYE-BiPjfqVv.js → classDiagram-v2-NBCMYWYE-CloRUEtk.js} +1 -1
  18. package/dist/assets/{cose-bilkent-JH36ORCC-Caxew3OY.js → cose-bilkent-JH36ORCC-D1BGZhp4.js} +1 -1
  19. package/dist/assets/{cynefin-OW5HDTMX-B_Pui369.js → cynefin-OW5HDTMX-CUf4T2CN.js} +1 -1
  20. package/dist/assets/{cynefinDiagram-VND7K2PF-X64ydIhP.js → cynefinDiagram-VND7K2PF-DPEZrFGA.js} +1 -1
  21. package/dist/assets/{dagre-6A5THRUB-TqYCI1E1.js → dagre-6A5THRUB-bDe6GIv5.js} +1 -1
  22. package/dist/assets/{diagram-22UHCM2B-TeOt3rAQ.js → diagram-22UHCM2B-CdcUQz55.js} +1 -1
  23. package/dist/assets/{diagram-3UASUU5V-D70BLhDA.js → diagram-3UASUU5V-BZpqGg7c.js} +1 -1
  24. package/dist/assets/{diagram-ATOU4E4O-BbQDLwUC.js → diagram-ATOU4E4O-DvGM6ugp.js} +1 -1
  25. package/dist/assets/{diagram-CDSNMT55-ChNJZ1u-.js → diagram-CDSNMT55-DSTmMm0s.js} +1 -1
  26. package/dist/assets/{diagram-MLGK6HIB-DMBl2APa.js → diagram-MLGK6HIB-15cucnMn.js} +1 -1
  27. package/dist/assets/{diagram-MPIPVDR6-CEqDchOp.js → diagram-MPIPVDR6-tMbuaE6n.js} +1 -1
  28. package/dist/assets/{ebnfDiagram-ZINNZB2B-BVDKRp_-.js → ebnfDiagram-ZINNZB2B-BYxYzjHI.js} +1 -1
  29. package/dist/assets/{elk-276RUBZZ-CRkofO9B.js → elk-276RUBZZ-v6bj852W.js} +1 -1
  30. package/dist/assets/{erDiagram-OPXOYQCR-DruOMh5e.js → erDiagram-OPXOYQCR-5lBBlnd2.js} +1 -1
  31. package/dist/assets/{flowDiagram-KWPJA3E3-8GMQeB7t.js → flowDiagram-KWPJA3E3-CZyl1zIj.js} +1 -1
  32. package/dist/assets/{ganttDiagram-FUAMR5RP-ORE9jcsM.js → ganttDiagram-FUAMR5RP-BiAI-ltZ.js} +1 -1
  33. package/dist/assets/{gitGraphDiagram-X574FWY7-BmsedsWh.js → gitGraphDiagram-X574FWY7-BAZISkj3.js} +1 -1
  34. package/dist/assets/index-Cdmjrl28.css +1 -0
  35. package/dist/assets/{index-DKNEM8Q6.js → index-DGBaEXrM.js} +1 -1
  36. package/dist/assets/index-DR7YPMkW.js +597 -0
  37. package/dist/assets/{infoDiagram-VRGFBTTK-E_p7AooJ.js → infoDiagram-VRGFBTTK-B6ooOy-Z.js} +1 -1
  38. package/dist/assets/{ishikawaDiagram-OU5B5YK6-Y-B2mKda.js → ishikawaDiagram-OU5B5YK6-BZ9ksi-B.js} +1 -1
  39. package/dist/assets/{journeyDiagram-ZHPQQLJL-eA8RELcK.js → journeyDiagram-ZHPQQLJL-DWqlzIEe.js} +1 -1
  40. package/dist/assets/{kanban-definition-PNTS6WVX-DqIL2e8e.js → kanban-definition-PNTS6WVX-JX6piDwL.js} +1 -1
  41. package/dist/assets/{linear-yC_29R6e.js → linear-CmOWzX0P.js} +1 -1
  42. package/dist/assets/{mermaid.core-VhEMkSND.js → mermaid.core-CF1zwoQ-.js} +5 -5
  43. package/dist/assets/{mindmap-definition-NLK3R4M7-oJ7kyQZy.js → mindmap-definition-NLK3R4M7-CGh2K4fW.js} +1 -1
  44. package/dist/assets/{pegDiagram-GJSIUBJH-BmfYcY0O.js → pegDiagram-GJSIUBJH-CDwYN0s7.js} +1 -1
  45. package/dist/assets/{pieDiagram-5QR66LMP-DKvgP7w9.js → pieDiagram-5QR66LMP-1rfjJocB.js} +1 -1
  46. package/dist/assets/{quadrantDiagram-O4NWA36T-BEKL3W0L.js → quadrantDiagram-O4NWA36T-tKPvPiok.js} +1 -1
  47. package/dist/assets/{railroadDiagram-XR7U4H2S-BEsiqR5s.js → railroadDiagram-XR7U4H2S-BHLm5vXR.js} +1 -1
  48. package/dist/assets/{requirementDiagram-PLB6GJNP-DWBcWvAP.js → requirementDiagram-PLB6GJNP-CB45STp7.js} +1 -1
  49. package/dist/assets/{sankeyDiagram-IPEJSGJF-rpZ2_O4s.js → sankeyDiagram-IPEJSGJF-55QA_huC.js} +1 -1
  50. package/dist/assets/{sequenceDiagram-PO4LG4MO-B99HQdJR.js → sequenceDiagram-PO4LG4MO-BckFmQHn.js} +1 -1
  51. package/dist/assets/{sizeCapture-INFHLROL-BjN0qT5T.js → sizeCapture-INFHLROL-BFCOJF11.js} +1 -1
  52. package/dist/assets/{stateDiagram-v2-GCMORJYK-BbsVgGsC.js → stateDiagram-v2-GCMORJYK-CZvmiVVN.js} +1 -1
  53. package/dist/assets/{swimlanes-2SLR337P-DG3gKkeR.js → swimlanes-2SLR337P-BpDUCEKy.js} +1 -1
  54. package/dist/assets/swimlanesDiagram-TC7HE7FX-M0GYW9Y3.js +8 -0
  55. package/dist/assets/{timeline-definition-EJHVYXUP-BTHOdtF_.js → timeline-definition-EJHVYXUP-BpxHq6IK.js} +1 -1
  56. package/dist/assets/{usecaseDiagram-POWQR4AR-acLSLFwc.js → usecaseDiagram-POWQR4AR-Cj66vm02.js} +1 -1
  57. package/dist/assets/{vennDiagram-UO4OBE2U-C5JzNX9G.js → vennDiagram-UO4OBE2U-C9nRkWE_.js} +1 -1
  58. package/dist/assets/{wardleyDiagram-VNRHLVJA-DL1-PrR_.js → wardleyDiagram-VNRHLVJA-D-me_nWj.js} +1 -1
  59. package/dist/assets/{xychartDiagram-PMCCYNJV-Bvr1477p.js → xychartDiagram-PMCCYNJV-Dc_fL4mk.js} +1 -1
  60. package/dist/index.html +2 -2
  61. package/dist/novnc-NOTICE.txt +118 -0
  62. package/dist-server/cloud-home-start.js +112 -14
  63. package/dist-server/container-mcp.js +195 -15
  64. package/dist-server/drivers/agents-proxy.js +5 -4
  65. package/dist-server/enterprise/server/index.js +5 -1
  66. package/dist-server/index.js +9300 -4155
  67. package/dist-server/local-computer-proxy.js +128 -7
  68. package/dist-server/mcp-gate.js +11 -5
  69. package/dist-server/model-catalog/models-dev.snapshot.json +7299 -0
  70. package/dist-server/openmausbot.js +3910 -1182
  71. package/dist-server/pair-cli.js +3910 -1182
  72. package/dist-server/server/boat.js +2 -2
  73. package/dist-server/server/browser-engine.js +10 -1
  74. package/dist-server/server/browser-live.js +46 -2
  75. package/dist-server/server/browser-runtime.js +74 -8
  76. package/dist-server/server/browser-tool-shape.js +6 -4
  77. package/dist-server/server/chief-of-staff.js +1 -1
  78. package/dist-server/server/cloud-home-start.js +118 -26
  79. package/dist-server/server/cloud-home.js +39 -3
  80. package/dist-server/server/cloud-lending.js +74 -10
  81. package/dist-server/server/cloud-owner.js +196 -0
  82. package/dist-server/server/cloud-secrets-boot.js +6 -0
  83. package/dist-server/server/cloud-secrets.js +65 -0
  84. package/dist-server/server/config.js +174 -14
  85. package/dist-server/server/container-computer.js +3 -2
  86. package/dist-server/server/desktop-viewer-targets.js +26 -0
  87. package/dist-server/server/drivers/acp/core.js +167 -21
  88. package/dist-server/server/drivers/acp/opencode-go.js +614 -85
  89. package/dist-server/server/drivers/agents-catalog.js +6 -6
  90. package/dist-server/server/drivers/antigravity-acp.js +69 -11
  91. package/dist-server/server/drivers/antigravity-temp.js +360 -0
  92. package/dist-server/server/drivers/antigravity.js +21 -0
  93. package/dist-server/server/drivers/chatgpt-plan-auth.js +481 -0
  94. package/dist-server/server/drivers/claude.js +312 -34
  95. package/dist-server/server/drivers/codex.js +228 -18
  96. package/dist-server/server/drivers/grok.js +2 -2
  97. package/dist-server/server/drivers/mistral.js +4 -3
  98. package/dist-server/server/drivers/openai-chat-protocol.js +4 -3
  99. package/dist-server/server/drivers/openai-chat.js +41 -3
  100. package/dist-server/server/drivers/openai-compat.js +73 -23
  101. package/dist-server/server/engine-install.js +46 -12
  102. package/dist-server/server/env-path.js +15 -3
  103. package/dist-server/server/harness/registry.js +2 -2
  104. package/dist-server/server/index.js +1338 -184
  105. package/dist-server/server/lending-memory.js +479 -0
  106. package/dist-server/server/local-vm-idle.js +24 -6
  107. package/dist-server/server/managed-desktop.js +3 -0
  108. package/dist-server/server/mcp-bridge.js +5 -3
  109. package/dist-server/server/mcp-http.js +6 -3
  110. package/dist-server/server/mcp-oauth-discovery.js +133 -0
  111. package/dist-server/server/mcp-oauth-store.js +70 -0
  112. package/dist-server/server/mcp-oauth.js +550 -0
  113. package/dist-server/server/mcp-probe.js +14 -1
  114. package/dist-server/server/mcp-registry.js +43 -0
  115. package/dist-server/server/memory-upkeep.js +94 -84
  116. package/dist-server/server/model-catalog/catalog.js +374 -0
  117. package/dist-server/server/model-catalog/presets.js +176 -0
  118. package/dist-server/server/model-catalog/trim.js +219 -0
  119. package/dist-server/server/provider-key-check.js +4 -2
  120. package/dist-server/server/recent-work.js +1 -1
  121. package/dist-server/server/request-auth.js +23 -0
  122. package/dist-server/server/routes/antigravity-leftovers.js +45 -0
  123. package/dist-server/server/routes/bot-memory.js +31 -6
  124. package/dist-server/server/routes/desktop-viewer.js +194 -0
  125. package/dist-server/server/routine-requests.js +1 -1
  126. package/dist-server/server/routines.js +1 -1
  127. package/dist-server/server/sessions.js +56 -9
  128. package/dist-server/server/skills.js +25 -10
  129. package/dist-server/server/store.js +62 -122
  130. package/dist-server/server/thread-events.js +2 -0
  131. package/dist-server/server/thread-starters.js +33 -0
  132. package/dist-server/server/thread-title.js +46 -0
  133. package/dist-server/server/tts/fish.js +4 -5
  134. package/dist-server/server/tts/index.js +5 -3
  135. package/dist-server/server/vps-computer.js +79 -12
  136. package/dist-server/server/webhooks.js +18 -1
  137. package/dist-server/server/workspace-backup-policy.js +3 -0
  138. package/dist-server/server/workspace-backup.js +3 -0
  139. package/dist-server/server/workspace.js +65 -15
  140. package/dist-server/shared/workspace-backup-client.js +1 -0
  141. package/dist-server/src/lib/citations.js +177 -0
  142. package/dist-server/src/lib/composer-attachments.js +7 -1
  143. package/dist-server/vps-container-mcp.js +196 -15
  144. package/dist-server/workspace-backup.worker.js +73 -9
  145. package/enterprise/server/index.js +5 -1
  146. package/package.json +1 -1
  147. package/dist/assets/channel-DrfGMu7X.js +0 -1
  148. package/dist/assets/index-BlnCEcU6.js +0 -588
  149. package/dist/assets/index-DVxjOvID.css +0 -1
  150. package/dist/assets/swimlanesDiagram-TC7HE7FX-BCZhxl7e.js +0 -8
@@ -19,6 +19,9 @@ const optionalText = z.string().optional();
19
19
  const SSH_ALIAS = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/;
20
20
  const LEGACY_BROWSER_PROFILE_ID = /^[A-Za-z0-9_-]{1,40}$/;
21
21
  const BROWSER_PROFILE_ID = /^[a-z0-9_-]{1,40}$/;
22
+ /** Fish Audio speech models (`tts.fishModel`). `s2.1-pro-free` is Fish's
23
+ * free developer tier; an unset model means `s2.1-pro`. */
24
+ export const FISH_TTS_MODELS = ["s2.1-pro", "s2.1-pro-free"];
22
25
  export const DEFAULT_ROOM_TURN_TIMEOUT_MINUTES = 5;
23
26
  export const MIN_ROOM_TURN_TIMEOUT_MINUTES = 1;
24
27
  export const MAX_ROOM_TURN_TIMEOUT_MINUTES = 1_440;
@@ -36,6 +39,11 @@ export const DEFAULT_LOCAL_VM_MODE = "shared";
36
39
  export const DEFAULT_LOCAL_VM_MAX_INSTANCES = 2;
37
40
  export const MIN_LOCAL_VM_MAX_INSTANCES = 1;
38
41
  export const MAX_LOCAL_VM_MAX_INSTANCES = 8;
42
+ /** Idle window before a Local VM's disposable container is recycled. The
43
+ * default keeps the historical 8-hour window for existing configs. */
44
+ export const DEFAULT_LOCAL_VM_IDLE_TIMEOUT_MINUTES = 480;
45
+ export const MIN_LOCAL_VM_IDLE_TIMEOUT_MINUTES = 5;
46
+ export const MAX_LOCAL_VM_IDLE_TIMEOUT_MINUTES = 1_440;
39
47
  export function isValidSshAlias(value) {
40
48
  return typeof value === "string" && SSH_ALIAS.test(value);
41
49
  }
@@ -112,6 +120,12 @@ const localVmConfigSchema = z.object({
112
120
  .min(MIN_LOCAL_VM_MAX_INSTANCES)
113
121
  .max(MAX_LOCAL_VM_MAX_INSTANCES)
114
122
  .optional(),
123
+ idleTimeoutMinutes: z
124
+ .number()
125
+ .int()
126
+ .min(MIN_LOCAL_VM_IDLE_TIMEOUT_MINUTES)
127
+ .max(MAX_LOCAL_VM_IDLE_TIMEOUT_MINUTES)
128
+ .optional(),
115
129
  });
116
130
  /** A named, shareable browser session ("Work", "Client A"). The id names a
117
131
  * durable Electron partition; user-controlled characters never reach it. */
@@ -302,6 +316,10 @@ const onboardingConfigSchema = z.object({
302
316
  version: z.number().int().min(0).max(1000).optional(),
303
317
  reelSeen: z.boolean().optional(),
304
318
  hintsSeen: z.array(z.string().trim().min(1).max(60)).max(100).optional(),
319
+ /** OMB Cloud home only: when a bot's turn first finished on this machine
320
+ * (cloud-home.ts firstCloudTurnPatch). Written by the server, read by the
321
+ * Cloud's setup checklist. */
322
+ firstTurnAt: z.string().trim().max(40).optional(),
305
323
  }).strict();
306
324
  const instanceConfigSchema = z.object({
307
325
  driver: z.string().min(1),
@@ -314,6 +332,7 @@ const instanceConfigSchema = z.object({
314
332
  ]).optional(),
315
333
  environment: z.record(z.string(), z.string()).optional(),
316
334
  enabled: z.boolean().optional(),
335
+ access: z.enum(["subscription", "custom", "api"]).optional(),
317
336
  config: z.json().optional(),
318
337
  });
319
338
  const instanceConfigMapSchema = z.record(z.string(), instanceConfigSchema);
@@ -373,7 +392,12 @@ const appConfigSchema = z.object({
373
392
  /** Anthropic API key for Claude Code billed per token, handed only to
374
393
  * Claude instances; `url` only for a proxy or a test double. Never a
375
394
  * personal-login OAuth token. */
376
- anthropic: z.object({ key: optionalText, url: optionalText }).optional(),
395
+ anthropic: z.object({ key: optionalText, url: optionalText, everyClaudeBot: z.boolean().optional() }).optional(),
396
+ /** OpenAI's own API key, for the `openai` instance only. Codex keeps its
397
+ * own ChatGPT or API login and never reads this. */
398
+ openai: z.object({ key: optionalText }).optional(),
399
+ /** OpenRouter key, for the `openrouter` instance only. */
400
+ openrouter: z.object({ key: optionalText }).optional(),
377
401
  /** Monthly spend limit for the whole workspace, against the cost engines
378
402
  * report to the usage ledger. Enforced only with the `budgets` entitlement. */
379
403
  budgets: z
@@ -412,8 +436,8 @@ const appConfigSchema = z.object({
412
436
  opencodeGo: z.object({ apiKey: optionalText }).optional(),
413
437
  /** Voice settings and the selected voice id. `provider` picks the
414
438
  * engine: "elevenlabs" (default; needs `key`), "fish" (needs its own
415
- * `fishKey`), "system" (the Mac's built-in voices, no key), or
416
- * "xai" (Grok TTS, reusing `xai.key`), or
439
+ * `fishKey`; `fishModel` picks its speech model), "system" (the Mac's
440
+ * built-in voices, no key), "xai" (Grok TTS, reusing `xai.key`), or
417
441
  * "chatterbox" (a local OpenAI-compatible Chatterbox server; `baseUrl`
418
442
  * and `model` are settings, not secrets). Cloud keys stay separate so
419
443
  * switching providers never overwrites or misuses the other key. */
@@ -429,6 +453,7 @@ const appConfigSchema = z.object({
429
453
  .refine((value) => !value || /^https?:\/\//i.test(value), "the Chatterbox server address must start with http:// or https://")
430
454
  .optional(),
431
455
  model: optionalText,
456
+ fishModel: z.enum(FISH_TTS_MODELS).optional(),
432
457
  }).optional(),
433
458
  /** The decision model (server/decider): a fast classifier that picks
434
459
  * things for bots, starting with who answers a room message. `key` is
@@ -638,6 +663,9 @@ export function localVmMode(cfg) {
638
663
  export function localVmMaxInstances(cfg) {
639
664
  return cfg.localVm?.maxInstances ?? DEFAULT_LOCAL_VM_MAX_INSTANCES;
640
665
  }
666
+ export function localVmIdleTimeoutMinutes(cfg) {
667
+ return cfg.localVm?.idleTimeoutMinutes ?? DEFAULT_LOCAL_VM_IDLE_TIMEOUT_MINUTES;
668
+ }
641
669
  /** On by default; only an explicit `false` (the Settings toggle, or a legacy
642
670
  * `skillRecorder: false` carried over at startup) switches it off. */
643
671
  export function skillAuthoringEnabled(cfg) {
@@ -867,6 +895,12 @@ export function loadConfig() {
867
895
  cfg.anthropic.key = process.env.OMB_ANTHROPIC_API_KEY;
868
896
  if (process.env.OMB_ANTHROPIC_API_URL !== undefined)
869
897
  cfg.anthropic.url = process.env.OMB_ANTHROPIC_API_URL;
898
+ cfg.openai = { ...cfg.openai };
899
+ if (process.env.OMB_OPENAI_API_KEY !== undefined)
900
+ cfg.openai.key = process.env.OMB_OPENAI_API_KEY;
901
+ cfg.openrouter = { ...cfg.openrouter };
902
+ if (process.env.OMB_OPENROUTER_API_KEY !== undefined)
903
+ cfg.openrouter.key = process.env.OMB_OPENROUTER_API_KEY;
870
904
  cfg.openaiCompat = { ...cfg.openaiCompat };
871
905
  if (process.env.OPENAI_COMPAT_API_KEY !== undefined)
872
906
  cfg.openaiCompat.key = process.env.OPENAI_COMPAT_API_KEY;
@@ -929,6 +963,8 @@ export function syncCredentialEnv(patch) {
929
963
  [patch.mistral?.key, "MISTRAL_API_KEY"],
930
964
  [patch.anthropic?.key, "OMB_ANTHROPIC_API_KEY"],
931
965
  [patch.openaiCompat?.key, "OPENAI_COMPAT_API_KEY"],
966
+ [patch.openai?.key, "OMB_OPENAI_API_KEY"],
967
+ [patch.openrouter?.key, "OMB_OPENROUTER_API_KEY"],
932
968
  [patch.composio?.apiKey, "COMPOSIO_API_KEY"],
933
969
  [patch.box?.token, "BOX_TOKEN"],
934
970
  [patch.opencodeGo?.apiKey, "OPENCODE_API_KEY"],
@@ -977,6 +1013,8 @@ export const WORKSPACE_CREDENTIAL_ENV = [
977
1013
  "OMB_HOSTED_MODELS",
978
1014
  "OPENAI_COMPAT_API_KEY",
979
1015
  "OPENAI_COMPAT_URL",
1016
+ "OMB_OPENAI_API_KEY",
1017
+ "OMB_OPENROUTER_API_KEY",
980
1018
  "BOX_TOKEN",
981
1019
  "OPENCODE_API_KEY",
982
1020
  "OMB_TTS_KEY",
@@ -1073,7 +1111,7 @@ export function saveConfig(patch, options = {}) {
1073
1111
  const storedProfiles = storedBrowserProfilesSchema.safeParse(disk.browserProfiles);
1074
1112
  if (storedProfiles.success)
1075
1113
  disk.browserProfiles = storedProfiles.data;
1076
- for (const key of ["xai", "anthropic", "mistral", "openaiCompat", "composio", "box", "opencodeGo", "tts", "decider", "imageGen", "profile", "rooms", "threads", "context", "memory", "localVm", "features", "cloudOverflow", "budgets", "billing", "decisions", "onboarding", "browserEngine", "newBots"]) {
1114
+ for (const key of ["xai", "anthropic", "mistral", "openai", "openrouter", "openaiCompat", "composio", "box", "opencodeGo", "tts", "decider", "imageGen", "profile", "rooms", "threads", "context", "memory", "localVm", "features", "cloudOverflow", "budgets", "billing", "decisions", "onboarding", "browserEngine", "newBots"]) {
1077
1115
  const section = checkedPatch[key];
1078
1116
  if (!section)
1079
1117
  continue;
@@ -1259,13 +1297,63 @@ export function persistableInstanceConfigs(cfg) {
1259
1297
  }
1260
1298
  return map;
1261
1299
  }
1300
+ /** Claude Code variables that choose where a turn goes and how it signs in.
1301
+ * An instance that sets any of them (a router, a gateway, a cloud platform)
1302
+ * owns that routing and credential pair outright. */
1303
+ const CLAUDE_ROUTING_ENV = [
1304
+ "ANTHROPIC_BASE_URL",
1305
+ "ANTHROPIC_AUTH_TOKEN",
1306
+ "ANTHROPIC_API_KEY",
1307
+ "ANTHROPIC_CUSTOM_HEADERS",
1308
+ "CLAUDE_CODE_USE_BEDROCK",
1309
+ "CLAUDE_CODE_USE_VERTEX",
1310
+ "CLAUDE_CODE_USE_FOUNDRY",
1311
+ ];
1312
+ /** Whether a Claude instance brings its own endpoint or credential. The
1313
+ * workspace Anthropic key must never reach it: the key would be sent as
1314
+ * x-api-key to that instance's host (a third-party router), or would
1315
+ * silently re-route its turns to the workspace URL. */
1316
+ export function claudeInstanceOwnsRouting(environment) {
1317
+ return CLAUDE_ROUTING_ENV.some((key) => typeof environment?.[key] === "string" && environment[key] !== "");
1318
+ }
1319
+ const sameUrl = (a, b) => a.trim().replace(/\/+$/u, "") === b.trim().replace(/\/+$/u, "");
1320
+ /** Whether an instance brings its own endpoint or credential, so the
1321
+ * workspace key for its driver must never reach it: that key would be sent
1322
+ * to the instance's own host (a third-party router or proxy). The same rule
1323
+ * as a Claude router instance, for the API-key engines. An instance on the
1324
+ * workspace's own endpoint with nothing of its own still gets the key. */
1325
+ export function instanceOwnsRouting(cfg, entry, routingDefaults) {
1326
+ const config = typeof entry.config === "object" && entry.config !== null && !Array.isArray(entry.config)
1327
+ ? entry.config
1328
+ : {};
1329
+ const own = (value) => typeof value === "string" && value.trim() !== "";
1330
+ const ownUrl = (...workspace) => own(config.url)
1331
+ && !workspace.some((url) => url !== undefined && sameUrl(config.url, url));
1332
+ const compatKeyEnv = routingDefaults?.apiKeyEnv ?? "OPENAI_COMPAT_API_KEY";
1333
+ switch (entry.driver) {
1334
+ case "claudeAgent":
1335
+ return claudeInstanceOwnsRouting(entry.environment);
1336
+ case "openai-compat":
1337
+ return own(config.key) || own(entry.environment?.OPENAI_COMPAT_API_KEY)
1338
+ || own(entry.environment?.[compatKeyEnv])
1339
+ || (own(config.apiKeyEnv) && config.apiKeyEnv !== compatKeyEnv)
1340
+ || ownUrl(routingDefaults?.url || cfg.openaiCompat?.url || process.env.OPENAI_COMPAT_URL || "https://openrouter.ai/api/v1");
1341
+ case "mistral":
1342
+ return own(entry.environment?.MISTRAL_API_KEY) || ownUrl("https://api.mistral.ai/v1");
1343
+ case "grok":
1344
+ return own(entry.environment?.XAI_API_KEY) || (own(config.apiKeyEnv) && config.apiKeyEnv !== "XAI_API_KEY")
1345
+ || ownUrl("https://api.x.ai/v1", cfg.xai?.url);
1346
+ default:
1347
+ return false;
1348
+ }
1349
+ }
1262
1350
  /** The credential env instanceConfigs() injects for one driver at runtime.
1263
1351
  * Each secret goes only to the driver that actually reads it: the API-key
1264
1352
  * Grok driver reads XAI_API_KEY, the Computer driver reads BOX_TOKEN, and
1265
1353
  * OpenCode reads OPENCODE_API_KEY. Every other engine brings its own
1266
1354
  * login, so handing it a key it never uses would only put that key in the
1267
1355
  * environment of an unrelated child process. */
1268
- function injectedEnvironment(cfg, driver) {
1356
+ function injectedEnvironment(cfg, instanceId, driver) {
1269
1357
  const environment = new Map();
1270
1358
  if (driver === "mistral" && cfg.mistral?.key)
1271
1359
  environment.set("MISTRAL_API_KEY", cfg.mistral.key);
@@ -1274,14 +1362,33 @@ function injectedEnvironment(cfg, driver) {
1274
1362
  // The workspace Anthropic key reaches Claude Code as the variable it
1275
1363
  // reads, carried in the instance environment so the driver can tell a
1276
1364
  // deliberate workspace key from one riding along in the parent's env.
1277
- if (driver === "claudeAgent" && cfg.anthropic?.key)
1365
+ // By default it runs only the `claudeApi` instance, so signed-in Claude
1366
+ // bots stay on their plan; `everyClaudeBot` (the pre-split behaviour, and
1367
+ // what a hosted workspace seeded with only a key relies on) runs every
1368
+ // other Claude instance on it too.
1369
+ const anthropicHere = driver === "claudeAgent" && Boolean(cfg.anthropic?.key)
1370
+ && (cfg.anthropic?.everyClaudeBot === false ? instanceId === CLAUDE_API_INSTANCE : instanceId !== CLAUDE_API_INSTANCE);
1371
+ if (anthropicHere)
1278
1372
  environment.set("ANTHROPIC_API_KEY", cfg.anthropic.key);
1279
- if (driver === "claudeAgent" && cfg.anthropic?.key && cfg.anthropic.url)
1373
+ if (anthropicHere && cfg.anthropic.url)
1280
1374
  environment.set("ANTHROPIC_BASE_URL", cfg.anthropic.url);
1281
- if (driver === "openai-compat" && cfg.openaiCompat?.key)
1282
- environment.set("OPENAI_COMPAT_API_KEY", cfg.openaiCompat.key);
1283
- if (driver === "openai-compat" && cfg.openaiCompat?.url)
1284
- environment.set("OPENAI_COMPAT_URL", cfg.openaiCompat.url);
1375
+ // OpenAI and OpenRouter have their own instances and keys; the workspace
1376
+ // OpenAI-compatible key and URL belong to every other openai-compat
1377
+ // instance ("Other" in Settings).
1378
+ if (driver === "openai-compat" && instanceId === OPENAI_API_INSTANCE) {
1379
+ if (cfg.openai?.key)
1380
+ environment.set("OMB_OPENAI_API_KEY", cfg.openai.key);
1381
+ }
1382
+ else if (driver === "openai-compat" && instanceId === OPENROUTER_API_INSTANCE) {
1383
+ if (cfg.openrouter?.key)
1384
+ environment.set("OMB_OPENROUTER_API_KEY", cfg.openrouter.key);
1385
+ }
1386
+ else {
1387
+ if (driver === "openai-compat" && cfg.openaiCompat?.key)
1388
+ environment.set("OPENAI_COMPAT_API_KEY", cfg.openaiCompat.key);
1389
+ if (driver === "openai-compat" && cfg.openaiCompat?.url)
1390
+ environment.set("OPENAI_COMPAT_URL", cfg.openaiCompat.url);
1391
+ }
1285
1392
  // driverKind "boxAgent" and env BOX_TOKEN keep their historical names.
1286
1393
  // Only the person's own token: without one the driver itself falls back
1287
1394
  // to Cloud Pro's included token, which never enters an environment map.
@@ -1291,6 +1398,28 @@ function injectedEnvironment(cfg, driver) {
1291
1398
  environment.set("OPENCODE_API_KEY", cfg.opencodeGo.apiKey);
1292
1399
  return environment;
1293
1400
  }
1401
+ /** One instance per pasted provider key, each running only on its own key.
1402
+ * They sit in the picker's "API keys" group and stay hidden until their key
1403
+ * is saved (unavailable without one). */
1404
+ export const OPENAI_API_INSTANCE = "openai";
1405
+ export const OPENROUTER_API_INSTANCE = "openrouter";
1406
+ export const XAI_API_INSTANCE = "xaiApi";
1407
+ export const CLAUDE_API_INSTANCE = "claudeApi";
1408
+ const API_KEY_FLEET = {
1409
+ [OPENAI_API_INSTANCE]: {
1410
+ driver: "openai-compat", displayName: "OpenAI", access: "api", icon: { kind: "preset", preset: "openai" },
1411
+ config: { url: "https://api.openai.com/v1", apiKeyEnv: "OMB_OPENAI_API_KEY", catalog: "openai" },
1412
+ },
1413
+ [CLAUDE_API_INSTANCE]: {
1414
+ driver: "claudeAgent", displayName: "Claude (API key)", access: "api", icon: { kind: "preset", preset: "anthropic" },
1415
+ config: { requireApiKey: true },
1416
+ },
1417
+ [XAI_API_INSTANCE]: { driver: "grok", displayName: "xAI", access: "api", icon: { kind: "preset", preset: "xai" } },
1418
+ [OPENROUTER_API_INSTANCE]: {
1419
+ driver: "openai-compat", displayName: "OpenRouter", access: "api", icon: { kind: "preset", preset: "openrouter" },
1420
+ config: { url: "https://openrouter.ai/api/v1", apiKeyEnv: "OMB_OPENROUTER_API_KEY" },
1421
+ },
1422
+ };
1294
1423
  // Default fleet: one instance per built-in driver (upstream
1295
1424
  // defaultInstanceIdForDriver — instanceId defaults to the driver kind).
1296
1425
  // Config-file keys are injected as per-instance environment so drivers
@@ -1318,11 +1447,13 @@ export function instanceConfigs(cfg) {
1318
1447
  cursor: { driver: "cursorAgent" },
1319
1448
  claude: { driver: "claudeAgent" },
1320
1449
  codex: { driver: "codex" },
1450
+ chatgpt: { driver: "codex", displayName: "ChatGPT plan", config: { authMode: "chatgpt-plan" } },
1321
1451
  antigravity: { driver: "antigravityAgent" },
1322
1452
  opencodeGo: { driver: "opencodeGo" },
1323
1453
  computer: { driver: "boxAgent" },
1324
1454
  openaiCompat: { driver: "openai-compat" },
1325
1455
  mistral: { driver: "mistral" },
1456
+ ...API_KEY_FLEET,
1326
1457
  qwen: { driver: "qwenAgent" },
1327
1458
  hermes: { driver: "hermesAgent" },
1328
1459
  pi: { driver: "piAgent" },
@@ -1336,9 +1467,11 @@ export function instanceConfigs(cfg) {
1336
1467
  // never see. Custom-only engines stay in CUSTOM_ONLY so a one-off test map
1337
1468
  // is not expanded, matching the claude/grok/codex product-fleet probe.
1338
1469
  const PRODUCT_FLEET_ADDITIONS = {
1470
+ chatgpt: { driver: "codex", displayName: "ChatGPT plan", config: { authMode: "chatgpt-plan" } },
1339
1471
  cursor: { driver: "cursorAgent" },
1340
1472
  openaiCompat: { driver: "openai-compat" },
1341
1473
  mistral: { driver: "mistral" },
1474
+ ...API_KEY_FLEET,
1342
1475
  ...CUSTOM_ONLY,
1343
1476
  };
1344
1477
  const configured = cfg.instances && Object.keys(cfg.instances).length ? cfg.instances : null;
@@ -1352,6 +1485,22 @@ export function instanceConfigs(cfg) {
1352
1485
  map[id] = { ...entry };
1353
1486
  }
1354
1487
  }
1488
+ // A saved `instances` map (any engine edit persists the whole fleet) keeps
1489
+ // these entries without their built-in routing. Re-apply it on every load
1490
+ // so the OpenAI and OpenRouter instances can never fall back to the shared
1491
+ // OpenAI-compatible key and URL; saved fields still win.
1492
+ for (const [id, builtIn] of Object.entries(API_KEY_FLEET)) {
1493
+ const saved = map[id];
1494
+ if (!saved || saved.driver !== builtIn.driver)
1495
+ continue;
1496
+ const savedConfig = typeof saved.config === "object" && saved.config !== null && !Array.isArray(saved.config) ? saved.config : {};
1497
+ map[id] = { ...builtIn, ...saved, config: { ...builtIn.config, ...savedConfig } };
1498
+ }
1499
+ if (map.chatgpt?.driver === "codex" && !map.chatgpt.config?.cli) {
1500
+ const cli = map.codex?.config?.cli;
1501
+ if (typeof cli === "string" && cli)
1502
+ map.chatgpt = { ...map.chatgpt, config: { ...map.chatgpt.config, cli } };
1503
+ }
1355
1504
  for (const [id, sourceEntry] of Object.entries(map)) {
1356
1505
  // instanceConfigs() builds a transient runtime map. Never mutate the
1357
1506
  // caller's persisted entries while injecting workspace defaults: doing so
@@ -1359,14 +1508,25 @@ export function instanceConfigs(cfg) {
1359
1508
  const entry = { ...sourceEntry };
1360
1509
  map[id] = entry;
1361
1510
  const environment = { ...entry.environment };
1362
- for (const [key, value] of injectedEnvironment(cfg, entry.driver))
1363
- environment[key] = value;
1511
+ // Main's rule: an instance that brought its own key or host gets no
1512
+ // workspace credential. Built-in per-provider routing is a default,
1513
+ // not an override, but saved custom routing on those IDs still owns its
1514
+ // credential rather than receiving the workspace's provider key.
1515
+ const builtIn = API_KEY_FLEET[id];
1516
+ const routingDefaults = builtIn?.driver === entry.driver
1517
+ ? builtIn.config
1518
+ : undefined;
1519
+ const ownsRouting = instanceOwnsRouting(cfg, entry, routingDefaults);
1520
+ if (!ownsRouting) {
1521
+ for (const [key, value] of injectedEnvironment(cfg, id, entry.driver))
1522
+ environment[key] = value;
1523
+ }
1364
1524
  entry.environment = environment;
1365
1525
  // The driver URL is configuration, not a credential. Environment is
1366
1526
  // intentionally not consulted by ProviderRegistry when it decodes a
1367
1527
  // driver's config, so carry the workspace default into the transient
1368
1528
  // instance map while preserving a per-instance override.
1369
- if (entry.driver === "openai-compat" && cfg.openaiCompat) {
1529
+ if (entry.driver === "openai-compat" && cfg.openaiCompat && id !== OPENAI_API_INSTANCE && id !== OPENROUTER_API_INSTANCE) {
1370
1530
  const defaults = {};
1371
1531
  if (cfg.openaiCompat.url)
1372
1532
  defaults.url = cfg.openaiCompat.url;
@@ -408,7 +408,7 @@ export function cuaExecArgs(args, options = {}) {
408
408
  ...args,
409
409
  ];
410
410
  }
411
- export async function containerComputerStatus(runner = sh, platform = process.platform, target = SHARED_LOCAL_VM_TARGET) {
411
+ export async function containerComputerStatus(runner = sh, platform = process.platform, target = SHARED_LOCAL_VM_TARGET, options = {}) {
412
412
  const status = emptyStatus(platform, target);
413
413
  const runtimeStatus = await containerRuntimeStatus(runner, platform);
414
414
  status.available = runtimeStatus.available;
@@ -475,7 +475,8 @@ export async function containerComputerStatus(runner = sh, platform = process.pl
475
475
  catch {
476
476
  // No container with this name.
477
477
  }
478
- const canProbe = status.container === "running" &&
478
+ const canProbe = options.probeDesktop !== false &&
479
+ status.container === "running" &&
479
480
  status.imageMatches &&
480
481
  status.managed &&
481
482
  status.network === "loopback" &&
@@ -0,0 +1,26 @@
1
+ import { desktopViewerUrl } from "./routes/desktop-viewer.js";
2
+ export const viewerTargetId = (target) => `local/${target.key.replace(":", "-")}`;
3
+ /** Native owner windows keep the existing direct viewer and cookie isolation. */
4
+ export function localVmViewerStatus(status, auth) {
5
+ return auth.kind === "loopback" ? status : {
6
+ ...status,
7
+ viewer_url: status.viewer_url ? desktopViewerUrl(viewerTargetId({ key: status.target_key })) : "",
8
+ };
9
+ }
10
+ export function localDesktopTarget(target, deps) {
11
+ return {
12
+ key: target.key,
13
+ async resolve() {
14
+ const status = await deps.status(target);
15
+ if (!status.managed || !status.imageMatches || status.network !== "loopback" || status.container !== "running"
16
+ || !status.viewer_url) {
17
+ throw Object.assign(new Error("Local VM unavailable"), { status: 409 });
18
+ }
19
+ return {
20
+ port: status.viewer_port ?? 0,
21
+ password: new URLSearchParams(new URL(status.viewer_url).hash.slice(1)).get("password"),
22
+ touch: () => deps.touch(target),
23
+ };
24
+ },
25
+ };
26
+ }