openmausbot 0.1.90 → 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-DUyXd0kL.js → abnfDiagram-O67JEVCF-Cd3X7Hxc.js} +1 -1
- package/dist/assets/{arc-q73qM-At.js → arc-DIYc7Ymx.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-D33O3Wur.js → architectureDiagram-NJMV4G6O-DBmRGVyP.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-DLsJeoXC.js → blockDiagram-BEXU5L5S-CPFePSEE.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-DnaljZnu.js → c4Diagram-YGBWAQC7-DD_DO1qy.js} +1 -1
- package/dist/assets/channel-Dtk5Mtic.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-Da0h2i79.js → chunk-2Q5K7J3B-BNEeNSjX.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-CfzHi54f.js → chunk-3FUC2YCW-BbinLaqH.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-LjE4d9Jf.js → chunk-5VM5RSS4-BBgYrgDR.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-DY-UGmvc.js → chunk-GTNCS2PH-ezXj-kA-.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-BqeGhyhP.js → chunk-GWA4HPMP-9FxZ3diV.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BYa9EBAg.js → chunk-JWPE2WC7-CO-40JpI.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-vTrDtWyb.js → chunk-NETBCI7D-BEhjslYE.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-ic5tXadM.js → chunk-WEXAMYUT-CNeq4n7_.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-DvRCY-c6.js → chunk-XXDRQBXY-OsCY7KSi.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-DVbmgz_L.js → classDiagram-v2-NBCMYWYE-CloRUEtk.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-vdUs2n57.js → cose-bilkent-JH36ORCC-D1BGZhp4.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-BvLGlV3U.js → cynefin-OW5HDTMX-CUf4T2CN.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-C4GcJQDt.js → cynefinDiagram-VND7K2PF-DPEZrFGA.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-BMSaHpt_.js → dagre-6A5THRUB-bDe6GIv5.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-D5b9UBhC.js → diagram-22UHCM2B-CdcUQz55.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-CIKLlB7Y.js → diagram-3UASUU5V-BZpqGg7c.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-BzXb-7k7.js → diagram-ATOU4E4O-DvGM6ugp.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-HsTvJ7uT.js → diagram-CDSNMT55-DSTmMm0s.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-Db9ySNbQ.js → diagram-MLGK6HIB-15cucnMn.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-D6BKbpqY.js → diagram-MPIPVDR6-tMbuaE6n.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-Bq067L0t.js → ebnfDiagram-ZINNZB2B-BYxYzjHI.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-DhaD7fNJ.js → elk-276RUBZZ-v6bj852W.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-fwIhfz36.js → erDiagram-OPXOYQCR-5lBBlnd2.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-DygIyu0V.js → flowDiagram-KWPJA3E3-CZyl1zIj.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-DPYfSO96.js → ganttDiagram-FUAMR5RP-BiAI-ltZ.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-DhkHeCle.js → gitGraphDiagram-X574FWY7-BAZISkj3.js} +1 -1
- package/dist/assets/index-Cdmjrl28.css +1 -0
- package/dist/assets/{index-G1PHKGyh.js → index-DGBaEXrM.js} +1 -1
- package/dist/assets/index-DR7YPMkW.js +597 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-DstBLxME.js → infoDiagram-VRGFBTTK-B6ooOy-Z.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-CrIsGgch.js → ishikawaDiagram-OU5B5YK6-BZ9ksi-B.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-CYypGDbB.js → journeyDiagram-ZHPQQLJL-DWqlzIEe.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-DGjPt8E_.js → kanban-definition-PNTS6WVX-JX6piDwL.js} +1 -1
- package/dist/assets/{linear-Dwc3Hrru.js → linear-CmOWzX0P.js} +1 -1
- package/dist/assets/{mermaid.core-Dc4OePBL.js → mermaid.core-CF1zwoQ-.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-DV-vepj-.js → mindmap-definition-NLK3R4M7-CGh2K4fW.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-lx5Kj-n5.js → pegDiagram-GJSIUBJH-CDwYN0s7.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-Bq7Vmm15.js → pieDiagram-5QR66LMP-1rfjJocB.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-DeqeqOBj.js → quadrantDiagram-O4NWA36T-tKPvPiok.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-CXwRQzSC.js → railroadDiagram-XR7U4H2S-BHLm5vXR.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-8tAeoB88.js → requirementDiagram-PLB6GJNP-CB45STp7.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-BG5zjGWU.js → sankeyDiagram-IPEJSGJF-55QA_huC.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-DKF3iLcw.js → sequenceDiagram-PO4LG4MO-BckFmQHn.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-B-y1qUgT.js → sizeCapture-INFHLROL-BFCOJF11.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-MkzPgxAc.js → stateDiagram-v2-GCMORJYK-CZvmiVVN.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-DnbaOEwH.js → swimlanes-2SLR337P-BpDUCEKy.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-M0GYW9Y3.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-Bb4xG_Bt.js → timeline-definition-EJHVYXUP-BpxHq6IK.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-JgkR9pFf.js → usecaseDiagram-POWQR4AR-Cj66vm02.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-CIPIrAYe.js → vennDiagram-UO4OBE2U-C9nRkWE_.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-BjCLuUvJ.js → wardleyDiagram-VNRHLVJA-D-me_nWj.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-DKgiaYxT.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 +128 -15
- package/dist-server/container-mcp.js +277 -45
- package/dist-server/drivers/agents-proxy.js +16 -4
- package/dist-server/enterprise/server/index.js +5 -1
- package/dist-server/index.js +46703 -39955
- 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 +4099 -1217
- package/dist-server/pair-cli.js +4099 -1217
- package/dist-server/server/boat.js +67 -22
- package/dist-server/server/bot-overview.js +7 -4
- 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 +136 -26
- package/dist-server/server/cloud-home.js +64 -5
- package/dist-server/server/cloud-lending.js +167 -0
- package/dist-server/server/cloud-move-http.js +268 -0
- package/dist-server/server/cloud-move.js +418 -0
- 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 +215 -17
- package/dist-server/server/container-computer.js +3 -2
- package/dist-server/server/decider/index.js +238 -0
- package/dist-server/server/decider/jev.js +183 -0
- package/dist-server/server/decider/log.js +62 -0
- package/dist-server/server/decider/relay.js +52 -0
- package/dist-server/server/decider/room-routing.js +88 -0
- package/dist-server/server/decider/types.js +5 -0
- 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 +22 -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/boatagent.js +20 -12
- package/dist-server/server/drivers/chatgpt-plan-auth.js +481 -0
- package/dist-server/server/drivers/claude.js +313 -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/group-goal-run.js +6 -4
- package/dist-server/server/harness/registry.js +2 -2
- package/dist-server/server/harness-capabilities.js +2 -0
- package/dist-server/server/included-services.js +70 -0
- package/dist-server/server/index.js +1753 -263
- 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/model-context-window.js +4 -4
- package/dist-server/server/package-export.js +17 -10
- package/dist-server/server/package-import.js +6 -3
- package/dist-server/server/package-parts.js +3 -1
- 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 +25 -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/decider.js +19 -0
- 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/shared-computers.js +116 -30
- package/dist-server/server/skills.js +25 -10
- package/dist-server/server/store.js +79 -124
- package/dist-server/server/surface.js +13 -0
- package/dist-server/server/system-prompt.js +11 -0
- package/dist-server/server/team-backup.js +6 -2
- package/dist-server/server/team-manifest.js +11 -3
- 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/elevenlabs.js +11 -6
- package/dist-server/server/tts/fish.js +4 -5
- package/dist-server/server/tts/index.js +20 -11
- 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 +25 -3
- package/dist-server/server/workspace-backup.js +28 -8
- package/dist-server/server/workspace.js +65 -15
- package/dist-server/shared/cloud-home.js +4 -0
- package/dist-server/shared/package-format.js +30 -4
- package/dist-server/shared/team-backup.js +20 -3
- 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 +278 -45
- package/dist-server/workspace-backup.worker.js +184 -38
- package/enterprise/server/index.js +5 -1
- package/package.json +1 -1
- package/dist/assets/channel-BCuyHjFV.js +0 -1
- package/dist/assets/index-CRvo-DS2.css +0 -1
- package/dist/assets/index-CWYGlA63.js +0 -588
- package/dist/assets/swimlanesDiagram-TC7HE7FX-BZQ07DGE.js +0 -8
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// The OMB Cloud home's secrets, and how the server gets them
|
|
2
|
+
// (docs/cloud-pro.md). This module imports nothing but node:fs, so the
|
|
3
|
+
// server can read them before anything else it loads can start a process
|
|
4
|
+
// (cloud-secrets-boot.ts, the server's first import).
|
|
5
|
+
import { closeSync, readFileSync } from "node:fs";
|
|
6
|
+
/** The Cloud home's secrets: the signing secret and the included services'
|
|
7
|
+
* relay tokens (server/included-services.ts). The Admin sets them as Fly app
|
|
8
|
+
* secrets, which reach the machine as the launcher's environment; the
|
|
9
|
+
* launcher never puts them in a child's environment, because
|
|
10
|
+
* /proc/<pid>/environ keeps a process's starting environment for anything
|
|
11
|
+
* running as the same user to read. It hands them to the server over an
|
|
12
|
+
* inherited pipe instead (CLOUD_SECRETS_FD_ENV names its descriptor). */
|
|
13
|
+
export const CLOUD_HOME_SECRET_KEYS = ["OMB_CLOUD_BOOTSTRAP_SECRET", "OMB_CLOUD_BOAT_TOKEN", "OMB_CLOUD_VOICE_TOKEN", "OMB_CLOUD_DECIDER_TOKEN"];
|
|
14
|
+
export const CLOUD_SECRETS_FD_ENV = "OMB_CLOUD_SECRETS_FD";
|
|
15
|
+
/** The secrets present in an environment (the launcher's). */
|
|
16
|
+
export function cloudHomeSecrets(env) {
|
|
17
|
+
return Object.fromEntries(CLOUD_HOME_SECRET_KEYS.flatMap((key) => env[key] ? [[key, env[key]]] : []));
|
|
18
|
+
}
|
|
19
|
+
/** An environment with no Cloud home secret in it. */
|
|
20
|
+
export function withoutCloudSecrets(env) {
|
|
21
|
+
const kept = { ...env };
|
|
22
|
+
for (const key of CLOUD_HOME_SECRET_KEYS)
|
|
23
|
+
delete kept[key];
|
|
24
|
+
return kept;
|
|
25
|
+
}
|
|
26
|
+
function invalid(why) {
|
|
27
|
+
throw new Error(`Cloud home configuration is invalid: ${why}.`);
|
|
28
|
+
}
|
|
29
|
+
/** At server startup: the secrets the launcher handed over its pipe (read
|
|
30
|
+
* to the end and closed, so no child inherits it), or none when the server
|
|
31
|
+
* was started another way and reads its environment as before. Only the
|
|
32
|
+
* known secret names are taken. */
|
|
33
|
+
export function takeCloudSecrets(env = process.env) {
|
|
34
|
+
const named = env[CLOUD_SECRETS_FD_ENV];
|
|
35
|
+
if (named === undefined)
|
|
36
|
+
return {};
|
|
37
|
+
delete env[CLOUD_SECRETS_FD_ENV];
|
|
38
|
+
const fd = Number(named);
|
|
39
|
+
if (!Number.isInteger(fd) || fd < 3 || fd > 1024)
|
|
40
|
+
invalid(`${CLOUD_SECRETS_FD_ENV} must name an inherited descriptor`);
|
|
41
|
+
let raw;
|
|
42
|
+
try {
|
|
43
|
+
raw = readFileSync(fd, "utf8");
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
invalid("the Cloud home's secrets could not be read from the launcher");
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
try {
|
|
50
|
+
closeSync(fd);
|
|
51
|
+
}
|
|
52
|
+
catch { /* already closed */ }
|
|
53
|
+
}
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(raw);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
parsed = null;
|
|
60
|
+
}
|
|
61
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
62
|
+
invalid("the Cloud home's secrets from the launcher are not readable");
|
|
63
|
+
const record = parsed;
|
|
64
|
+
return Object.fromEntries(CLOUD_HOME_SECRET_KEYS.flatMap((key) => typeof record[key] === "string" && record[key] ? [[key, record[key]]] : []));
|
|
65
|
+
}
|
|
@@ -14,10 +14,14 @@ import { PROVIDER_ICON_PRESETS, providerIconError } from "../shared/provider-ico
|
|
|
14
14
|
import { isRemoteMcpServer, parseStoredMcpServer } from "./mcp-registry.js";
|
|
15
15
|
import { parseJson, schemaIssue } from "./schema.js";
|
|
16
16
|
import { CLOUD_SEAT_IDLE_STOP_MS } from "./cloud-overflow.js";
|
|
17
|
+
import { cloudHomeConfigured } from "./cloud-home.js";
|
|
17
18
|
const optionalText = z.string().optional();
|
|
18
19
|
const SSH_ALIAS = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/;
|
|
19
20
|
const LEGACY_BROWSER_PROFILE_ID = /^[A-Za-z0-9_-]{1,40}$/;
|
|
20
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"];
|
|
21
25
|
export const DEFAULT_ROOM_TURN_TIMEOUT_MINUTES = 5;
|
|
22
26
|
export const MIN_ROOM_TURN_TIMEOUT_MINUTES = 1;
|
|
23
27
|
export const MAX_ROOM_TURN_TIMEOUT_MINUTES = 1_440;
|
|
@@ -35,6 +39,11 @@ export const DEFAULT_LOCAL_VM_MODE = "shared";
|
|
|
35
39
|
export const DEFAULT_LOCAL_VM_MAX_INSTANCES = 2;
|
|
36
40
|
export const MIN_LOCAL_VM_MAX_INSTANCES = 1;
|
|
37
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;
|
|
38
47
|
export function isValidSshAlias(value) {
|
|
39
48
|
return typeof value === "string" && SSH_ALIAS.test(value);
|
|
40
49
|
}
|
|
@@ -111,6 +120,12 @@ const localVmConfigSchema = z.object({
|
|
|
111
120
|
.min(MIN_LOCAL_VM_MAX_INSTANCES)
|
|
112
121
|
.max(MAX_LOCAL_VM_MAX_INSTANCES)
|
|
113
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(),
|
|
114
129
|
});
|
|
115
130
|
/** A named, shareable browser session ("Work", "Client A"). The id names a
|
|
116
131
|
* durable Electron partition; user-controlled characters never reach it. */
|
|
@@ -301,6 +316,10 @@ const onboardingConfigSchema = z.object({
|
|
|
301
316
|
version: z.number().int().min(0).max(1000).optional(),
|
|
302
317
|
reelSeen: z.boolean().optional(),
|
|
303
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(),
|
|
304
323
|
}).strict();
|
|
305
324
|
const instanceConfigSchema = z.object({
|
|
306
325
|
driver: z.string().min(1),
|
|
@@ -313,6 +332,7 @@ const instanceConfigSchema = z.object({
|
|
|
313
332
|
]).optional(),
|
|
314
333
|
environment: z.record(z.string(), z.string()).optional(),
|
|
315
334
|
enabled: z.boolean().optional(),
|
|
335
|
+
access: z.enum(["subscription", "custom", "api"]).optional(),
|
|
316
336
|
config: z.json().optional(),
|
|
317
337
|
});
|
|
318
338
|
const instanceConfigMapSchema = z.record(z.string(), instanceConfigSchema);
|
|
@@ -372,7 +392,12 @@ const appConfigSchema = z.object({
|
|
|
372
392
|
/** Anthropic API key for Claude Code billed per token, handed only to
|
|
373
393
|
* Claude instances; `url` only for a proxy or a test double. Never a
|
|
374
394
|
* personal-login OAuth token. */
|
|
375
|
-
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(),
|
|
376
401
|
/** Monthly spend limit for the whole workspace, against the cost engines
|
|
377
402
|
* report to the usage ledger. Enforced only with the `budgets` entitlement. */
|
|
378
403
|
budgets: z
|
|
@@ -411,8 +436,8 @@ const appConfigSchema = z.object({
|
|
|
411
436
|
opencodeGo: z.object({ apiKey: optionalText }).optional(),
|
|
412
437
|
/** Voice settings and the selected voice id. `provider` picks the
|
|
413
438
|
* engine: "elevenlabs" (default; needs `key`), "fish" (needs its own
|
|
414
|
-
* `fishKey`), "system" (the Mac's
|
|
415
|
-
* "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
|
|
416
441
|
* "chatterbox" (a local OpenAI-compatible Chatterbox server; `baseUrl`
|
|
417
442
|
* and `model` are settings, not secrets). Cloud keys stay separate so
|
|
418
443
|
* switching providers never overwrites or misuses the other key. */
|
|
@@ -428,6 +453,25 @@ const appConfigSchema = z.object({
|
|
|
428
453
|
.refine((value) => !value || /^https?:\/\//i.test(value), "the Chatterbox server address must start with http:// or https://")
|
|
429
454
|
.optional(),
|
|
430
455
|
model: optionalText,
|
|
456
|
+
fishModel: z.enum(FISH_TTS_MODELS).optional(),
|
|
457
|
+
}).optional(),
|
|
458
|
+
/** The decision model (server/decider): a fast classifier that picks
|
|
459
|
+
* things for bots, starting with who answers a room message. `key` is
|
|
460
|
+
* write-only like every credential; `baseUrl` points at a Jev-compatible
|
|
461
|
+
* server instead of TypeSafe's (an operator setting with no UI); `jobs`
|
|
462
|
+
* switches each decision on or off. Not `decisions`: that section is the
|
|
463
|
+
* authorization log's retention. */
|
|
464
|
+
decider: z.object({
|
|
465
|
+
enabled: z.boolean().optional(),
|
|
466
|
+
provider: z.enum(["jev", "off"]).optional(),
|
|
467
|
+
key: optionalText,
|
|
468
|
+
baseUrl: z
|
|
469
|
+
.string()
|
|
470
|
+
.trim()
|
|
471
|
+
.max(2048)
|
|
472
|
+
.refine((value) => !value || /^https?:\/\//i.test(value), "the decision model address must start with http:// or https://")
|
|
473
|
+
.optional(),
|
|
474
|
+
jobs: z.object({ roomRouting: z.boolean().optional() }).optional(),
|
|
431
475
|
}).optional(),
|
|
432
476
|
/** Avatar provider credentials stay separate; choosing a router never reuses a cloud key. */
|
|
433
477
|
imageGen: z.object({
|
|
@@ -619,6 +663,9 @@ export function localVmMode(cfg) {
|
|
|
619
663
|
export function localVmMaxInstances(cfg) {
|
|
620
664
|
return cfg.localVm?.maxInstances ?? DEFAULT_LOCAL_VM_MAX_INSTANCES;
|
|
621
665
|
}
|
|
666
|
+
export function localVmIdleTimeoutMinutes(cfg) {
|
|
667
|
+
return cfg.localVm?.idleTimeoutMinutes ?? DEFAULT_LOCAL_VM_IDLE_TIMEOUT_MINUTES;
|
|
668
|
+
}
|
|
622
669
|
/** On by default; only an explicit `false` (the Settings toggle, or a legacy
|
|
623
670
|
* `skillRecorder: false` carried over at startup) switches it off. */
|
|
624
671
|
export function skillAuthoringEnabled(cfg) {
|
|
@@ -645,9 +692,13 @@ export function routinesInConversationEnabled(cfg) {
|
|
|
645
692
|
return cfg.features?.routinesInConversation === true;
|
|
646
693
|
}
|
|
647
694
|
/** Workspace-level gate for the experimental built-in browser. A bot's own
|
|
648
|
-
* switch sits under it, so either can withhold the browser.
|
|
649
|
-
|
|
650
|
-
|
|
695
|
+
* switch sits under it, so either can withhold the browser. Off until the
|
|
696
|
+
* desktop's first-run welcome turns it on. A Cloud home (cloud-home.ts)
|
|
697
|
+
* skips that welcome, ships the browser in its image and has no other screen
|
|
698
|
+
* of its own, so there it is on unless the person switched it off. */
|
|
699
|
+
export function builtInBrowserEnabled(cfg, env = process.env) {
|
|
700
|
+
const chosen = cfg.features?.browser;
|
|
701
|
+
return chosen === undefined ? cloudHomeConfigured(env) : chosen === true;
|
|
651
702
|
}
|
|
652
703
|
/** Opt-in computer sharing: the routes, the agent tools, the advertised
|
|
653
704
|
* capability and the desktop connector. Off unless an explicit `true` turns
|
|
@@ -723,6 +774,8 @@ export const FLEET_NEUTRAL_KEYS = new Set([
|
|
|
723
774
|
"profile",
|
|
724
775
|
"language",
|
|
725
776
|
"tts",
|
|
777
|
+
// no engine reads it: the harness asks it before a turn starts
|
|
778
|
+
"decider",
|
|
726
779
|
"imageGen",
|
|
727
780
|
"vps",
|
|
728
781
|
"rooms",
|
|
@@ -842,6 +895,12 @@ export function loadConfig() {
|
|
|
842
895
|
cfg.anthropic.key = process.env.OMB_ANTHROPIC_API_KEY;
|
|
843
896
|
if (process.env.OMB_ANTHROPIC_API_URL !== undefined)
|
|
844
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;
|
|
845
904
|
cfg.openaiCompat = { ...cfg.openaiCompat };
|
|
846
905
|
if (process.env.OPENAI_COMPAT_API_KEY !== undefined)
|
|
847
906
|
cfg.openaiCompat.key = process.env.OPENAI_COMPAT_API_KEY;
|
|
@@ -871,6 +930,9 @@ export function loadConfig() {
|
|
|
871
930
|
cfg.tts.voice = presetVoice;
|
|
872
931
|
if (process.env.OMB_FISH_AUDIO_API_KEY !== undefined)
|
|
873
932
|
cfg.tts.fishKey = process.env.OMB_FISH_AUDIO_API_KEY;
|
|
933
|
+
cfg.decider = { ...cfg.decider };
|
|
934
|
+
if (process.env.OMB_JEV_API_KEY !== undefined)
|
|
935
|
+
cfg.decider.key = process.env.OMB_JEV_API_KEY;
|
|
874
936
|
cfg.imageGen = { ...cfg.imageGen };
|
|
875
937
|
if (process.env.OMB_OPENAI_IMAGE_KEY !== undefined)
|
|
876
938
|
cfg.imageGen.key = process.env.OMB_OPENAI_IMAGE_KEY;
|
|
@@ -901,11 +963,14 @@ export function syncCredentialEnv(patch) {
|
|
|
901
963
|
[patch.mistral?.key, "MISTRAL_API_KEY"],
|
|
902
964
|
[patch.anthropic?.key, "OMB_ANTHROPIC_API_KEY"],
|
|
903
965
|
[patch.openaiCompat?.key, "OPENAI_COMPAT_API_KEY"],
|
|
966
|
+
[patch.openai?.key, "OMB_OPENAI_API_KEY"],
|
|
967
|
+
[patch.openrouter?.key, "OMB_OPENROUTER_API_KEY"],
|
|
904
968
|
[patch.composio?.apiKey, "COMPOSIO_API_KEY"],
|
|
905
969
|
[patch.box?.token, "BOX_TOKEN"],
|
|
906
970
|
[patch.opencodeGo?.apiKey, "OPENCODE_API_KEY"],
|
|
907
971
|
[patch.tts?.key, "OMB_TTS_KEY"],
|
|
908
972
|
[patch.tts?.fishKey, "OMB_FISH_AUDIO_API_KEY"],
|
|
973
|
+
[patch.decider?.key, "OMB_JEV_API_KEY"],
|
|
909
974
|
[patch.imageGen?.key, "OMB_OPENAI_IMAGE_KEY"],
|
|
910
975
|
[patch.imageGen?.customApiKey, "OMB_CUSTOM_IMAGE_KEY"],
|
|
911
976
|
];
|
|
@@ -948,14 +1013,23 @@ export const WORKSPACE_CREDENTIAL_ENV = [
|
|
|
948
1013
|
"OMB_HOSTED_MODELS",
|
|
949
1014
|
"OPENAI_COMPAT_API_KEY",
|
|
950
1015
|
"OPENAI_COMPAT_URL",
|
|
1016
|
+
"OMB_OPENAI_API_KEY",
|
|
1017
|
+
"OMB_OPENROUTER_API_KEY",
|
|
951
1018
|
"BOX_TOKEN",
|
|
952
1019
|
"OPENCODE_API_KEY",
|
|
953
1020
|
"OMB_TTS_KEY",
|
|
954
1021
|
"OMB_FISH_AUDIO_API_KEY",
|
|
1022
|
+
"OMB_JEV_API_KEY",
|
|
955
1023
|
"OMB_OPENAI_IMAGE_KEY",
|
|
956
1024
|
"OMB_CUSTOM_IMAGE_KEY",
|
|
957
1025
|
"COMPOSIO_API_KEY",
|
|
958
1026
|
"OMB_COMPOSIO_BROKER_TOKEN",
|
|
1027
|
+
// Cloud Pro's included Boat, voice and decision relay tokens
|
|
1028
|
+
// (included-services.ts), used only in-process by the Boat, voice and
|
|
1029
|
+
// decider modules.
|
|
1030
|
+
"OMB_CLOUD_BOAT_TOKEN",
|
|
1031
|
+
"OMB_CLOUD_VOICE_TOKEN",
|
|
1032
|
+
"OMB_CLOUD_DECIDER_TOKEN",
|
|
959
1033
|
// Harness-private filesystem hints are not credentials themselves, but
|
|
960
1034
|
// exposing them to a shell-capable agent points straight at app-owned
|
|
961
1035
|
// state. The built-in browser master is delivered privately in memory.
|
|
@@ -1037,7 +1111,7 @@ export function saveConfig(patch, options = {}) {
|
|
|
1037
1111
|
const storedProfiles = storedBrowserProfilesSchema.safeParse(disk.browserProfiles);
|
|
1038
1112
|
if (storedProfiles.success)
|
|
1039
1113
|
disk.browserProfiles = storedProfiles.data;
|
|
1040
|
-
for (const key of ["xai", "anthropic", "mistral", "openaiCompat", "composio", "box", "opencodeGo", "tts", "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"]) {
|
|
1041
1115
|
const section = checkedPatch[key];
|
|
1042
1116
|
if (!section)
|
|
1043
1117
|
continue;
|
|
@@ -1223,13 +1297,63 @@ export function persistableInstanceConfigs(cfg) {
|
|
|
1223
1297
|
}
|
|
1224
1298
|
return map;
|
|
1225
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
|
+
}
|
|
1226
1350
|
/** The credential env instanceConfigs() injects for one driver at runtime.
|
|
1227
1351
|
* Each secret goes only to the driver that actually reads it: the API-key
|
|
1228
1352
|
* Grok driver reads XAI_API_KEY, the Computer driver reads BOX_TOKEN, and
|
|
1229
1353
|
* OpenCode reads OPENCODE_API_KEY. Every other engine brings its own
|
|
1230
1354
|
* login, so handing it a key it never uses would only put that key in the
|
|
1231
1355
|
* environment of an unrelated child process. */
|
|
1232
|
-
function injectedEnvironment(cfg, driver) {
|
|
1356
|
+
function injectedEnvironment(cfg, instanceId, driver) {
|
|
1233
1357
|
const environment = new Map();
|
|
1234
1358
|
if (driver === "mistral" && cfg.mistral?.key)
|
|
1235
1359
|
environment.set("MISTRAL_API_KEY", cfg.mistral.key);
|
|
@@ -1238,21 +1362,64 @@ function injectedEnvironment(cfg, driver) {
|
|
|
1238
1362
|
// The workspace Anthropic key reaches Claude Code as the variable it
|
|
1239
1363
|
// reads, carried in the instance environment so the driver can tell a
|
|
1240
1364
|
// deliberate workspace key from one riding along in the parent's env.
|
|
1241
|
-
|
|
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)
|
|
1242
1372
|
environment.set("ANTHROPIC_API_KEY", cfg.anthropic.key);
|
|
1243
|
-
if (
|
|
1373
|
+
if (anthropicHere && cfg.anthropic.url)
|
|
1244
1374
|
environment.set("ANTHROPIC_BASE_URL", cfg.anthropic.url);
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
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
|
+
}
|
|
1249
1392
|
// driverKind "boxAgent" and env BOX_TOKEN keep their historical names.
|
|
1393
|
+
// Only the person's own token: without one the driver itself falls back
|
|
1394
|
+
// to Cloud Pro's included token, which never enters an environment map.
|
|
1250
1395
|
if (driver === "boxAgent" && cfg.box?.token)
|
|
1251
1396
|
environment.set("BOX_TOKEN", cfg.box.token);
|
|
1252
1397
|
if (driver === "opencodeGo" && cfg.opencodeGo?.apiKey)
|
|
1253
1398
|
environment.set("OPENCODE_API_KEY", cfg.opencodeGo.apiKey);
|
|
1254
1399
|
return environment;
|
|
1255
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
|
+
};
|
|
1256
1423
|
// Default fleet: one instance per built-in driver (upstream
|
|
1257
1424
|
// defaultInstanceIdForDriver — instanceId defaults to the driver kind).
|
|
1258
1425
|
// Config-file keys are injected as per-instance environment so drivers
|
|
@@ -1280,11 +1447,13 @@ export function instanceConfigs(cfg) {
|
|
|
1280
1447
|
cursor: { driver: "cursorAgent" },
|
|
1281
1448
|
claude: { driver: "claudeAgent" },
|
|
1282
1449
|
codex: { driver: "codex" },
|
|
1450
|
+
chatgpt: { driver: "codex", displayName: "ChatGPT plan", config: { authMode: "chatgpt-plan" } },
|
|
1283
1451
|
antigravity: { driver: "antigravityAgent" },
|
|
1284
1452
|
opencodeGo: { driver: "opencodeGo" },
|
|
1285
1453
|
computer: { driver: "boxAgent" },
|
|
1286
1454
|
openaiCompat: { driver: "openai-compat" },
|
|
1287
1455
|
mistral: { driver: "mistral" },
|
|
1456
|
+
...API_KEY_FLEET,
|
|
1288
1457
|
qwen: { driver: "qwenAgent" },
|
|
1289
1458
|
hermes: { driver: "hermesAgent" },
|
|
1290
1459
|
pi: { driver: "piAgent" },
|
|
@@ -1298,9 +1467,11 @@ export function instanceConfigs(cfg) {
|
|
|
1298
1467
|
// never see. Custom-only engines stay in CUSTOM_ONLY so a one-off test map
|
|
1299
1468
|
// is not expanded, matching the claude/grok/codex product-fleet probe.
|
|
1300
1469
|
const PRODUCT_FLEET_ADDITIONS = {
|
|
1470
|
+
chatgpt: { driver: "codex", displayName: "ChatGPT plan", config: { authMode: "chatgpt-plan" } },
|
|
1301
1471
|
cursor: { driver: "cursorAgent" },
|
|
1302
1472
|
openaiCompat: { driver: "openai-compat" },
|
|
1303
1473
|
mistral: { driver: "mistral" },
|
|
1474
|
+
...API_KEY_FLEET,
|
|
1304
1475
|
...CUSTOM_ONLY,
|
|
1305
1476
|
};
|
|
1306
1477
|
const configured = cfg.instances && Object.keys(cfg.instances).length ? cfg.instances : null;
|
|
@@ -1314,6 +1485,22 @@ export function instanceConfigs(cfg) {
|
|
|
1314
1485
|
map[id] = { ...entry };
|
|
1315
1486
|
}
|
|
1316
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
|
+
}
|
|
1317
1504
|
for (const [id, sourceEntry] of Object.entries(map)) {
|
|
1318
1505
|
// instanceConfigs() builds a transient runtime map. Never mutate the
|
|
1319
1506
|
// caller's persisted entries while injecting workspace defaults: doing so
|
|
@@ -1321,14 +1508,25 @@ export function instanceConfigs(cfg) {
|
|
|
1321
1508
|
const entry = { ...sourceEntry };
|
|
1322
1509
|
map[id] = entry;
|
|
1323
1510
|
const environment = { ...entry.environment };
|
|
1324
|
-
|
|
1325
|
-
|
|
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
|
+
}
|
|
1326
1524
|
entry.environment = environment;
|
|
1327
1525
|
// The driver URL is configuration, not a credential. Environment is
|
|
1328
1526
|
// intentionally not consulted by ProviderRegistry when it decodes a
|
|
1329
1527
|
// driver's config, so carry the workspace default into the transient
|
|
1330
1528
|
// instance map while preserving a per-instance override.
|
|
1331
|
-
if (entry.driver === "openai-compat" && cfg.openaiCompat) {
|
|
1529
|
+
if (entry.driver === "openai-compat" && cfg.openaiCompat && id !== OPENAI_API_INSTANCE && id !== OPENROUTER_API_INSTANCE) {
|
|
1332
1530
|
const defaults = {};
|
|
1333
1531
|
if (cfg.openaiCompat.url)
|
|
1334
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" &&
|