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.
- package/dist/assets/DesktopViewer-Do6lBa4C.js +5 -0
- package/dist/assets/{abnfDiagram-O67JEVCF-yP-w8pRq.js → abnfDiagram-O67JEVCF-Cd3X7Hxc.js} +1 -1
- package/dist/assets/{arc-DsivfZEh.js → arc-DIYc7Ymx.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-CILw7lzT.js → architectureDiagram-NJMV4G6O-DBmRGVyP.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-DEQUV3HF.js → blockDiagram-BEXU5L5S-CPFePSEE.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-lSXCZoKA.js → c4Diagram-YGBWAQC7-DD_DO1qy.js} +1 -1
- package/dist/assets/channel-Dtk5Mtic.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-QCD5bgWP.js → chunk-2Q5K7J3B-BNEeNSjX.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-C5-iZJuL.js → chunk-3FUC2YCW-BbinLaqH.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-cxsawMpY.js → chunk-5VM5RSS4-BBgYrgDR.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-DV8tV8H7.js → chunk-GTNCS2PH-ezXj-kA-.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-CJFZyfbW.js → chunk-GWA4HPMP-9FxZ3diV.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BigbMXco.js → chunk-JWPE2WC7-CO-40JpI.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-oVxTfpDy.js → chunk-NETBCI7D-BEhjslYE.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-CH4BiroW.js → chunk-WEXAMYUT-CNeq4n7_.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-KEDa6gax.js → chunk-XXDRQBXY-OsCY7KSi.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BiPjfqVv.js → classDiagram-v2-NBCMYWYE-CloRUEtk.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-Caxew3OY.js → cose-bilkent-JH36ORCC-D1BGZhp4.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-B_Pui369.js → cynefin-OW5HDTMX-CUf4T2CN.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-X64ydIhP.js → cynefinDiagram-VND7K2PF-DPEZrFGA.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-TqYCI1E1.js → dagre-6A5THRUB-bDe6GIv5.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-TeOt3rAQ.js → diagram-22UHCM2B-CdcUQz55.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-D70BLhDA.js → diagram-3UASUU5V-BZpqGg7c.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-BbQDLwUC.js → diagram-ATOU4E4O-DvGM6ugp.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-ChNJZ1u-.js → diagram-CDSNMT55-DSTmMm0s.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-DMBl2APa.js → diagram-MLGK6HIB-15cucnMn.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-CEqDchOp.js → diagram-MPIPVDR6-tMbuaE6n.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-BVDKRp_-.js → ebnfDiagram-ZINNZB2B-BYxYzjHI.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-CRkofO9B.js → elk-276RUBZZ-v6bj852W.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-DruOMh5e.js → erDiagram-OPXOYQCR-5lBBlnd2.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-8GMQeB7t.js → flowDiagram-KWPJA3E3-CZyl1zIj.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-ORE9jcsM.js → ganttDiagram-FUAMR5RP-BiAI-ltZ.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-BmsedsWh.js → gitGraphDiagram-X574FWY7-BAZISkj3.js} +1 -1
- package/dist/assets/index-Cdmjrl28.css +1 -0
- package/dist/assets/{index-DKNEM8Q6.js → index-DGBaEXrM.js} +1 -1
- package/dist/assets/index-DR7YPMkW.js +597 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-E_p7AooJ.js → infoDiagram-VRGFBTTK-B6ooOy-Z.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-Y-B2mKda.js → ishikawaDiagram-OU5B5YK6-BZ9ksi-B.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-eA8RELcK.js → journeyDiagram-ZHPQQLJL-DWqlzIEe.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-DqIL2e8e.js → kanban-definition-PNTS6WVX-JX6piDwL.js} +1 -1
- package/dist/assets/{linear-yC_29R6e.js → linear-CmOWzX0P.js} +1 -1
- package/dist/assets/{mermaid.core-VhEMkSND.js → mermaid.core-CF1zwoQ-.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-oJ7kyQZy.js → mindmap-definition-NLK3R4M7-CGh2K4fW.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-BmfYcY0O.js → pegDiagram-GJSIUBJH-CDwYN0s7.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-DKvgP7w9.js → pieDiagram-5QR66LMP-1rfjJocB.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-BEKL3W0L.js → quadrantDiagram-O4NWA36T-tKPvPiok.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-BEsiqR5s.js → railroadDiagram-XR7U4H2S-BHLm5vXR.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-DWBcWvAP.js → requirementDiagram-PLB6GJNP-CB45STp7.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-rpZ2_O4s.js → sankeyDiagram-IPEJSGJF-55QA_huC.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-B99HQdJR.js → sequenceDiagram-PO4LG4MO-BckFmQHn.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-BjN0qT5T.js → sizeCapture-INFHLROL-BFCOJF11.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-BbsVgGsC.js → stateDiagram-v2-GCMORJYK-CZvmiVVN.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-DG3gKkeR.js → swimlanes-2SLR337P-BpDUCEKy.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-M0GYW9Y3.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-BTHOdtF_.js → timeline-definition-EJHVYXUP-BpxHq6IK.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-acLSLFwc.js → usecaseDiagram-POWQR4AR-Cj66vm02.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-C5JzNX9G.js → vennDiagram-UO4OBE2U-C9nRkWE_.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-DL1-PrR_.js → wardleyDiagram-VNRHLVJA-D-me_nWj.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-Bvr1477p.js → xychartDiagram-PMCCYNJV-Dc_fL4mk.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/novnc-NOTICE.txt +118 -0
- package/dist-server/cloud-home-start.js +112 -14
- package/dist-server/container-mcp.js +195 -15
- package/dist-server/drivers/agents-proxy.js +5 -4
- package/dist-server/enterprise/server/index.js +5 -1
- package/dist-server/index.js +9300 -4155
- package/dist-server/local-computer-proxy.js +128 -7
- package/dist-server/mcp-gate.js +11 -5
- package/dist-server/model-catalog/models-dev.snapshot.json +7299 -0
- package/dist-server/openmausbot.js +3910 -1182
- package/dist-server/pair-cli.js +3910 -1182
- package/dist-server/server/boat.js +2 -2
- package/dist-server/server/browser-engine.js +10 -1
- package/dist-server/server/browser-live.js +46 -2
- package/dist-server/server/browser-runtime.js +74 -8
- package/dist-server/server/browser-tool-shape.js +6 -4
- package/dist-server/server/chief-of-staff.js +1 -1
- package/dist-server/server/cloud-home-start.js +118 -26
- package/dist-server/server/cloud-home.js +39 -3
- package/dist-server/server/cloud-lending.js +74 -10
- package/dist-server/server/cloud-owner.js +196 -0
- package/dist-server/server/cloud-secrets-boot.js +6 -0
- package/dist-server/server/cloud-secrets.js +65 -0
- package/dist-server/server/config.js +174 -14
- package/dist-server/server/container-computer.js +3 -2
- package/dist-server/server/desktop-viewer-targets.js +26 -0
- package/dist-server/server/drivers/acp/core.js +167 -21
- package/dist-server/server/drivers/acp/opencode-go.js +614 -85
- package/dist-server/server/drivers/agents-catalog.js +6 -6
- package/dist-server/server/drivers/antigravity-acp.js +69 -11
- package/dist-server/server/drivers/antigravity-temp.js +360 -0
- package/dist-server/server/drivers/antigravity.js +21 -0
- package/dist-server/server/drivers/chatgpt-plan-auth.js +481 -0
- package/dist-server/server/drivers/claude.js +312 -34
- package/dist-server/server/drivers/codex.js +228 -18
- package/dist-server/server/drivers/grok.js +2 -2
- package/dist-server/server/drivers/mistral.js +4 -3
- package/dist-server/server/drivers/openai-chat-protocol.js +4 -3
- package/dist-server/server/drivers/openai-chat.js +41 -3
- package/dist-server/server/drivers/openai-compat.js +73 -23
- package/dist-server/server/engine-install.js +46 -12
- package/dist-server/server/env-path.js +15 -3
- package/dist-server/server/harness/registry.js +2 -2
- package/dist-server/server/index.js +1338 -184
- package/dist-server/server/lending-memory.js +479 -0
- package/dist-server/server/local-vm-idle.js +24 -6
- package/dist-server/server/managed-desktop.js +3 -0
- package/dist-server/server/mcp-bridge.js +5 -3
- package/dist-server/server/mcp-http.js +6 -3
- package/dist-server/server/mcp-oauth-discovery.js +133 -0
- package/dist-server/server/mcp-oauth-store.js +70 -0
- package/dist-server/server/mcp-oauth.js +550 -0
- package/dist-server/server/mcp-probe.js +14 -1
- package/dist-server/server/mcp-registry.js +43 -0
- package/dist-server/server/memory-upkeep.js +94 -84
- package/dist-server/server/model-catalog/catalog.js +374 -0
- package/dist-server/server/model-catalog/presets.js +176 -0
- package/dist-server/server/model-catalog/trim.js +219 -0
- package/dist-server/server/provider-key-check.js +4 -2
- package/dist-server/server/recent-work.js +1 -1
- package/dist-server/server/request-auth.js +23 -0
- package/dist-server/server/routes/antigravity-leftovers.js +45 -0
- package/dist-server/server/routes/bot-memory.js +31 -6
- package/dist-server/server/routes/desktop-viewer.js +194 -0
- package/dist-server/server/routine-requests.js +1 -1
- package/dist-server/server/routines.js +1 -1
- package/dist-server/server/sessions.js +56 -9
- package/dist-server/server/skills.js +25 -10
- package/dist-server/server/store.js +62 -122
- package/dist-server/server/thread-events.js +2 -0
- package/dist-server/server/thread-starters.js +33 -0
- package/dist-server/server/thread-title.js +46 -0
- package/dist-server/server/tts/fish.js +4 -5
- package/dist-server/server/tts/index.js +5 -3
- package/dist-server/server/vps-computer.js +79 -12
- package/dist-server/server/webhooks.js +18 -1
- package/dist-server/server/workspace-backup-policy.js +3 -0
- package/dist-server/server/workspace-backup.js +3 -0
- package/dist-server/server/workspace.js +65 -15
- package/dist-server/shared/workspace-backup-client.js +1 -0
- package/dist-server/src/lib/citations.js +177 -0
- package/dist-server/src/lib/composer-attachments.js +7 -1
- package/dist-server/vps-container-mcp.js +196 -15
- package/dist-server/workspace-backup.worker.js +73 -9
- package/enterprise/server/index.js +5 -1
- package/package.json +1 -1
- package/dist/assets/channel-DrfGMu7X.js +0 -1
- package/dist/assets/index-BlnCEcU6.js +0 -588
- package/dist/assets/index-DVxjOvID.css +0 -1
- 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
|
|
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
|
-
|
|
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 (
|
|
1373
|
+
if (anthropicHere && cfg.anthropic.url)
|
|
1280
1374
|
environment.set("ANTHROPIC_BASE_URL", cfg.anthropic.url);
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
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
|
-
|
|
1363
|
-
|
|
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 =
|
|
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
|
+
}
|