@openclaw/codex 2026.9.5 → 2026.9.7
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/README.md +2 -0
- package/assets/icon.png +0 -0
- package/dist/.setup/{account-usage-runtime-C2MOIvfq.mjs → account-usage-runtime-DFhbh1zz.mjs} +6 -6
- package/dist/.setup/{apply-CJ0ZfmBG.mjs → apply-D3GcSoSa.mjs} +35 -55
- package/dist/.setup/{attempt-client-cleanup-BpYKdTA0.mjs → attempt-client-cleanup-DOP-cW5j.mjs} +179 -73
- package/dist/.setup/attempt-workspace-context-C5HigshI.mjs +164 -0
- package/dist/.setup/{auth-binding-DhLWa_7K.mjs → auth-binding-BLWz7stm.mjs} +14 -1
- package/dist/.setup/{auth-cache-key-QirmgiCo.mjs → auth-cache-key-Dbvq-QgB.mjs} +41 -111
- package/dist/.setup/{auth-profile-WqZtZfXN.mjs → auth-profile-B0okYl2D.mjs} +1 -2
- package/dist/.setup/auth-profile-health-oHizqgye.mjs +53 -0
- package/dist/.setup/auth-profile-recovery-BREezV0L.mjs +14 -0
- package/dist/.setup/auth-start-options-BPIPeEX8.mjs +44 -0
- package/dist/.setup/{binding-connection-Bm38niE8.mjs → binding-connection-DTtN1KrX.mjs} +2 -2
- package/dist/.setup/{binding-connection.runtime-CnRMC0ta.mjs → binding-connection.runtime-6TpgD1M6.mjs} +5 -5
- package/dist/.setup/{bounded-turn-BtlZcivC.mjs → bounded-turn-CbjoN2OW.mjs} +140 -37
- package/dist/.setup/bounded-turn-isolation-B8K_ylqk.mjs +14 -0
- package/dist/.setup/{build-state-Xx9ybZqa.mjs → build-state-Da1_z0kd.mjs} +1 -1
- package/dist/.setup/{capabilities-_vXcZGSx.mjs → capabilities-B3WEZl4e.mjs} +2 -10
- package/dist/.setup/{client-vXs2929Q.mjs → client-N0JpgSzF.mjs} +635 -872
- package/dist/.setup/client-catalog-response-5HHLdcde.mjs +285 -0
- package/dist/.setup/{command-dispatch-naHIZwwF.mjs → command-dispatch-BeAoaovI.mjs} +9 -8
- package/dist/.setup/{command-formatters-CGEkdawF.mjs → command-formatters-R3ckE3Co.mjs} +36 -64
- package/dist/.setup/{command-handlers-BtAJEW1X.mjs → command-handlers-ylTMS00z.mjs} +301 -488
- package/dist/.setup/{command-rpc-DHFpn4le.mjs → command-rpc-CGJGUQ6Y.mjs} +40 -28
- package/dist/.setup/compact-C2WamavS.mjs +776 -0
- package/dist/.setup/{config-CedDWjM-.mjs → config-DKhXquav.mjs} +26 -28
- package/dist/.setup/{config-options-BsanJ8QG.mjs → config-options-CzyWLTJt.mjs} +68 -65
- package/dist/.setup/{config-parsing-CcB9iPoq.mjs → config-parsing-b088LyhT.mjs} +57 -60
- package/dist/.setup/{config-runtime-CeAhl_h4.mjs → config-runtime-C0NbLms1.mjs} +2 -2
- package/dist/.setup/{config-security-BRQ2mgW9.mjs → config-security-CE-FcGw9.mjs} +51 -215
- package/dist/.setup/{config-utils-DujwEnhg.mjs → config-utils-D0CXecki.mjs} +3 -13
- package/dist/.setup/context-engine-projection-PezSESth.mjs +387 -0
- package/dist/.setup/{conversation-binding-DhzrpTM6.mjs → conversation-binding-3Min8qvP.mjs} +71 -105
- package/dist/.setup/{conversation-binding-data-CrcyZk6O.mjs → conversation-binding-data-C746z5Pn.mjs} +8 -12
- package/dist/.setup/{conversation-control-DN53ns41.mjs → conversation-control-CEP8X5JC.mjs} +30 -69
- package/dist/.setup/{cyber-failover-DYwbmf1o.mjs → cyber-failover-XV0nNRZu.mjs} +1 -1
- package/dist/.setup/{cyber-failover-notice-e9G3J9-e.mjs → cyber-failover-notice-RNpGpTyP.mjs} +1 -1
- package/dist/.setup/{dynamic-tools-D16lOp67.mjs → dynamic-tools-DzlsLUuS.mjs} +3136 -3167
- package/dist/.setup/{effective-mcp-catalog-K6kLCN8X.mjs → effective-mcp-catalog-CxUtC-xG.mjs} +8 -17
- package/dist/.setup/{ephemeral-turn-R1gA8Sn6.mjs → ephemeral-turn-CDsPJOa2.mjs} +9 -6
- package/dist/.setup/event-projector-assistant-message-CigreO4K.mjs +69 -0
- package/dist/.setup/{event-projector-usage-C7Ntzs-f.mjs → event-projector-usage-03R0I2dM.mjs} +4 -26
- package/dist/.setup/event-projector-values-DJSz8BTk.mjs +106 -0
- package/dist/.setup/incognito-session-DWYapfwf.mjs +2 -0
- package/dist/.setup/{inference-context-fulhZuz8.mjs → inference-context-D-9gWlTR.mjs} +16 -18
- package/dist/.setup/inference-dispatch-CMZtjQyz.mjs +359 -0
- package/dist/.setup/inference-metadata-CgIf7sOx.mjs +81 -0
- package/dist/.setup/inference-proxy-DhcXOgUE.mjs +561 -0
- package/dist/.setup/{isolated-completion-B6srt48J.mjs → isolated-completion-Dm3PzxJk.mjs} +15 -11
- package/dist/.setup/launch-args-DbFCehO7.mjs +196 -0
- package/dist/.setup/{managed-binary-DP6Rib3P.mjs → managed-binary-kiWRhNRE.mjs} +16 -9
- package/dist/.setup/{model-catalog-DkvUuBey.mjs → model-catalog-PAuZgJC6.mjs} +14 -14
- package/dist/.setup/model-runtime-BdqzOMkU.mjs +11 -0
- package/dist/.setup/{models-BDFgm_G-.mjs → models-DHXZtVVk.mjs} +9 -18
- package/dist/.setup/{native-auth-BLZGqFjw.mjs → native-auth-CuG53tu4.mjs} +5 -5
- package/dist/.setup/{native-execution-policy-YBMHSzC4.mjs → native-execution-policy-BxZJy5mP.mjs} +6 -31
- package/dist/.setup/{native-subagent-monitor-B1hxzIOy.mjs → native-subagent-monitor-Dpsmf2wz.mjs} +4422 -3808
- package/dist/.setup/native-subagent-pending-assignments-DwkCzLvD.mjs +203 -0
- package/dist/.setup/native-task-assignments-DMQ0g3hu.mjs +194 -0
- package/dist/.setup/{node-cli-sessions-Cl8b2ZF3.mjs → node-cli-sessions-HC4W6FLI.mjs} +99 -111
- package/dist/.setup/node-exec-server.runtime-BgBfWQqo.mjs +244 -0
- package/dist/.setup/{plan-xD7WKYTM.mjs → plan-Y-QIvrKB.mjs} +118 -163
- package/dist/.setup/{plugin-activation-r8nq5Jzp.mjs → plugin-activation-cNHRfaxU.mjs} +10 -47
- package/dist/.setup/{plugin-app-cache-key-BOxKP7db.mjs → plugin-app-cache-key-BL9B5H0L.mjs} +9 -42
- package/dist/.setup/{plugin-inventory-DUle8DLz.mjs → plugin-inventory-Cky2XJUw.mjs} +32 -23
- package/dist/.setup/{plugin-marketplace-discovery-P-Vndfqq.mjs → plugin-marketplace-discovery-D_dV9FXa.mjs} +22 -25
- package/dist/.setup/{plugin-metadata-cache-DsS5buNa.mjs → plugin-metadata-cache-DsjsL1dr.mjs} +328 -318
- package/dist/.setup/protocol-CANUwXJ3.mjs +17 -0
- package/dist/.setup/provider-DH8NSzGt.mjs +51 -0
- package/dist/.setup/{provider-capabilities-B8J4Dy-O.mjs → provider-capabilities-CAxoF-X1.mjs} +35 -12
- package/dist/.setup/{rate-limits-DOEGYRNs.mjs → rate-limits-B7-X2TgG.mjs} +123 -166
- package/dist/.setup/{request-BioXJLYa.mjs → request-BHZ1nz8d.mjs} +25 -16
- package/dist/.setup/responses-oauth-twnuTaAa.mjs +93 -0
- package/dist/.setup/rpc-error-BlD2vuzn.mjs +31 -0
- package/dist/.setup/{run-attempt-rlA07pUv.mjs → run-attempt-D8RZUOzY.mjs} +2046 -1454
- package/dist/.setup/{run-attempt-lifecycle-B7JbY4-F.mjs → run-attempt-lifecycle-C-6O3Exn.mjs} +10 -24
- package/dist/.setup/{runtime-artifact-Dy38Z5iU.mjs → runtime-artifact-DDQ-HGXf.mjs} +43 -47
- package/dist/.setup/runtime-artifact-connection-D68qQn1m.mjs +78 -0
- package/dist/.setup/sandbox-guard-DjCz2eOw.mjs +126 -0
- package/dist/.setup/scope-DOC1Xjc8.mjs +6 -0
- package/dist/.setup/session-binding-DGCYAfvD.mjs +443 -0
- package/dist/.setup/{session-binding-record-TH3xA0zj.mjs → session-binding-record-Oe9L-CNY.mjs} +73 -132
- package/dist/.setup/{session-binding-sidecars-CKYEcfNn.mjs → session-binding-sidecars-jo6H8dFr.mjs} +242 -60
- package/dist/.setup/{session-catalog-fUdWDx-U.mjs → session-catalog-4sCdv5qx.mjs} +598 -452
- package/dist/.setup/{session-catalog-adoption-CR_whkEW.mjs → session-catalog-adoption-BU_pHWG7.mjs} +59 -26
- package/dist/.setup/{session-catalog-archive-CwZaPacf.mjs → session-catalog-archive-spHfihri.mjs} +7 -7
- package/dist/.setup/session-catalog-events-DePDzzjc.mjs +139 -0
- package/dist/.setup/session-catalog-home-id-vlih0B_g.mjs +22 -0
- package/dist/.setup/{session-catalog-index-B141u15s.mjs → session-catalog-index-CZvDzC6z.mjs} +286 -182
- package/dist/.setup/{session-catalog-index-order-Cc5BzlQr.mjs → session-catalog-index-order-BcQpHks3.mjs} +2 -2
- package/dist/.setup/{session-catalog-index-state-BAuDztHN.mjs → session-catalog-index-state-TeO7EyO-.mjs} +6 -2
- package/dist/.setup/session-catalog-limits-DDxOsQeC.mjs +14 -0
- package/dist/.setup/session-catalog-list-request-CvtxrYSe.mjs +170 -0
- package/dist/.setup/session-catalog-native-projection-DjhwGL6x.mjs +178 -0
- package/dist/.setup/{session-catalog-node-adoption-BVRsYOSh.mjs → session-catalog-node-adoption-DgXvSGu0.mjs} +30 -35
- package/dist/.setup/{session-catalog-node-continue-Dowv8osw.mjs → session-catalog-node-continue-DjKXHBbi.mjs} +25 -18
- package/dist/.setup/session-catalog-node-lookup-CtAXI5W5.mjs +56 -0
- package/dist/.setup/{session-catalog-native-projection-ChV3cCtm.mjs → session-catalog-parsing-CPGaPYr-.mjs} +14 -180
- package/dist/.setup/{session-catalog-projection-C1Bj9Jl-.mjs → session-catalog-projection-Cr5qP5Dj.mjs} +15 -5
- package/dist/.setup/session-catalog-provenance-DoAvktcj.mjs +95 -0
- package/dist/.setup/{session-cli-DIj-IE-E.mjs → session-cli-Cn_nCqfq.mjs} +11 -24
- package/dist/.setup/{session-history-C--SrLI8.mjs → session-history-D83lJWHG.mjs} +16 -23
- package/dist/.setup/{session-history-import-JHXT5Xfi.mjs → session-history-import-WhX-X33a.mjs} +7 -10
- package/dist/.setup/session-history-read-Ov3StuL4.mjs +133 -0
- package/dist/.setup/{session-history.worker-CpNj7H_P.mjs → session-history.worker-MnPKncqP.mjs} +14 -23
- package/dist/.setup/session-initialization-6aveB9qb.mjs +22 -0
- package/dist/.setup/{session-permission-policy-DhurXY0l.mjs → session-permission-policy-Ci_fVX6y.mjs} +4 -8
- package/dist/.setup/{session-retirement-CU87AOg9.mjs → session-retirement-qMTxkNx2.mjs} +31 -12
- package/dist/.setup/{settled-turn-context-fmwouT5I.mjs → settled-turn-context-D4yj4rxf.mjs} +1 -1
- package/dist/.setup/{settled-turn-finalizer-CiY2bLFK.mjs → settled-turn-finalizer-BsvEabkw.mjs} +17 -13
- package/dist/.setup/{shared-client-BpTtuLQ5.mjs → shared-client-DqyTMYug.mjs} +826 -485
- package/dist/.setup/shared-client-lifecycle-DJIgqgMw.mjs +153 -0
- package/dist/.setup/{side-question-DSCZyoBD.mjs → side-question-C4kCmQy1.mjs} +206 -334
- package/dist/.setup/{source-Bjp4UQij.mjs → source-CS_7GFJZ.mjs} +82 -139
- package/dist/.setup/spawn-error-CL3n6MAa.mjs +57 -0
- package/dist/.setup/{thread-archive-guard-jmecf6OJ.mjs → thread-archive-guard-C9frICzX.mjs} +10 -3
- package/dist/.setup/thread-lifecycle-DPflntEJ.mjs +2362 -0
- package/dist/.setup/{incognito-session-uhrBF6wJ.mjs → thread-ownership-queue-Dp24d7-F.mjs} +1 -2
- package/dist/.setup/{thread-ownership-skomxnIt.mjs → thread-ownership-roNP0l1c.mjs} +47 -10
- package/dist/.setup/thread-policy-DTd-DtkX.mjs +109 -0
- package/dist/.setup/thread-requests-B_bRnMGN.mjs +2102 -0
- package/dist/.setup/{thread-resume-BkNpj9Pz.mjs → thread-resume-CdDaoc7T.mjs} +6 -6
- package/dist/.setup/timeout-CXOfNoaX.mjs +46 -0
- package/dist/.setup/transcript-history-projection-DRSifI-1.mjs +201 -0
- package/dist/.setup/{transcript-mirror-attestation-CzM-94NC.mjs → transcript-mirror-attestation-YSegaktH.mjs} +3 -4
- package/dist/.setup/{transcript-mirror-D4qp_leP.mjs → transcript-mirror-kxmCYsuF.mjs} +224 -206
- package/dist/.setup/{transport-stdio-Cmx94Oiy.mjs → transport-stdio-Bdegpn2V.mjs} +138 -75
- package/dist/.setup/transport-websocket-BTBE-LD9.mjs +211 -0
- package/dist/.setup/{upstream-prompt-provenance-LphB8slG.mjs → upstream-prompt-provenance-fXkKbPVC.mjs} +3 -9
- package/dist/.setup/{upstream-session-fork-D3UKxgVD.mjs → upstream-session-fork-CBeVjBpT.mjs} +48 -40
- package/dist/.setup/{usage-BOgBEKso.mjs → usage-BMzNxeR_.mjs} +5 -4
- package/dist/.setup/{usage-limit-error-C0ow7DOc.mjs → usage-limit-error-QXerDo3i.mjs} +5 -20
- package/dist/.setup/{web-search-provider.runtime-CTGuJt6i.mjs → web-search-provider.runtime-P77OUeoo.mjs} +10 -20
- package/dist/api.js +25 -16
- package/dist/catalog-page-worker-entrypoint.js +12 -0
- package/dist/catalog-page.worker.js +9 -0
- package/dist/cli-metadata.js +1 -1
- package/dist/doctor-contract-api.js +50 -8
- package/dist/harness.js +344 -1
- package/dist/index.js +345 -346
- package/dist/media-understanding-provider.js +9 -22
- package/dist/migration-provider-api.js +1 -1
- package/dist/provider-discovery.js +1 -1
- package/dist/session-history-worker-runtime.js +16 -33
- package/dist/session-history.worker.js +1 -1
- package/openclaw.plugin.json +14 -2
- package/package.json +11 -7
- package/dist/.setup/attempt-context-VXVwTCfS.mjs +0 -636
- package/dist/.setup/compact-C3i3qlzj.mjs +0 -687
- package/dist/.setup/event-projector-assistant-message-DNUR1s0A.mjs +0 -105
- package/dist/.setup/event-projector-items-DyTQ_XqL.mjs +0 -200
- package/dist/.setup/harness-rxaaoN-o.mjs +0 -349
- package/dist/.setup/history-rejection-B4nOXNXF.mjs +0 -13
- package/dist/.setup/image-payload-sanitizer-tupAr-2o.mjs +0 -79
- package/dist/.setup/inference-proxy-CC15HuSi.mjs +0 -311
- package/dist/.setup/launch-args-DnMak49W.mjs +0 -111
- package/dist/.setup/local-runtime-attribution-B_2mnqhb.mjs +0 -19
- package/dist/.setup/native-subagent-history-YXaL6dMd.mjs +0 -135
- package/dist/.setup/native-subagent-task-ids-DsuOonKv.mjs +0 -411
- package/dist/.setup/node-exec-server.runtime-BaViBKNz.mjs +0 -226
- package/dist/.setup/plugin-approval-roundtrip-CjfzafAZ.mjs +0 -122
- package/dist/.setup/provider-fFh_-VR3.mjs +0 -50
- package/dist/.setup/rolldown-runtime-8H4AJuhK.mjs +0 -14
- package/dist/.setup/sandbox-guard-BBP-RbTf.mjs +0 -166
- package/dist/.setup/session-binding-_0W_fHRr.mjs +0 -721
- package/dist/.setup/session-catalog-events-jMcYfQyd.mjs +0 -250
- package/dist/.setup/session-history-read-B3ZfSUbF.mjs +0 -50
- package/dist/.setup/session-initialization-DH1z6fXn.mjs +0 -75
- package/dist/.setup/thread-history-page-BTx-4xsg.mjs +0 -83
- package/dist/.setup/thread-lifecycle-Cik28qWn.mjs +0 -4904
- package/dist/.setup/thread-policy-Cjs4B8xf.mjs +0 -54
- package/dist/.setup/timeout-DZF05XrR.mjs +0 -63
- package/dist/.setup/transcript-history-projection-DHNBDhCU.mjs +0 -243
- package/dist/.setup/version-CoqzXPgK.mjs +0 -12
|
@@ -1,38 +1,43 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as defineCodexBuildState, t as codexBuildSymbol } from "./build-state-
|
|
1
|
+
import { i as isJsonObject } from "./protocol-CANUwXJ3.mjs";
|
|
2
|
+
import { n as defineCodexBuildState, t as codexBuildSymbol } from "./build-state-Da1_z0kd.mjs";
|
|
3
3
|
import { t as CODEX_APP_SERVER_AUTH_PROVIDER } from "./auth-profile-selection-DFu9e167.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { n as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { c as
|
|
9
|
-
import {
|
|
10
|
-
import { n as resolveCodexAppServerSpawnEnv } from "./transport-stdio-
|
|
11
|
-
import { n as
|
|
12
|
-
import {
|
|
13
|
-
import { n as
|
|
14
|
-
import {
|
|
4
|
+
import { n as isCodexAppServerProxyLaunch } from "./launch-args-DbFCehO7.mjs";
|
|
5
|
+
import { a as withEphemeralCodexAuthStore, i as withClearedEnvironmentVariables, n as resolveCodexAppServerLocalHomeDir, r as resolveCodexAppServerUserHomeDir, t as resolveCodexAppServerHomeDir } from "./auth-start-options-BPIPeEX8.mjs";
|
|
6
|
+
import { a as resolveCodexAppServerStartOptionsForAgent, d as readCodexEffectiveConfig, o as resolveCodexComputerUseConfig, t as codexAppServerStartOptionsKey, u as CODEX_SESSION_OVERRIDABLE_LAYER_TYPES } from "./config-options-CzyWLTJt.mjs";
|
|
7
|
+
import { d as resolveMacOSDesktopCodexBundledMarketplaceCandidates, f as resolveMacOSDesktopCodexComputerUseServiceAppCandidates, i as resolveManagedCodexNativeCommand, l as resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath, r as resolveManagedCodexAppServerStartOptions, t as isManagedCodexDesktopCommand, u as resolveMacOSDesktopCodexAppPathCandidates } from "./managed-binary-kiWRhNRE.mjs";
|
|
8
|
+
import { a as readCodexCliAuthFileApiKey, c as resolveCodexAppServerPreparedApiKeyCacheKey, d as waitForCodexDesktopGeneration, f as readCodexDesktopArtifactTreeFingerprint, i as fingerprintTokenAuthProfileCacheKey, n as CODEX_AUTH_JSON_FILENAME, o as readFirstNonEmptyEnv, r as fingerprintApiKeyAuthProfileCacheKey, s as resolveCodexAppServerFallbackApiKeyCacheKey, t as CODEX_APP_SERVER_API_KEY_ENV_VARS, u as isCodexDesktopGenerationCurrent } from "./auth-cache-key-Dbvq-QgB.mjs";
|
|
9
|
+
import { a as notifyDesktopGenerationDrainChecks, c as retainSharedClientEntry, d as retireSharedCodexAppServerClientIfCurrent, m as waitForUnclaimedSharedClientStartup, n as getCurrentSharedClientEntry, o as ownCodexStartup, r as getSharedCodexAppServerClientState, s as releaseSharedClientEntry, t as createCodexAppServerStartupLifetime, u as retirePendingSharedClientEntryIfUnclaimed } from "./shared-client-lifecycle-DJIgqgMw.mjs";
|
|
10
|
+
import { n as resolveCodexAppServerSpawnEnv } from "./transport-stdio-Bdegpn2V.mjs";
|
|
11
|
+
import { n as CodexAppServerAuthProfileUnavailableError, r as formatCodexAuthProfileUnavailableMessage } from "./auth-profile-recovery-BREezV0L.mjs";
|
|
12
|
+
import { i as withTimeout } from "./timeout-CXOfNoaX.mjs";
|
|
13
|
+
import { n as readCodexSessionMeta } from "./session-catalog-provenance-DoAvktcj.mjs";
|
|
14
|
+
import { n as observeCodexCatalogClient } from "./session-catalog-events-DePDzzjc.mjs";
|
|
15
|
+
import { n as resolveCodexAppServerRuntimeOptions } from "./config-runtime-C0NbLms1.mjs";
|
|
16
|
+
import { a as resolveCodexAppServerAuthProfileStore, i as resolveCodexAppServerAuthProfileIdForAgent, r as resolveCodexAppServerAuthProfileId, t as isCodexAppServerNativeAuthProfile } from "./auth-profile-B0okYl2D.mjs";
|
|
17
|
+
import { i as isCodexResponsesOAuthCredential, n as createCodexResponsesOAuth, o as resolveCodexResponsesOAuthProfileFingerprint, r as isCodexResponsesOAuth, t as CODEX_RESPONSES_OAUTH_PROVIDER } from "./responses-oauth-twnuTaAa.mjs";
|
|
18
|
+
import { f as isUnsupportedCodexAppServerVersionError, t as CodexAppServerClient } from "./client-N0JpgSzF.mjs";
|
|
19
|
+
import { n as isCodexWebSocketOpenFailure } from "./transport-websocket-BTBE-LD9.mjs";
|
|
20
|
+
import { t as __exportAll } from "./session-binding-sidecars-jo6H8dFr.mjs";
|
|
15
21
|
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
16
|
-
import { createHash } from "node:crypto";
|
|
22
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
17
23
|
import { isDeepStrictEqual } from "node:util";
|
|
18
24
|
import { AgentHarnessPreflightError, resolveDefaultAgentDir } from "openclaw/plugin-sdk/agent-harness-registration";
|
|
19
|
-
import { addTimerTimeoutGraceMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
20
25
|
import { runExec } from "openclaw/plugin-sdk/process-runtime";
|
|
21
|
-
import
|
|
26
|
+
import { addTimerTimeoutGraceMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
27
|
+
import fs, { constants, existsSync } from "node:fs";
|
|
22
28
|
import path from "node:path";
|
|
23
29
|
import fs$1 from "node:fs/promises";
|
|
30
|
+
import { extractErrorCode } from "openclaw/plugin-sdk/error-runtime";
|
|
31
|
+
import { resolveGlobalMap } from "openclaw/plugin-sdk/global-singleton";
|
|
24
32
|
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
25
33
|
import { ensureAuthProfileStore, findPersistedAuthProfileCredential, refreshOAuthCredentialForRuntime, resolveApiKeyForProfile, resolvePersistedAuthProfileOwnerAgentDir } from "openclaw/plugin-sdk/agent-runtime";
|
|
26
|
-
import { AgentHarnessPreflightError as AgentHarnessPreflightError$1, embeddedAgentLog as embeddedAgentLog$1, formatErrorMessage } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
34
|
+
import { AgentHarnessPreflightError as AgentHarnessPreflightError$1, embeddedAgentLog as embeddedAgentLog$1, formatErrorMessage as formatErrorMessage$1 } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
27
35
|
import { hasUsableOAuthCredential, resolveOpenAICodexAuthIdentity } from "openclaw/plugin-sdk/provider-auth";
|
|
28
36
|
import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
|
|
29
|
-
import {
|
|
37
|
+
import { assertDirectoryIdentitySync, readDirectoryIdentity } from "@openclaw/fs-safe/advanced";
|
|
38
|
+
import { assertNoSymlinkParents, pathScope } from "openclaw/plugin-sdk/security-runtime";
|
|
39
|
+
import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
|
|
30
40
|
//#region extensions/codex/src/app-server/attempt-timeouts.ts
|
|
31
|
-
/**
|
|
32
|
-
* Timeout defaults and normalizers for Codex app-server startup and turn
|
|
33
|
-
* liveness watches.
|
|
34
|
-
*/
|
|
35
|
-
/** Minimum startup timeout accepted by the Codex app-server harness. */
|
|
36
41
|
const CODEX_APP_SERVER_STARTUP_TIMEOUT_FLOOR_MS = 100;
|
|
37
42
|
const TURN_TERMINAL_SETTLEMENT_TIMEOUT_MS = 12e4;
|
|
38
43
|
const TURN_FINALIZE_DRAIN_ABORT_GRACE_MS = 5e3;
|
|
@@ -51,7 +56,6 @@ function resolvePositiveIntegerTimeoutMs(value, fallbackMs) {
|
|
|
51
56
|
const fallback = resolveTimerTimeoutMs(fallbackMs, 1);
|
|
52
57
|
return resolveTimerTimeoutMs(value, fallback);
|
|
53
58
|
}
|
|
54
|
-
/** Runs startup work with abort and timeout handling plus optional cleanup. */
|
|
55
59
|
async function withCodexStartupTimeout(params) {
|
|
56
60
|
if (params.signal.aborted) throw new CodexAppServerStartupError("aborted");
|
|
57
61
|
let timeout;
|
|
@@ -89,13 +93,11 @@ async function withCodexStartupTimeout(params) {
|
|
|
89
93
|
abortCleanup?.();
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
|
-
/** Resolves startup timeout while honoring the configured floor. */
|
|
93
96
|
function resolveCodexStartupTimeoutMs(params) {
|
|
94
97
|
const timeoutFloorMs = resolvePositiveIntegerTimeoutMs(params.timeoutFloorMs, CODEX_APP_SERVER_STARTUP_TIMEOUT_FLOOR_MS);
|
|
95
98
|
const timeoutMs = resolvePositiveIntegerTimeoutMs(params.timeoutMs, timeoutFloorMs);
|
|
96
99
|
return Math.max(timeoutFloorMs, timeoutMs);
|
|
97
100
|
}
|
|
98
|
-
/** Adds gateway grace time to a caller timeout without overflowing invalid values. */
|
|
99
101
|
function resolveCodexGatewayTimeoutWithGraceMs(timeoutMs, graceMs = 1e4) {
|
|
100
102
|
const timeout = resolvePositiveIntegerTimeoutMs(timeoutMs, 1);
|
|
101
103
|
const grace = resolveTimerTimeoutMs(graceMs, 0, 0);
|
|
@@ -166,17 +168,16 @@ async function assertOwnedCodexHomePath(params) {
|
|
|
166
168
|
}
|
|
167
169
|
async function readRealDirectoryIdentity(directoryPath, label) {
|
|
168
170
|
const logicalPath = path.resolve(directoryPath);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
};
|
|
171
|
+
try {
|
|
172
|
+
const identity = await readDirectoryIdentity(logicalPath);
|
|
173
|
+
assertDirectoryIdentitySync(logicalPath, identity);
|
|
174
|
+
return {
|
|
175
|
+
logicalPath,
|
|
176
|
+
...identity
|
|
177
|
+
};
|
|
178
|
+
} catch (cause) {
|
|
179
|
+
throw new Error(`${label} must remain a real directory: ${logicalPath}`, { cause });
|
|
180
|
+
}
|
|
180
181
|
}
|
|
181
182
|
async function assertOwnedServiceParentStable(parent) {
|
|
182
183
|
await assertDirectoryIdentityStable(parent, "Computer Use service parent");
|
|
@@ -184,14 +185,10 @@ async function assertOwnedServiceParentStable(parent) {
|
|
|
184
185
|
async function assertDirectoryIdentityStable(expected, label) {
|
|
185
186
|
if (!await directoryIdentityIsStable(expected)) throw new Error(`${label} changed during refresh; refusing to mutate the replacement path.`);
|
|
186
187
|
}
|
|
187
|
-
async function ownedServiceParentIsStable(parent) {
|
|
188
|
-
return await directoryIdentityIsStable(parent);
|
|
189
|
-
}
|
|
190
188
|
async function directoryIdentityIsStable(expected) {
|
|
191
189
|
try {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return await fs$1.realpath(expected.logicalPath) === expected.realPath;
|
|
190
|
+
assertDirectoryIdentitySync(expected.logicalPath, expected);
|
|
191
|
+
return true;
|
|
195
192
|
} catch {
|
|
196
193
|
return false;
|
|
197
194
|
}
|
|
@@ -200,7 +197,7 @@ async function assertNotSymlink(filePath, label) {
|
|
|
200
197
|
try {
|
|
201
198
|
if ((await fs$1.lstat(filePath)).isSymbolicLink()) throw new Error(`${label} must not be a symbolic link: ${filePath}`);
|
|
202
199
|
} catch (error) {
|
|
203
|
-
if (
|
|
200
|
+
if (extractErrorCode(error) === "ENOENT") return;
|
|
204
201
|
throw error;
|
|
205
202
|
}
|
|
206
203
|
}
|
|
@@ -208,84 +205,43 @@ async function ensureRealDirectoryTree(ownershipRoot, directoryPath, label) {
|
|
|
208
205
|
const root = path.resolve(ownershipRoot);
|
|
209
206
|
const target = path.resolve(directoryPath);
|
|
210
207
|
assertPathAtOrInside(root, target, label);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
current = path.join(current, segment);
|
|
215
|
-
const existing = await fs$1.lstat(current).catch((error) => {
|
|
216
|
-
if (hasNodeErrorCode$1(error, "ENOENT")) return;
|
|
217
|
-
throw error;
|
|
218
|
-
});
|
|
219
|
-
if (!existing) {
|
|
220
|
-
try {
|
|
221
|
-
await fs$1.mkdir(current, { mode: 448 });
|
|
222
|
-
} catch (error) {
|
|
223
|
-
if (!hasNodeErrorCode$1(error, "EEXIST")) throw error;
|
|
224
|
-
}
|
|
225
|
-
const created = await fs$1.lstat(current);
|
|
226
|
-
if (created.isSymbolicLink() || !created.isDirectory()) throw new Error(`${label} changed while its directory tree was being created: ${current}`);
|
|
227
|
-
} else if (existing.isSymbolicLink() || !existing.isDirectory()) throw new Error(`${label} must traverse real directories: ${current}`);
|
|
208
|
+
if (root !== target) {
|
|
209
|
+
const prepared = await pathScope(root, { label }).ensureDir(`${target}${path.sep}`, { mode: 448 });
|
|
210
|
+
if (!prepared.ok) throw new Error(`${label} must traverse real directories: ${prepared.error}`, { cause: prepared.diagnostic });
|
|
228
211
|
}
|
|
229
|
-
await assertNoSymlinkParents({
|
|
230
|
-
rootDir: root,
|
|
231
|
-
targetPath: target,
|
|
232
|
-
allowMissing: false,
|
|
233
|
-
requireDirectories: true,
|
|
234
|
-
messagePrefix: "Computer Use service path"
|
|
235
|
-
});
|
|
236
212
|
await readRealDirectoryIdentity(target, label);
|
|
237
213
|
}
|
|
238
214
|
function assertPathAtOrInside(rootPath, candidatePath, label) {
|
|
239
215
|
const relative = path.relative(path.resolve(rootPath), path.resolve(candidatePath));
|
|
240
216
|
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`${label} must remain inside ${path.resolve(rootPath)}.`);
|
|
241
217
|
}
|
|
242
|
-
function hasNodeErrorCode$1(error, code) {
|
|
243
|
-
return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
|
|
244
|
-
}
|
|
245
218
|
//#endregion
|
|
246
219
|
//#region extensions/codex/src/app-server/computer-use-cache.ts
|
|
247
220
|
/** Shared Computer Use plugin cache reconciliation for isolated Codex homes. */
|
|
248
221
|
const DEFAULT_CODEX_COMPUTER_USE_BUNDLED_MARKETPLACE_PATH = resolveMacOSDesktopCodexBundledMarketplaceCandidates("darwin")[0] ?? "";
|
|
249
222
|
const DEFAULT_BUNDLED_MARKETPLACE_NAME = "openai-bundled";
|
|
250
223
|
async function ensureCodexComputerUseSharedPluginCache(params) {
|
|
251
|
-
if (!params.config.enabled
|
|
252
|
-
if (params.config.pluginCacheMode === "independent") return skippedCacheResult("independent", "Computer Use cache sharing skipped because pluginCacheMode is independent.");
|
|
253
|
-
if (params.config.marketplaceName || params.config.marketplacePath) return skippedCacheResult("explicit_marketplace", "Computer Use cache sharing skipped because an explicit marketplace is configured.");
|
|
224
|
+
if (!params.config.enabled || params.config.pluginCacheMode === "independent" || params.config.marketplaceName || params.config.marketplacePath) return false;
|
|
254
225
|
const bundledMarketplacePath = resolveComputerUseBundledMarketplacePath(params);
|
|
255
226
|
const sourcePluginRoot = path.join(bundledMarketplacePath, "plugins", params.config.pluginName);
|
|
256
227
|
const version = await readBundledPluginVersion(sourcePluginRoot);
|
|
257
|
-
if (!version) return
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
assertCurrent: params.assertCurrent,
|
|
267
|
-
forceRefresh: params.forceRefresh
|
|
268
|
-
}),
|
|
269
|
-
cachePath,
|
|
270
|
-
targetPath: sourcePluginRoot,
|
|
271
|
-
version,
|
|
272
|
-
removedStaleVersions: [],
|
|
273
|
-
warnings: [],
|
|
274
|
-
message: `Computer Use plugin cache ${cachePath} contains bundled plugin ${sourcePluginRoot}.`
|
|
275
|
-
};
|
|
228
|
+
if (!version) return false;
|
|
229
|
+
const cacheRoot = path.join(params.codexHome, "plugins", "cache", params.config.marketplaceName ?? DEFAULT_BUNDLED_MARKETPLACE_NAME, params.config.pluginName);
|
|
230
|
+
await ensureRealDirectoryCopy(path.join(cacheRoot, version), sourcePluginRoot, version, {
|
|
231
|
+
codexHome: params.codexHome,
|
|
232
|
+
ownershipRoot: params.ownershipRoot,
|
|
233
|
+
assertCurrent: params.assertCurrent,
|
|
234
|
+
forceRefresh: params.forceRefresh
|
|
235
|
+
});
|
|
236
|
+
return true;
|
|
276
237
|
}
|
|
277
238
|
function resolveComputerUseBundledMarketplacePath(params) {
|
|
278
239
|
return params.bundledMarketplacePath ?? resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath({ candidates: params.bundledMarketplacePathCandidates }) ?? params.bundledMarketplacePathCandidates?.[0] ?? DEFAULT_CODEX_COMPUTER_USE_BUNDLED_MARKETPLACE_PATH;
|
|
279
240
|
}
|
|
280
241
|
async function readBundledPluginVersion(sourcePluginRoot) {
|
|
281
242
|
const pluginJsonPath = path.join(sourcePluginRoot, ".codex-plugin", "plugin.json");
|
|
282
|
-
let raw;
|
|
283
|
-
try {
|
|
284
|
-
raw = await fs$1.readFile(pluginJsonPath, "utf8");
|
|
285
|
-
} catch {
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
243
|
try {
|
|
244
|
+
const raw = await fs$1.readFile(pluginJsonPath, "utf8");
|
|
289
245
|
const parsed = JSON.parse(raw);
|
|
290
246
|
return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : void 0;
|
|
291
247
|
} catch {
|
|
@@ -303,7 +259,13 @@ async function ensureRealDirectoryCopy(cachePath, sourcePluginRoot, version, bou
|
|
|
303
259
|
const physicalCachePath = ownedParent ? path.join(ownedParent.realPath, path.basename(cachePath)) : cachePath;
|
|
304
260
|
const stat = await fs$1.lstat(physicalCachePath).catch(() => void 0);
|
|
305
261
|
if (stat?.isDirectory() && !stat.isSymbolicLink()) {
|
|
306
|
-
if (await readBundledPluginVersion(physicalCachePath) === version && !boundary.forceRefresh)
|
|
262
|
+
if (await readBundledPluginVersion(physicalCachePath) === version && !boundary.forceRefresh) {
|
|
263
|
+
const [cachedMcp, sourceMcp] = await Promise.all([physicalCachePath, sourcePluginRoot].map(async (root) => fs$1.readFile(path.join(root, ".mcp.json"), "utf8").catch((error) => {
|
|
264
|
+
if (extractErrorCode(error) === "ENOENT") return;
|
|
265
|
+
throw error;
|
|
266
|
+
})));
|
|
267
|
+
if (cachedMcp === sourceMcp) return;
|
|
268
|
+
}
|
|
307
269
|
}
|
|
308
270
|
const cacheName = path.basename(cachePath);
|
|
309
271
|
const physicalCacheRoot = path.dirname(physicalCachePath);
|
|
@@ -312,7 +274,8 @@ async function ensureRealDirectoryCopy(cachePath, sourcePluginRoot, version, bou
|
|
|
312
274
|
const backupPath = path.join(physicalCacheRoot, `.${cacheName}.backup-${process.pid}-${Date.now()}`);
|
|
313
275
|
let backupCreated = false;
|
|
314
276
|
try {
|
|
315
|
-
await fs$1.
|
|
277
|
+
const physicalSourceRoot = await fs$1.realpath(sourcePluginRoot);
|
|
278
|
+
await fs$1.cp(physicalSourceRoot, stagedPath, { recursive: true });
|
|
316
279
|
await waitForCodexDesktopGeneration();
|
|
317
280
|
if (ownedParent) await assertDirectoryIdentityStable(ownedParent, "Computer Use plugin cache parent");
|
|
318
281
|
if (stat) {
|
|
@@ -341,7 +304,6 @@ async function ensureRealDirectoryCopy(cachePath, sourcePluginRoot, version, bou
|
|
|
341
304
|
force: true
|
|
342
305
|
});
|
|
343
306
|
}
|
|
344
|
-
return true;
|
|
345
307
|
} finally {
|
|
346
308
|
if (!ownedParent || await directoryIdentityIsStable(ownedParent)) await fs$1.rm(stagingRoot, {
|
|
347
309
|
recursive: true,
|
|
@@ -349,19 +311,148 @@ async function ensureRealDirectoryCopy(cachePath, sourcePluginRoot, version, bou
|
|
|
349
311
|
});
|
|
350
312
|
}
|
|
351
313
|
}
|
|
352
|
-
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region extensions/codex/src/app-server/computer-use-unified.ts
|
|
316
|
+
/** Materializes the desktop-owned unified MCP template for an isolated Computer Use home. */
|
|
317
|
+
const UNIFIED_COMPUTER_USE_PLUGIN = "unified-computer-use";
|
|
318
|
+
const UNIFIED_SERVER = "cua_repl";
|
|
319
|
+
const CUA_REPL_ENTRY = "@oai/cua/tinyskyAlt";
|
|
320
|
+
/** The selected desktop distribution owns both the plugin and its executable dependencies. */
|
|
321
|
+
async function resolveCodexUnifiedComputerUseRuntime(candidate, codexHome, requestedPluginName, requestedServerName) {
|
|
322
|
+
const pluginName = requestedPluginName ?? "computer-use";
|
|
323
|
+
const serverName = requestedServerName ?? pluginName;
|
|
324
|
+
if (!(pluginName === "computer-use" && serverName === "computer-use") && !(pluginName === "unified-computer-use" && serverName === UNIFIED_SERVER)) return;
|
|
325
|
+
if ((await readObject(path.join(candidate.bundledMarketplacePath, "plugins", "computer-use", ".codex-plugin", "plugin.json")))?.mcpServers && requestedPluginName !== "unified-computer-use") return;
|
|
326
|
+
const pluginRoot = path.join(candidate.bundledMarketplacePath, "plugins", UNIFIED_COMPUTER_USE_PLUGIN);
|
|
327
|
+
const manifest = await readObject(path.join(pluginRoot, ".codex-plugin", "plugin.json"));
|
|
328
|
+
if (!manifest || manifest.name !== "unified-computer-use" || manifest.mcpServers !== "./.mcp.json") return;
|
|
329
|
+
const template = await readObject(path.join(pluginRoot, ".mcp.json"));
|
|
330
|
+
const servers = template?.mcpServers;
|
|
331
|
+
const server = isRecord(servers) ? servers[UNIFIED_SERVER] : void 0;
|
|
332
|
+
if (!template || !isRecord(servers) || !isRecord(server)) throw new Error("The selected desktop's unified Computer Use MCP template is invalid.");
|
|
333
|
+
const runtimeRoot = path.join(candidate.appBundlePath, "Contents", "Resources", "cua_node");
|
|
334
|
+
const modules = path.join(runtimeRoot, "lib", "node_modules");
|
|
335
|
+
const node = path.join(runtimeRoot, "bin", "node");
|
|
336
|
+
const nodeRepl = path.join(runtimeRoot, "bin", "node_repl");
|
|
337
|
+
const launcher = path.join(modules, "@oai", "cua-repl", "bin", "cua-repl.mjs");
|
|
338
|
+
const cuaRoot = path.join(modules, "@oai", "cua");
|
|
339
|
+
const cuaPackage = await readObject(path.join(cuaRoot, "package.json"));
|
|
340
|
+
const cuaEntry = isRecord(cuaPackage?.exports) ? cuaPackage.exports["./tinyskyAlt"] : void 0;
|
|
341
|
+
try {
|
|
342
|
+
if (typeof cuaEntry !== "string" || !cuaEntry.startsWith("./") || path.relative(cuaRoot, path.resolve(cuaRoot, cuaEntry)).startsWith("..")) throw new Error("The desktop CUA package does not expose its unified entry point.");
|
|
343
|
+
await Promise.all([
|
|
344
|
+
fs$1.access(node, constants.X_OK),
|
|
345
|
+
fs$1.access(nodeRepl, constants.X_OK),
|
|
346
|
+
fs$1.access(launcher, constants.R_OK),
|
|
347
|
+
fs$1.access(path.join(modules, "@oai", "sky", "package.json"), constants.R_OK),
|
|
348
|
+
fs$1.access(path.resolve(cuaRoot, cuaEntry), constants.R_OK)
|
|
349
|
+
]);
|
|
350
|
+
} catch (cause) {
|
|
351
|
+
throw new Error("The selected desktop's unified Computer Use runtime is incomplete.", { cause });
|
|
352
|
+
}
|
|
353
353
|
return {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
354
|
+
pluginRoot,
|
|
355
|
+
sourceFingerprint: await readCodexDesktopArtifactTreeFingerprint(pluginRoot),
|
|
356
|
+
manifest,
|
|
357
|
+
mcp: {
|
|
358
|
+
...template,
|
|
359
|
+
mcpServers: {
|
|
360
|
+
...servers,
|
|
361
|
+
[UNIFIED_SERVER]: {
|
|
362
|
+
...server,
|
|
363
|
+
command: node,
|
|
364
|
+
args: [launcher],
|
|
365
|
+
enabled: true,
|
|
366
|
+
env_vars: [],
|
|
367
|
+
env: {
|
|
368
|
+
CODEX_HOME: codexHome,
|
|
369
|
+
CUA_REPL_NODE_REPL_PATH: nodeRepl,
|
|
370
|
+
CUA_REPL_ENABLED_SURFACES: "computer",
|
|
371
|
+
NODE_REPL_NODE_PATH: node,
|
|
372
|
+
NODE_REPL_NODE_MODULE_DIRS: modules,
|
|
373
|
+
NODE_REPL_TRUSTED_CODE_PATHS: [codexHome, modules].join(path.delimiter),
|
|
374
|
+
NODE_REPL_TRUSTED_SERVICES: JSON.stringify({ sky: "@oai/sky/service" }),
|
|
375
|
+
NODE_REPL_JS_BANNER: `await import(${JSON.stringify(CUA_REPL_ENTRY)});`,
|
|
376
|
+
SKY_CUA_SERVICE_PATH: path.join(codexHome, "computer-use", "Codex Computer Use.app")
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
async function publishCodexUnifiedComputerUsePlugin(target, runtime) {
|
|
384
|
+
const templatePath = path.join(runtime.pluginRoot, ".mcp.json");
|
|
385
|
+
await fs$1.cp(runtime.pluginRoot, target, {
|
|
386
|
+
recursive: true,
|
|
387
|
+
filter: (source) => source !== templatePath
|
|
388
|
+
});
|
|
389
|
+
await fs$1.writeFile(path.join(target, ".mcp.json"), `${JSON.stringify(runtime.mcp, null, 2)}\n`, { mode: 384 });
|
|
390
|
+
}
|
|
391
|
+
async function codexUnifiedComputerUsePluginMatches(target, runtime) {
|
|
392
|
+
return isDeepStrictEqual(await readObject(path.join(target, ".codex-plugin", "plugin.json")), runtime.manifest) && isDeepStrictEqual(await readObject(path.join(target, ".mcp.json")), runtime.mcp);
|
|
393
|
+
}
|
|
394
|
+
/** Preserve custom identities and legacy desktops; the legacy default follows its replacement. */
|
|
395
|
+
async function resolveManagedCodexComputerUseConfig(config, marketplacePath) {
|
|
396
|
+
if (!marketplacePath || config.marketplaceSource || config.marketplaceName || config.marketplacePath || config.pluginName !== "computer-use" || config.mcpServerName !== "computer-use") return config;
|
|
397
|
+
const plugins = path.join(marketplacePath, "plugins");
|
|
398
|
+
const legacy = await readObject(path.join(plugins, "computer-use", ".codex-plugin", "plugin.json"));
|
|
399
|
+
if (!legacy || legacy.mcpServers) return config;
|
|
400
|
+
const unified = await readObject(path.join(plugins, UNIFIED_COMPUTER_USE_PLUGIN, ".codex-plugin", "plugin.json"));
|
|
401
|
+
const mcp = await readObject(path.join(plugins, UNIFIED_COMPUTER_USE_PLUGIN, ".mcp.json"));
|
|
402
|
+
const server = isRecord(mcp?.mcpServers) ? mcp.mcpServers[UNIFIED_SERVER] : void 0;
|
|
403
|
+
if (unified?.name !== "unified-computer-use" || !isRecord(server) || server.enabled !== true) return config;
|
|
404
|
+
return {
|
|
405
|
+
...config,
|
|
406
|
+
pluginName: UNIFIED_COMPUTER_USE_PLUGIN,
|
|
407
|
+
mcpServerName: UNIFIED_SERVER
|
|
359
408
|
};
|
|
360
409
|
}
|
|
410
|
+
/** A native plugin disable veto must survive an automatic identity replacement. */
|
|
411
|
+
function isLegacyCodexComputerUsePluginDisabled(config) {
|
|
412
|
+
const plugin = isRecord(config) && isRecord(config.plugins) ? config.plugins["computer-use@openai-bundled"] : void 0;
|
|
413
|
+
return isRecord(plugin) && plugin.enabled === false;
|
|
414
|
+
}
|
|
415
|
+
/** Renaming a server must not discard an operator's legacy server or tool restrictions. */
|
|
416
|
+
function hasLegacyCodexComputerUseMcpPolicy(config) {
|
|
417
|
+
if (!isRecord(config)) return false;
|
|
418
|
+
if (isRecord(config.mcp_servers) && Object.hasOwn(config.mcp_servers, "computer-use")) return true;
|
|
419
|
+
const plugin = isRecord(config.plugins) ? config.plugins["computer-use@openai-bundled"] : void 0;
|
|
420
|
+
return isRecord(plugin) && isRecord(plugin.mcp_servers) && Object.keys(plugin.mcp_servers).length > 0;
|
|
421
|
+
}
|
|
422
|
+
async function readObject(file) {
|
|
423
|
+
try {
|
|
424
|
+
const value = JSON.parse(await fs$1.readFile(file, "utf8"));
|
|
425
|
+
return isRecord(value) ? value : void 0;
|
|
426
|
+
} catch {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/** Publish the cache from the same prepared source that native plugin/install consumes. */
|
|
431
|
+
async function reconcileManagedCodexComputerUseCache(params) {
|
|
432
|
+
const config = params.config;
|
|
433
|
+
params.assertCurrent();
|
|
434
|
+
const bundledMarketplacePath = params.managedMarketplacePath ?? params.bundledMarketplacePath;
|
|
435
|
+
const cacheBinding = [
|
|
436
|
+
params.epoch ?? "manual",
|
|
437
|
+
bundledMarketplacePath ?? "default",
|
|
438
|
+
config.pluginName
|
|
439
|
+
].join("\0");
|
|
440
|
+
const shared = await ensureCodexComputerUseSharedPluginCache({
|
|
441
|
+
codexHome: params.codexHome,
|
|
442
|
+
config,
|
|
443
|
+
...params.ownershipRoot ? { ownershipRoot: params.ownershipRoot } : {},
|
|
444
|
+
...bundledMarketplacePath ? { bundledMarketplacePath } : {},
|
|
445
|
+
assertCurrent: params.assertCurrent,
|
|
446
|
+
forceRefresh: params.forceRefresh === true || params.previousCacheBinding !== cacheBinding
|
|
447
|
+
});
|
|
448
|
+
params.assertCurrent();
|
|
449
|
+
return shared ? cacheBinding : void 0;
|
|
450
|
+
}
|
|
361
451
|
//#endregion
|
|
362
452
|
//#region extensions/codex/src/app-server/computer-use-marketplace.ts
|
|
363
453
|
/** Managed local wrapper for Codex's reserved bundled marketplace. */
|
|
364
454
|
const MARKETPLACE_NAME = "openai-bundled";
|
|
455
|
+
const UNIFIED_SOURCE_FINGERPRINT = ".openclaw-unified-source-fingerprint";
|
|
365
456
|
const activeInstalls$1 = /* @__PURE__ */ new Map();
|
|
366
457
|
function resolveCodexManagedBundledMarketplacePath(codexHome) {
|
|
367
458
|
return path.join(codexHome, ".tmp", "bundled-marketplaces", MARKETPLACE_NAME);
|
|
@@ -373,6 +464,7 @@ async function ensureCodexManagedBundledMarketplace(params) {
|
|
|
373
464
|
candidates
|
|
374
465
|
});
|
|
375
466
|
if (!source) return;
|
|
467
|
+
const unifiedRuntime = await resolveCodexUnifiedComputerUseRuntime(source, params.codexHome, params.computerUsePluginName, params.computerUseMcpServerName);
|
|
376
468
|
const parentPath = path.dirname(resolveCodexManagedBundledMarketplacePath(params.codexHome));
|
|
377
469
|
const targetPath = path.join(parentPath, MARKETPLACE_NAME);
|
|
378
470
|
const parent = await prepareOwnedServiceParent({
|
|
@@ -388,11 +480,12 @@ async function ensureCodexManagedBundledMarketplace(params) {
|
|
|
388
480
|
await active.promise.catch(() => void 0);
|
|
389
481
|
return await ensureCodexManagedBundledMarketplace(params);
|
|
390
482
|
}
|
|
391
|
-
const install =
|
|
483
|
+
const install = publishManagedWrapper({
|
|
392
484
|
parent,
|
|
393
485
|
physicalTargetPath,
|
|
394
486
|
targetPath,
|
|
395
487
|
source,
|
|
488
|
+
unifiedRuntime,
|
|
396
489
|
ownershipCandidates: params.ownershipCandidates ?? candidates,
|
|
397
490
|
assertCurrent: params.assertCurrent
|
|
398
491
|
});
|
|
@@ -407,12 +500,9 @@ async function ensureCodexManagedBundledMarketplace(params) {
|
|
|
407
500
|
install.then(clearActive, clearActive);
|
|
408
501
|
return await install;
|
|
409
502
|
}
|
|
410
|
-
async function reconcileManagedWrapper(params) {
|
|
411
|
-
if (await wrapperMatches(params.physicalTargetPath, params.source.bundledMarketplacePath)) return params.targetPath;
|
|
412
|
-
return await publishManagedWrapper(params);
|
|
413
|
-
}
|
|
414
503
|
async function publishManagedWrapper(params) {
|
|
415
504
|
const { parent, physicalTargetPath, targetPath, source, ownershipCandidates, assertCurrent } = params;
|
|
505
|
+
if (await wrapperMatches(physicalTargetPath, source.bundledMarketplacePath, params.unifiedRuntime)) return targetPath;
|
|
416
506
|
const stagingPath = await fs$1.mkdtemp(path.join(parent.realPath, `.${MARKETPLACE_NAME}.staging-`));
|
|
417
507
|
const backupPath = path.join(parent.realPath, `.${MARKETPLACE_NAME}.backup-${process.pid}-${Date.now()}`);
|
|
418
508
|
let backupCreated = false;
|
|
@@ -422,10 +512,22 @@ async function publishManagedWrapper(params) {
|
|
|
422
512
|
recursive: true,
|
|
423
513
|
mode: 448
|
|
424
514
|
});
|
|
425
|
-
await
|
|
515
|
+
await fs$1.symlink(path.join(source.bundledMarketplacePath, ".agents", "plugins", "marketplace.json"), path.join(manifestParent, "marketplace.json"));
|
|
516
|
+
if (params.unifiedRuntime) {
|
|
517
|
+
const pluginsPath = path.join(stagingPath, "plugins");
|
|
518
|
+
await fs$1.mkdir(pluginsPath);
|
|
519
|
+
for (const entry of await fs$1.readdir(path.join(source.bundledMarketplacePath, "plugins"))) {
|
|
520
|
+
const target = path.join(pluginsPath, entry);
|
|
521
|
+
if (entry === "unified-computer-use") await publishCodexUnifiedComputerUsePlugin(target, params.unifiedRuntime);
|
|
522
|
+
else await fs$1.symlink(path.join(source.bundledMarketplacePath, "plugins", entry), target);
|
|
523
|
+
}
|
|
524
|
+
await fs$1.writeFile(path.join(stagingPath, UNIFIED_SOURCE_FINGERPRINT), params.unifiedRuntime.sourceFingerprint, { mode: 384 });
|
|
525
|
+
} else await fs$1.symlink(path.join(source.bundledMarketplacePath, "plugins"), path.join(stagingPath, "plugins"));
|
|
526
|
+
await waitForCodexDesktopGeneration();
|
|
527
|
+
assertCurrent?.();
|
|
426
528
|
await assertDirectoryIdentityStable(parent, "managed bundled marketplace parent");
|
|
427
529
|
const existing = await fs$1.lstat(physicalTargetPath).catch((error) => {
|
|
428
|
-
if (
|
|
530
|
+
if (extractErrorCode(error) === "ENOENT") return;
|
|
429
531
|
throw error;
|
|
430
532
|
});
|
|
431
533
|
if (existing && !existing.isDirectory()) throw new Error(`Managed bundled marketplace must be a real directory: ${targetPath}`);
|
|
@@ -448,7 +550,7 @@ async function publishManagedWrapper(params) {
|
|
|
448
550
|
if (backupCreated) try {
|
|
449
551
|
await assertDirectoryIdentityStable(parent, "managed bundled marketplace parent");
|
|
450
552
|
if (await fs$1.lstat(physicalTargetPath).catch(() => void 0)) {
|
|
451
|
-
if (!await
|
|
553
|
+
if (!await wrapperOwnedBySource(physicalTargetPath, source.bundledMarketplacePath)) throw new Error("managed bundled marketplace replacement is no longer owned", { cause: error });
|
|
452
554
|
await fs$1.rm(physicalTargetPath, { recursive: true });
|
|
453
555
|
}
|
|
454
556
|
await fs$1.rename(backupPath, physicalTargetPath);
|
|
@@ -479,22 +581,51 @@ async function isExpectedMarketplace(root) {
|
|
|
479
581
|
return false;
|
|
480
582
|
}
|
|
481
583
|
}
|
|
482
|
-
async function wrapperMatches(targetPath, sourcePath) {
|
|
584
|
+
async function wrapperMatches(targetPath, sourcePath, unifiedRuntime) {
|
|
585
|
+
if (!await wrapperOwnedBySource(targetPath, sourcePath)) return false;
|
|
586
|
+
const plugins = await fs$1.lstat(path.join(targetPath, "plugins"));
|
|
587
|
+
if (unifiedRuntime && plugins.isDirectory()) {
|
|
588
|
+
const [sourceEntries, targetEntries] = await Promise.all([fs$1.readdir(path.join(sourcePath, "plugins")), fs$1.readdir(path.join(targetPath, "plugins"))]);
|
|
589
|
+
const sourceNames = new Set(sourceEntries);
|
|
590
|
+
if (sourceNames.size !== targetEntries.length || targetEntries.some((name) => !sourceNames.has(name))) return false;
|
|
591
|
+
}
|
|
592
|
+
return unifiedRuntime ? plugins.isDirectory() && await fs$1.readFile(path.join(targetPath, UNIFIED_SOURCE_FINGERPRINT), "utf8").catch(() => void 0) === unifiedRuntime.sourceFingerprint && await codexUnifiedComputerUsePluginMatches(path.join(targetPath, "plugins", "unified-computer-use"), unifiedRuntime) : plugins.isSymbolicLink();
|
|
593
|
+
}
|
|
594
|
+
async function wrapperOwnedBySource(targetPath, sourcePath) {
|
|
483
595
|
try {
|
|
484
596
|
const target = await fs$1.lstat(targetPath);
|
|
485
597
|
if (!target.isDirectory() || target.isSymbolicLink()) return false;
|
|
486
|
-
|
|
487
|
-
|
|
598
|
+
if (await fs$1.readlink(path.join(targetPath, ".agents", "plugins", "marketplace.json")) !== path.join(sourcePath, ".agents", "plugins", "marketplace.json")) return false;
|
|
599
|
+
const pluginsPath = path.join(targetPath, "plugins");
|
|
600
|
+
const plugins = await fs$1.lstat(pluginsPath);
|
|
601
|
+
if (plugins.isSymbolicLink()) return await fs$1.readlink(pluginsPath) === path.join(sourcePath, "plugins");
|
|
602
|
+
if (!plugins.isDirectory()) return false;
|
|
603
|
+
let hasUnifiedPlugin = false;
|
|
604
|
+
for (const entry of await fs$1.readdir(pluginsPath, { withFileTypes: true })) if (entry.name === "unified-computer-use" && entry.isDirectory()) {
|
|
605
|
+
const plugin = JSON.parse(await fs$1.readFile(path.join(pluginsPath, entry.name, ".codex-plugin", "plugin.json"), "utf8"));
|
|
606
|
+
if (!isRecord(plugin) || plugin.name !== "unified-computer-use") return false;
|
|
607
|
+
hasUnifiedPlugin = true;
|
|
608
|
+
} else if (!entry.isSymbolicLink() || await fs$1.readlink(path.join(pluginsPath, entry.name)) !== path.join(sourcePath, "plugins", entry.name)) return false;
|
|
609
|
+
return hasUnifiedPlugin;
|
|
488
610
|
} catch {
|
|
489
611
|
return false;
|
|
490
612
|
}
|
|
491
613
|
}
|
|
492
614
|
async function wrapperMatchesAnySource(targetPath, candidates) {
|
|
493
|
-
for (const candidate of candidates) if (await
|
|
615
|
+
for (const candidate of candidates) if (await wrapperOwnedBySource(targetPath, candidate.bundledMarketplacePath)) return true;
|
|
494
616
|
return false;
|
|
495
617
|
}
|
|
496
|
-
function
|
|
497
|
-
|
|
618
|
+
async function resolveClientManagedBundledMarketplacePath(codexHome, agentDir) {
|
|
619
|
+
if (!codexHome || !agentDir) return;
|
|
620
|
+
const [actualRealHome, expectedRealHome] = await Promise.all([fs$1.realpath(codexHome).catch(() => void 0), fs$1.realpath(resolveCodexAppServerHomeDir(agentDir)).catch(() => void 0)]);
|
|
621
|
+
if (!actualRealHome || actualRealHome !== expectedRealHome) return;
|
|
622
|
+
const managedPath = resolveCodexManagedBundledMarketplacePath(codexHome);
|
|
623
|
+
return existsSync(managedPath) ? managedPath : void 0;
|
|
624
|
+
}
|
|
625
|
+
function resolveBundledComputerUseMarketplacePath(params) {
|
|
626
|
+
if (params.defaultBundledMarketplacePath) return existsSync(params.defaultBundledMarketplacePath) ? params.defaultBundledMarketplacePath : void 0;
|
|
627
|
+
if (!params.defaultBundledMarketplacePathCandidates) return;
|
|
628
|
+
return resolveFirstExistingMacOSDesktopCodexBundledMarketplacePath({ candidates: params.defaultBundledMarketplacePathCandidates });
|
|
498
629
|
}
|
|
499
630
|
//#endregion
|
|
500
631
|
//#region extensions/codex/src/app-server/computer-use-service.ts
|
|
@@ -546,7 +677,6 @@ async function ensureCodexComputerUseServiceApp(params) {
|
|
|
546
677
|
ownershipRoot,
|
|
547
678
|
targetParent,
|
|
548
679
|
targetPath,
|
|
549
|
-
platform,
|
|
550
680
|
sourceAppCandidates: candidates
|
|
551
681
|
});
|
|
552
682
|
const activeEntry = {
|
|
@@ -562,13 +692,20 @@ async function ensureCodexComputerUseServiceApp(params) {
|
|
|
562
692
|
}
|
|
563
693
|
async function ensureCodexComputerUseServiceAppOnce(params) {
|
|
564
694
|
const inspectServiceApp = params.inspectServiceApp ?? inspectTrustedServiceApp;
|
|
565
|
-
const source = await findUsableServiceApp(params.sourceAppCandidates
|
|
695
|
+
const source = await findUsableServiceApp(params.sourceAppCandidates, inspectServiceApp);
|
|
566
696
|
if (!source) return {
|
|
567
697
|
status: "source_missing",
|
|
568
698
|
changed: false,
|
|
569
699
|
targetPath: params.targetPath
|
|
570
700
|
};
|
|
571
701
|
const { path: sourcePath, identity: sourceIdentity } = source;
|
|
702
|
+
const alreadyCurrent = {
|
|
703
|
+
status: "already_current",
|
|
704
|
+
changed: false,
|
|
705
|
+
targetPath: params.targetPath,
|
|
706
|
+
sourcePath,
|
|
707
|
+
sourceBuild: sourceIdentity.build
|
|
708
|
+
};
|
|
572
709
|
const ownedParent = await prepareOwnedServiceParent({
|
|
573
710
|
ownershipRoot: params.ownershipRoot,
|
|
574
711
|
codexHome: params.codexHome,
|
|
@@ -577,19 +714,29 @@ async function ensureCodexComputerUseServiceAppOnce(params) {
|
|
|
577
714
|
const operationTargetPath = path.join(ownedParent.realPath, SERVICE_APP_NAME);
|
|
578
715
|
await assertNotSymlink(operationTargetPath, "Computer Use service target");
|
|
579
716
|
const initialTarget = await readServiceAppSnapshot(operationTargetPath, inspectServiceApp);
|
|
580
|
-
if (initialTarget.identity && identitiesMatch(initialTarget.identity, sourceIdentity)) return
|
|
581
|
-
status: "already_current",
|
|
582
|
-
changed: false,
|
|
583
|
-
targetPath: params.targetPath,
|
|
584
|
-
sourcePath,
|
|
585
|
-
sourceBuild: sourceIdentity.build
|
|
586
|
-
};
|
|
717
|
+
if (initialTarget.identity && identitiesMatch(initialTarget.identity, sourceIdentity)) return alreadyCurrent;
|
|
587
718
|
await assertOwnedServiceParentStable(ownedParent);
|
|
588
719
|
const stagingRoot = await fs$1.mkdtemp(path.join(ownedParent.realPath, ".service-app.staging-"));
|
|
589
720
|
const stagingRootIdentity = await readRealDirectoryIdentity(stagingRoot, "Computer Use service staging directory");
|
|
590
721
|
const stagedPath = path.join(stagingRoot, SERVICE_APP_NAME);
|
|
591
722
|
const backupPath = path.join(ownedParent.realPath, `.service-app.backup-${process.pid}-${Date.now()}`);
|
|
592
723
|
let backupCreated = false;
|
|
724
|
+
const restoreBackup = async () => {
|
|
725
|
+
await assertOwnedServiceParentStable(ownedParent);
|
|
726
|
+
await fs$1.rename(backupPath, operationTargetPath);
|
|
727
|
+
await assertOwnedServiceParentStable(ownedParent);
|
|
728
|
+
backupCreated = false;
|
|
729
|
+
};
|
|
730
|
+
const removeBackup = async () => {
|
|
731
|
+
await assertOwnedServiceParentStable(ownedParent);
|
|
732
|
+
await assertNotSymlink(backupPath, "Computer Use service backup");
|
|
733
|
+
await fs$1.rm(backupPath, {
|
|
734
|
+
recursive: true,
|
|
735
|
+
force: true
|
|
736
|
+
});
|
|
737
|
+
await assertOwnedServiceParentStable(ownedParent);
|
|
738
|
+
backupCreated = false;
|
|
739
|
+
};
|
|
593
740
|
try {
|
|
594
741
|
await (params.copyServiceApp ?? copyServiceAppWithDitto)(sourcePath, stagedPath);
|
|
595
742
|
await assertOwnedServiceParentStable(ownedParent);
|
|
@@ -612,23 +759,11 @@ async function ensureCodexComputerUseServiceAppOnce(params) {
|
|
|
612
759
|
backupCreated = true;
|
|
613
760
|
const movedTarget = await readServiceAppSnapshot(backupPath, inspectServiceApp);
|
|
614
761
|
if (movedTarget.identity && identitiesMatch(movedTarget.identity, sourceIdentity)) {
|
|
615
|
-
await
|
|
616
|
-
|
|
617
|
-
await assertOwnedServiceParentStable(ownedParent);
|
|
618
|
-
backupCreated = false;
|
|
619
|
-
return {
|
|
620
|
-
status: "already_current",
|
|
621
|
-
changed: false,
|
|
622
|
-
targetPath: params.targetPath,
|
|
623
|
-
sourcePath,
|
|
624
|
-
sourceBuild: sourceIdentity.build
|
|
625
|
-
};
|
|
762
|
+
await restoreBackup();
|
|
763
|
+
return alreadyCurrent;
|
|
626
764
|
}
|
|
627
765
|
if (!snapshotsMatch(initialTarget, movedTarget)) {
|
|
628
|
-
await
|
|
629
|
-
await fs$1.rename(backupPath, operationTargetPath);
|
|
630
|
-
await assertOwnedServiceParentStable(ownedParent);
|
|
631
|
-
backupCreated = false;
|
|
766
|
+
await restoreBackup();
|
|
632
767
|
throw new Error("Computer Use service target changed to an unexpected generation during refresh.");
|
|
633
768
|
}
|
|
634
769
|
}
|
|
@@ -642,33 +777,11 @@ async function ensureCodexComputerUseServiceAppOnce(params) {
|
|
|
642
777
|
await assertNotSymlink(operationTargetPath, "Computer Use service target");
|
|
643
778
|
const winnerIdentity = await inspectServiceApp(operationTargetPath);
|
|
644
779
|
if (!winnerIdentity || !identitiesMatch(winnerIdentity, sourceIdentity)) {
|
|
645
|
-
if (backupCreated)
|
|
646
|
-
if (!await pathExists$1(operationTargetPath)) {
|
|
647
|
-
await assertOwnedServiceParentStable(ownedParent);
|
|
648
|
-
await fs$1.rename(backupPath, operationTargetPath);
|
|
649
|
-
await assertOwnedServiceParentStable(ownedParent);
|
|
650
|
-
backupCreated = false;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
780
|
+
if (backupCreated && !await pathExists$1(operationTargetPath)) await restoreBackup();
|
|
653
781
|
throw error;
|
|
654
782
|
}
|
|
655
|
-
if (backupCreated)
|
|
656
|
-
|
|
657
|
-
await assertNotSymlink(backupPath, "Computer Use service backup");
|
|
658
|
-
await fs$1.rm(backupPath, {
|
|
659
|
-
recursive: true,
|
|
660
|
-
force: true
|
|
661
|
-
});
|
|
662
|
-
await assertOwnedServiceParentStable(ownedParent);
|
|
663
|
-
backupCreated = false;
|
|
664
|
-
}
|
|
665
|
-
return {
|
|
666
|
-
status: "already_current",
|
|
667
|
-
changed: false,
|
|
668
|
-
targetPath: params.targetPath,
|
|
669
|
-
sourcePath,
|
|
670
|
-
sourceBuild: sourceIdentity.build
|
|
671
|
-
};
|
|
783
|
+
if (backupCreated) await removeBackup();
|
|
784
|
+
return alreadyCurrent;
|
|
672
785
|
}
|
|
673
786
|
await assertNotSymlink(operationTargetPath, "Installed Computer Use service app");
|
|
674
787
|
const installedSnapshot = await readServiceAppSnapshot(operationTargetPath, inspectServiceApp);
|
|
@@ -688,16 +801,7 @@ async function ensureCodexComputerUseServiceAppOnce(params) {
|
|
|
688
801
|
}
|
|
689
802
|
throw new Error("Installed Computer Use service app failed post-install identity verification.");
|
|
690
803
|
}
|
|
691
|
-
if (backupCreated)
|
|
692
|
-
await assertOwnedServiceParentStable(ownedParent);
|
|
693
|
-
await assertNotSymlink(backupPath, "Computer Use service backup");
|
|
694
|
-
await fs$1.rm(backupPath, {
|
|
695
|
-
recursive: true,
|
|
696
|
-
force: true
|
|
697
|
-
});
|
|
698
|
-
await assertOwnedServiceParentStable(ownedParent);
|
|
699
|
-
backupCreated = false;
|
|
700
|
-
}
|
|
804
|
+
if (backupCreated) await removeBackup();
|
|
701
805
|
return {
|
|
702
806
|
status: initialTarget.exists ? "refreshed" : "installed",
|
|
703
807
|
changed: true,
|
|
@@ -707,14 +811,14 @@ async function ensureCodexComputerUseServiceAppOnce(params) {
|
|
|
707
811
|
...initialTarget.identity ? { previousBuild: initialTarget.identity.build } : {}
|
|
708
812
|
};
|
|
709
813
|
} catch (error) {
|
|
710
|
-
if (backupCreated && await
|
|
814
|
+
if (backupCreated && await directoryIdentityIsStable(ownedParent) && !await pathExists$1(operationTargetPath)) {
|
|
711
815
|
await assertOwnedServiceParentStable(ownedParent);
|
|
712
816
|
await fs$1.rename(backupPath, operationTargetPath);
|
|
713
817
|
backupCreated = false;
|
|
714
818
|
}
|
|
715
819
|
throw error;
|
|
716
820
|
} finally {
|
|
717
|
-
if (await
|
|
821
|
+
if (await directoryIdentityIsStable(ownedParent) && await directoryIdentityIsStable(stagingRootIdentity)) await fs$1.rm(stagingRoot, {
|
|
718
822
|
recursive: true,
|
|
719
823
|
force: true
|
|
720
824
|
});
|
|
@@ -761,18 +865,10 @@ function filesystemSnapshotsMatch(left, right) {
|
|
|
761
865
|
function identitiesMatch(left, right) {
|
|
762
866
|
return left.bundleId === right.bundleId && left.version === right.version && left.build === right.build && left.cdHash === right.cdHash && left.teamId === right.teamId && left.clientBundleId === right.clientBundleId && left.clientCdHash === right.clientCdHash && left.clientTeamId === right.clientTeamId;
|
|
763
867
|
}
|
|
764
|
-
async function hasExecutableClient(appPath) {
|
|
765
|
-
try {
|
|
766
|
-
await fs$1.access(path.join(appPath, CLIENT_RELATIVE_PATH), constants.X_OK);
|
|
767
|
-
return true;
|
|
768
|
-
} catch {
|
|
769
|
-
return false;
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
868
|
async function inspectTrustedServiceApp(appPath) {
|
|
773
|
-
if (!await hasExecutableClient(appPath)) return;
|
|
774
869
|
const clientAppPath = path.join(appPath, CLIENT_APP_RELATIVE_PATH);
|
|
775
870
|
try {
|
|
871
|
+
await fs$1.access(path.join(appPath, CLIENT_RELATIVE_PATH), constants.X_OK);
|
|
776
872
|
await verifyTrustedBundle(appPath, SERVICE_BUNDLE_ID, true);
|
|
777
873
|
await verifyTrustedBundle(clientAppPath, CLIENT_BUNDLE_ID, false);
|
|
778
874
|
const [info, serviceSignature, clientSignature] = await Promise.all([
|
|
@@ -880,7 +976,10 @@ const scopedOAuthRefreshQueues = /* @__PURE__ */ new WeakMap();
|
|
|
880
976
|
async function bridgeCodexAppServerStartOptions(params) {
|
|
881
977
|
if (params.startOptions.transport !== "stdio") return params.startOptions;
|
|
882
978
|
const scopeStartOptions = () => withCodexHomeEnvironment(withEphemeralCodexAuthStore(params), params.agentDir);
|
|
883
|
-
if (params.preparedAuth)
|
|
979
|
+
if (params.preparedAuth) {
|
|
980
|
+
const scopedStartOptions = await scopeStartOptions();
|
|
981
|
+
return withClearedEnvironmentVariables(scopedStartOptions, CODEX_APP_SERVER_PREPARED_AUTH_ENV_VARS);
|
|
982
|
+
}
|
|
884
983
|
if (params.authProfileId === null) return scopeStartOptions();
|
|
885
984
|
const store = resolveCodexAppServerAuthProfileStore({
|
|
886
985
|
agentDir: params.agentDir,
|
|
@@ -913,8 +1012,7 @@ function resolveUnimportedAgentCodexAuthMessage(params) {
|
|
|
913
1012
|
const targetAgentId = params.agentId?.trim() || "<agent-id>";
|
|
914
1013
|
return `A Codex auth file exists at ${authPath}, but agent-scoped Codex runs use OpenClaw's auth store and do not read that file. Preview only that credential import with \`openclaw migrate plan codex --from <codex-home> --agent ${targetAgentId} --include-secrets --item auth:openai\`, then run \`openclaw migrate apply codex --from <codex-home> --agent ${targetAgentId} --include-secrets --item auth:openai --yes\`. If the plan finds no credentials, remove the stale auth file.`;
|
|
915
1014
|
}
|
|
916
|
-
|
|
917
|
-
async function resolveCodexAppServerPreparedAuthProfileSnapshot(params) {
|
|
1015
|
+
function resolveCodexAppServerAuthProfile(params) {
|
|
918
1016
|
const agentDir = params.agentDir?.trim() || resolveDefaultAgentDir(params.config ?? {});
|
|
919
1017
|
const store = resolveCodexAppServerAuthProfileStore({
|
|
920
1018
|
agentDir,
|
|
@@ -929,7 +1027,33 @@ async function resolveCodexAppServerPreparedAuthProfileSnapshot(params) {
|
|
|
929
1027
|
});
|
|
930
1028
|
if (!profileId) return;
|
|
931
1029
|
const credential = store.profiles[profileId];
|
|
932
|
-
if (!credential || !
|
|
1030
|
+
if (!credential || !isCodexAppServerAuthProvider(credential.provider)) return;
|
|
1031
|
+
return {
|
|
1032
|
+
agentDir,
|
|
1033
|
+
store,
|
|
1034
|
+
profileId,
|
|
1035
|
+
credential
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
/** Resolves prepared profile login material once so cache identity and RPC login cannot drift. */
|
|
1039
|
+
async function resolveCodexAppServerPreparedAuthProfileSnapshot(params) {
|
|
1040
|
+
const profile = resolveCodexAppServerAuthProfile(params);
|
|
1041
|
+
if (!profile) return;
|
|
1042
|
+
const { agentDir, store, profileId, credential } = profile;
|
|
1043
|
+
if (isCodexResponsesOAuthCredential(credential)) return {
|
|
1044
|
+
inferenceAuth: "host-oauth",
|
|
1045
|
+
loginParams: {
|
|
1046
|
+
type: "apiKey",
|
|
1047
|
+
apiKey: `openclaw-local-${randomUUID()}`
|
|
1048
|
+
},
|
|
1049
|
+
secretFreeCacheKey: await resolveCodexResponsesOAuthProfileFingerprint({
|
|
1050
|
+
profileId,
|
|
1051
|
+
store,
|
|
1052
|
+
agentDir,
|
|
1053
|
+
config: params.config
|
|
1054
|
+
})
|
|
1055
|
+
};
|
|
1056
|
+
if (credential.type === "oauth" && credential.authFlow === "chatgpt-identity") throw new Error("ChatGPT subscription sharing was not granted; sign in again and enable sharing.");
|
|
933
1057
|
const loginParams = await resolveCodexAppServerAuthProfileLoginParamsInternal({
|
|
934
1058
|
agentDir,
|
|
935
1059
|
authProfileId: profileId,
|
|
@@ -950,6 +1074,22 @@ async function resolveCodexAppServerPreparedAuthProfileSnapshot(params) {
|
|
|
950
1074
|
/** Maps one prepared route to one mutually exclusive app-server auth handoff. */
|
|
951
1075
|
async function resolveCodexAppServerPreparedAuthHandoff(params) {
|
|
952
1076
|
const usesNativeHome = params.homeScope === "user";
|
|
1077
|
+
const selectedCredential = params.authProfileId ? params.authProfileStore.profiles[params.authProfileId] : void 0;
|
|
1078
|
+
if (isCodexResponsesOAuthCredential(selectedCredential)) {
|
|
1079
|
+
if (usesNativeHome || params.requirePreparedAuth || params.authRequirement !== "api-key") throw new Error("ChatGPT subscription sharing requires the managed local Codex API route and an isolated home.");
|
|
1080
|
+
const snapshot = await resolveCodexAppServerPreparedAuthProfileSnapshot(params);
|
|
1081
|
+
if (!snapshot || !params.authProfileId) throw new Error("ChatGPT subscription sharing could not prepare the selected profile.");
|
|
1082
|
+
return {
|
|
1083
|
+
authProfileId: params.authProfileId,
|
|
1084
|
+
nativeAuthProfile: false,
|
|
1085
|
+
preparedAuth: {
|
|
1086
|
+
kind: "profile",
|
|
1087
|
+
profileId: params.authProfileId,
|
|
1088
|
+
store: params.authProfileStore,
|
|
1089
|
+
snapshot
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
953
1093
|
if (params.requirePreparedAuth && usesNativeHome) throw createCodexAppServerAuthError("Codex remote-exec cloud placement requires prepared OpenAI auth. Configure an OpenAI API-key, OAuth, or token profile and use appServer.homeScope=\"agent\"; ambient credentials and native Codex auth are not allowed.");
|
|
954
1094
|
if (usesNativeHome) return { nativeAuthProfile: true };
|
|
955
1095
|
if (params.authRequirement === "api-key") {
|
|
@@ -964,6 +1104,7 @@ async function resolveCodexAppServerPreparedAuthHandoff(params) {
|
|
|
964
1104
|
};
|
|
965
1105
|
}
|
|
966
1106
|
const authProfileId = params.authProfileId?.trim() || void 0;
|
|
1107
|
+
if (authProfileId && !params.authProfileStore.profiles[authProfileId]) throw new CodexAppServerAuthProfileUnavailableError(formatCodexAuthProfileUnavailableMessage(authProfileId));
|
|
967
1108
|
const nativeAuthProfile = isCodexAppServerNativeAuthProfile({
|
|
968
1109
|
authProfileId,
|
|
969
1110
|
authProfileStore: params.authProfileStore,
|
|
@@ -994,38 +1135,19 @@ async function resolveCodexAppServerPreparedAuthHandoff(params) {
|
|
|
994
1135
|
};
|
|
995
1136
|
}
|
|
996
1137
|
async function resolveCodexAppServerAuthAccountCacheKey(params) {
|
|
997
|
-
const
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
const profileId = resolveCodexAppServerAuthProfileId({
|
|
1005
|
-
authProfileId: params.authProfileId,
|
|
1138
|
+
const profile = resolveCodexAppServerAuthProfile(params);
|
|
1139
|
+
if (!profile) return;
|
|
1140
|
+
const { agentDir, store, profileId, credential } = profile;
|
|
1141
|
+
const accountId = resolveChatgptAccountId(profileId, credential);
|
|
1142
|
+
if (credential.type === "oauth") return accountId;
|
|
1143
|
+
const value = (await resolveApiKeyForProfile({
|
|
1144
|
+
cfg: params.config,
|
|
1006
1145
|
store,
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
if (credential.type === "api_key") {
|
|
1013
|
-
const apiKey = (await resolveApiKeyForProfile({
|
|
1014
|
-
store,
|
|
1015
|
-
profileId,
|
|
1016
|
-
agentDir
|
|
1017
|
-
}))?.apiKey?.trim();
|
|
1018
|
-
return apiKey ? `${resolveChatgptAccountId(profileId, credential)}:${fingerprintApiKeyAuthProfileCacheKey(apiKey)}` : resolveChatgptAccountId(profileId, credential);
|
|
1019
|
-
}
|
|
1020
|
-
if (credential.type === "token") {
|
|
1021
|
-
const accessToken = (await resolveApiKeyForProfile({
|
|
1022
|
-
store,
|
|
1023
|
-
profileId,
|
|
1024
|
-
agentDir
|
|
1025
|
-
}))?.apiKey?.trim();
|
|
1026
|
-
return accessToken ? `${resolveChatgptAccountId(profileId, credential)}:${fingerprintTokenAuthProfileCacheKey(accessToken)}` : resolveChatgptAccountId(profileId, credential);
|
|
1027
|
-
}
|
|
1028
|
-
return resolveChatgptAccountId(profileId, credential);
|
|
1146
|
+
profileId,
|
|
1147
|
+
agentDir
|
|
1148
|
+
}))?.apiKey?.trim();
|
|
1149
|
+
if (!value) return accountId;
|
|
1150
|
+
return `${accountId}:${credential.type === "api_key" ? fingerprintApiKeyAuthProfileCacheKey(value) : fingerprintTokenAuthProfileCacheKey(value)}`;
|
|
1029
1151
|
}
|
|
1030
1152
|
async function withCodexHomeEnvironment(startOptions, agentDir) {
|
|
1031
1153
|
const codexHome = resolveCodexAppServerLocalHomeDir(startOptions, agentDir);
|
|
@@ -1107,12 +1229,15 @@ async function reconcileCodexComputerUseStartArtifactsOnce(params) {
|
|
|
1107
1229
|
needsBundledMarketplace
|
|
1108
1230
|
}) : exactDesktopCandidate;
|
|
1109
1231
|
params.assertCurrent();
|
|
1232
|
+
let marketplacePath;
|
|
1110
1233
|
if (provisioningAgentDir) {
|
|
1111
1234
|
if (desktopCandidates.length > 0 && !artifactCandidate) throw new CodexComputerUseCandidateArtifactsUnavailableError();
|
|
1112
1235
|
try {
|
|
1113
|
-
|
|
1236
|
+
marketplacePath = usesManagedBundledMarketplace ? await ensureCodexManagedBundledMarketplace({
|
|
1114
1237
|
codexHome,
|
|
1115
1238
|
ownershipRoot: provisioningAgentDir,
|
|
1239
|
+
computerUsePluginName: computerUseConfig.pluginName,
|
|
1240
|
+
computerUseMcpServerName: computerUseConfig.mcpServerName,
|
|
1116
1241
|
...artifactCandidate ? {
|
|
1117
1242
|
appServerCommand: artifactCandidate.appServerCommandPath,
|
|
1118
1243
|
candidates: [artifactCandidate],
|
|
@@ -1143,21 +1268,17 @@ async function reconcileCodexComputerUseStartArtifactsOnce(params) {
|
|
|
1143
1268
|
}
|
|
1144
1269
|
}
|
|
1145
1270
|
params.assertCurrent();
|
|
1146
|
-
|
|
1147
|
-
params.desktopGeneration?.epoch ?? "manual",
|
|
1148
|
-
artifactCandidate?.bundledMarketplacePath ?? "default",
|
|
1149
|
-
computerUseConfig.pluginName
|
|
1150
|
-
].join("\0");
|
|
1151
|
-
const cache = await ensureCodexComputerUseSharedPluginCache({
|
|
1271
|
+
return await reconcileManagedCodexComputerUseCache({
|
|
1152
1272
|
codexHome,
|
|
1153
1273
|
config: computerUseConfig,
|
|
1154
|
-
|
|
1155
|
-
|
|
1274
|
+
ownershipRoot: ownsIsolatedCodexHome ? params.agentDir : void 0,
|
|
1275
|
+
managedMarketplacePath: marketplacePath,
|
|
1276
|
+
bundledMarketplacePath: artifactCandidate?.bundledMarketplacePath,
|
|
1277
|
+
epoch: params.desktopGeneration?.epoch,
|
|
1156
1278
|
assertCurrent: params.assertCurrent,
|
|
1157
|
-
forceRefresh: params.forceCacheRefresh
|
|
1279
|
+
forceRefresh: params.forceCacheRefresh,
|
|
1280
|
+
previousCacheBinding: params.previousCacheBinding
|
|
1158
1281
|
});
|
|
1159
|
-
params.assertCurrent();
|
|
1160
|
-
return cache.status === "shared" ? cacheBinding : void 0;
|
|
1161
1282
|
}
|
|
1162
1283
|
async function resolveCompleteComputerUseArtifactCandidate(params) {
|
|
1163
1284
|
for (const candidate of params.candidates) {
|
|
@@ -1195,10 +1316,14 @@ async function applyCodexAppServerAuthProfile(params) {
|
|
|
1195
1316
|
let loginParams = params.preparedAuth?.kind === "profile" ? params.preparedAuth.snapshot.loginParams : params.preparedAuth?.kind === "api-key" ? {
|
|
1196
1317
|
type: "apiKey",
|
|
1197
1318
|
apiKey: params.preparedAuth.apiKey
|
|
1198
|
-
} : await
|
|
1319
|
+
} : await resolveCodexAppServerAuthProfileLoginParamsInternal({
|
|
1199
1320
|
agentDir,
|
|
1200
1321
|
authProfileId: params.authProfileId ?? void 0,
|
|
1201
|
-
authProfileStore:
|
|
1322
|
+
authProfileStore: resolveCodexAppServerAuthProfileStore({
|
|
1323
|
+
...params,
|
|
1324
|
+
agentDir,
|
|
1325
|
+
authProfileId: params.authProfileId ?? void 0
|
|
1326
|
+
}),
|
|
1202
1327
|
config: params.config
|
|
1203
1328
|
});
|
|
1204
1329
|
if (params.authRequirement === "subscription" && loginParams?.type !== "chatgptAuthTokens") throw createCodexAppServerAuthError("Codex subscription auth profile could not produce login credentials. Sign in with `openclaw models auth login --provider openai`, select that profile, then retry.");
|
|
@@ -1231,8 +1356,7 @@ async function applyCodexAppServerAuthProfile(params) {
|
|
|
1231
1356
|
*/
|
|
1232
1357
|
async function assertNativeCodexAccountMatchesRoute(client, authRequirement, assertCurrent) {
|
|
1233
1358
|
if (!authRequirement) return;
|
|
1234
|
-
const
|
|
1235
|
-
const accountType = isJsonObject(response.account) ? response.account.type : void 0;
|
|
1359
|
+
const accountType = (await client.request("account/read", { refreshToken: false }, { assertCurrent })).account?.type;
|
|
1236
1360
|
if (authRequirement === "subscription") {
|
|
1237
1361
|
if (accountType !== "chatgpt") throw createCodexAppServerAuthError("Codex subscription route requires ChatGPT auth in the native Codex home. Run `codex login` for that home, or use appServer.homeScope=\"agent\" with an OpenClaw OAuth profile, then retry.");
|
|
1238
1362
|
return;
|
|
@@ -1243,28 +1367,6 @@ function createCodexAppServerAuthError(message, cause) {
|
|
|
1243
1367
|
const error = cause === void 0 ? new Error(message) : new Error(message, { cause });
|
|
1244
1368
|
return Object.assign(error, { status: 401 });
|
|
1245
1369
|
}
|
|
1246
|
-
var CodexAppServerAuthProfileUnavailableError = class extends Error {
|
|
1247
|
-
constructor(..._args) {
|
|
1248
|
-
super(..._args);
|
|
1249
|
-
this.status = 401;
|
|
1250
|
-
this.code = "selected_auth_profile_unavailable";
|
|
1251
|
-
}
|
|
1252
|
-
};
|
|
1253
|
-
async function resolveCodexAppServerAuthProfileLoginParams(params) {
|
|
1254
|
-
const store = resolveCodexAppServerAuthProfileStore(params);
|
|
1255
|
-
const profileId = resolveCodexAppServerAuthProfileId({
|
|
1256
|
-
authProfileId: params.authProfileId,
|
|
1257
|
-
store,
|
|
1258
|
-
config: params.config
|
|
1259
|
-
});
|
|
1260
|
-
const profile = profileId ? store.profiles[profileId] : void 0;
|
|
1261
|
-
if (profileId && !profile) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" was not found. Select an existing OpenAI profile or sign in again with OpenClaw, then retry.`);
|
|
1262
|
-
if (profileId && profile && !isCodexAppServerAuthProfileCredential(profile)) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" must use the canonical OpenAI auth provider; run "openclaw doctor --fix" to migrate legacy provider IDs.`);
|
|
1263
|
-
return await resolveCodexAppServerAuthProfileLoginParamsInternal({
|
|
1264
|
-
...params,
|
|
1265
|
-
authProfileStore: store
|
|
1266
|
-
});
|
|
1267
|
-
}
|
|
1268
1370
|
async function refreshCodexAppServerAuthTokens(params) {
|
|
1269
1371
|
const previousAccountId = params.previousAccountId?.trim();
|
|
1270
1372
|
const handoffAccountId = params.authHandoff?.chatgptAccountId.trim();
|
|
@@ -1285,8 +1387,7 @@ async function refreshCodexAppServerAuthTokens(params) {
|
|
|
1285
1387
|
forceOAuthRefresh: true
|
|
1286
1388
|
});
|
|
1287
1389
|
if (!loginParams || loginParams.type !== "chatgptAuthTokens") throw new Error("Codex app-server ChatGPT token refresh requires an OAuth auth profile. Sign in with `openclaw models auth login --provider openai`, select that profile, then retry.");
|
|
1288
|
-
if (previousAccountId && loginParams.chatgptAccountId !== previousAccountId) throw new Error("ChatGPT workspace changed during Codex token refresh. Retry to start a client for the selected workspace.");
|
|
1289
|
-
if (params.authHandoff && loginParams.chatgptAccountId !== params.authHandoff.chatgptAccountId) throw new Error("ChatGPT workspace changed during Codex token refresh. Retry to start a client for the selected workspace.");
|
|
1390
|
+
if (previousAccountId && loginParams.chatgptAccountId !== previousAccountId || params.authHandoff && loginParams.chatgptAccountId !== params.authHandoff.chatgptAccountId) throw new Error("ChatGPT workspace changed during Codex token refresh. Retry to start a client for the selected workspace.");
|
|
1290
1391
|
return {
|
|
1291
1392
|
accessToken: loginParams.accessToken,
|
|
1292
1393
|
chatgptAccountId: loginParams.chatgptAccountId,
|
|
@@ -1307,8 +1408,8 @@ async function resolveCodexAppServerAuthProfileLoginParamsInternal(params) {
|
|
|
1307
1408
|
});
|
|
1308
1409
|
if (!profileId) return;
|
|
1309
1410
|
const credential = store.profiles[profileId];
|
|
1310
|
-
if (!credential) throw new CodexAppServerAuthProfileUnavailableError(
|
|
1311
|
-
if (!
|
|
1411
|
+
if (!credential) throw new CodexAppServerAuthProfileUnavailableError(formatCodexAuthProfileUnavailableMessage(profileId));
|
|
1412
|
+
if (!isCodexAppServerAuthProvider(credential.provider)) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" must use the canonical OpenAI auth provider; run "openclaw doctor --fix" to migrate legacy provider IDs.`);
|
|
1312
1413
|
const loginParams = await resolveLoginParamsForCredential(profileId, credential, {
|
|
1313
1414
|
agentDir: params.agentDir,
|
|
1314
1415
|
store,
|
|
@@ -1331,30 +1432,22 @@ async function resolveCodexAppServerFallbackApiKeyLoginParams(params) {
|
|
|
1331
1432
|
};
|
|
1332
1433
|
}
|
|
1333
1434
|
async function resolveLoginParamsForCredential(profileId, credential, params) {
|
|
1334
|
-
if (credential.type === "api_key") {
|
|
1335
|
-
const
|
|
1435
|
+
if (credential.type === "api_key" || credential.type === "token") {
|
|
1436
|
+
const value = (await resolveApiKeyForProfile({
|
|
1437
|
+
cfg: params.config,
|
|
1336
1438
|
store: params.preferStoreCredential ? params.store : ensureAuthProfileStore(params.agentDir, {
|
|
1337
1439
|
allowKeychainPrompt: false,
|
|
1338
|
-
profileId
|
|
1440
|
+
profileId,
|
|
1441
|
+
config: params.config
|
|
1339
1442
|
}),
|
|
1340
1443
|
profileId,
|
|
1341
1444
|
agentDir: params.agentDir
|
|
1342
1445
|
}))?.apiKey?.trim();
|
|
1343
|
-
|
|
1446
|
+
if (!value) return;
|
|
1447
|
+
return credential.type === "api_key" ? {
|
|
1344
1448
|
type: "apiKey",
|
|
1345
|
-
apiKey
|
|
1346
|
-
} :
|
|
1347
|
-
}
|
|
1348
|
-
if (credential.type === "token") {
|
|
1349
|
-
const accessToken = (await resolveApiKeyForProfile({
|
|
1350
|
-
store: params.preferStoreCredential ? params.store : ensureAuthProfileStore(params.agentDir, {
|
|
1351
|
-
allowKeychainPrompt: false,
|
|
1352
|
-
profileId
|
|
1353
|
-
}),
|
|
1354
|
-
profileId,
|
|
1355
|
-
agentDir: params.agentDir
|
|
1356
|
-
}))?.apiKey?.trim();
|
|
1357
|
-
return accessToken ? buildChatgptAuthTokensParams(profileId, credential, accessToken) : void 0;
|
|
1449
|
+
apiKey: value
|
|
1450
|
+
} : buildChatgptAuthTokensParams(profileId, credential, value);
|
|
1358
1451
|
}
|
|
1359
1452
|
if (credential.type !== "oauth") return;
|
|
1360
1453
|
const resolvedCredential = await resolveOAuthCredentialForCodexAppServer(profileId, credential, {
|
|
@@ -1384,7 +1477,7 @@ async function resolveOAuthCredentialForCodexAppServer(profileId, credential, pa
|
|
|
1384
1477
|
persistedCredential,
|
|
1385
1478
|
suppliedCredential: credential
|
|
1386
1479
|
});
|
|
1387
|
-
if (params.preferStoreCredential && params.store.runtimePersistedProfileIds?.includes(profileId) && (persistedCredential?.type !== "oauth" || !isCodexAppServerAuthProvider(persistedCredential.provider))) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" is no longer
|
|
1480
|
+
if (params.preferStoreCredential && params.store.runtimePersistedProfileIds?.includes(profileId) && (persistedCredential?.type !== "oauth" || !isCodexAppServerAuthProvider(persistedCredential.provider))) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" is no longer an OpenAI OAuth credential in its persisted OpenClaw store. Run "openclaw doctor" to inspect credential ownership, or select an existing OpenAI profile.`);
|
|
1388
1481
|
const store = useScopedCredential ? params.store : resolveCodexAppServerAuthProfileStore({
|
|
1389
1482
|
agentDir: ownerAgentDir,
|
|
1390
1483
|
authProfileId: profileId,
|
|
@@ -1408,17 +1501,22 @@ async function resolveOAuthCredentialForCodexAppServer(profileId, credential, pa
|
|
|
1408
1501
|
profileId,
|
|
1409
1502
|
credential: overlaidOAuthCredential,
|
|
1410
1503
|
forceRefresh: params.forceRefresh && !reuseCompletedRotation,
|
|
1411
|
-
expectedAccountId
|
|
1504
|
+
expectedAccountId,
|
|
1505
|
+
config: params.config
|
|
1412
1506
|
});
|
|
1413
1507
|
if (params.forceRefresh && !persistedOAuthCredential && overlaidOAuthCredential) {
|
|
1414
1508
|
if (reuseCompletedRotation) return overlaidOAuthCredential;
|
|
1415
|
-
const refreshedRuntimeCredential = await refreshOAuthCredentialForRuntime({
|
|
1509
|
+
const refreshedRuntimeCredential = await refreshOAuthCredentialForRuntime({
|
|
1510
|
+
credential: overlaidOAuthCredential,
|
|
1511
|
+
cfg: params.config
|
|
1512
|
+
});
|
|
1416
1513
|
if (!refreshedRuntimeCredential?.access?.trim()) throw new Error(`Codex app-server auth profile "${profileId}" could not refresh. Sign in again with OpenClaw, then retry.`);
|
|
1417
1514
|
assertCodexOAuthRefreshWorkspace(profileId, refreshedRuntimeCredential, expectedAccountId);
|
|
1418
1515
|
store.profiles[profileId] = refreshedRuntimeCredential;
|
|
1419
1516
|
return refreshedRuntimeCredential;
|
|
1420
1517
|
}
|
|
1421
1518
|
const resolved = await resolveApiKeyForProfile({
|
|
1519
|
+
cfg: params.config,
|
|
1422
1520
|
store,
|
|
1423
1521
|
profileId,
|
|
1424
1522
|
agentDir: ownerAgentDir,
|
|
@@ -1426,7 +1524,7 @@ async function resolveOAuthCredentialForCodexAppServer(profileId, credential, pa
|
|
|
1426
1524
|
allowProfileFallback: false,
|
|
1427
1525
|
...expectedAccountId ? { validateOAuthCredential: (candidate) => assertCodexOAuthRefreshWorkspace(profileId, candidate, expectedAccountId) } : {}
|
|
1428
1526
|
});
|
|
1429
|
-
if (!resolved || resolved.credential?.type !== "oauth" || !isCodexAppServerAuthProvider(resolved.credential.provider) || !resolved.apiKey.trim()) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}"
|
|
1527
|
+
if (!resolved || resolved.credential?.type !== "oauth" || !isCodexAppServerAuthProvider(resolved.credential.provider) || !resolved.apiKey.trim()) throw new CodexAppServerAuthProfileUnavailableError(`Codex app-server auth profile "${profileId}" could not resolve usable OAuth credentials from its OpenClaw credential store. Run "openclaw doctor" to inspect credential ownership, or select an existing OpenAI profile.`);
|
|
1430
1528
|
const candidate = {
|
|
1431
1529
|
...resolved.credential,
|
|
1432
1530
|
access: resolved.apiKey
|
|
@@ -1470,7 +1568,10 @@ async function resolveScopedOAuthCredential(params) {
|
|
|
1470
1568
|
const current = params.store.profiles[params.profileId];
|
|
1471
1569
|
const credential = current?.type === "oauth" ? current : params.credential;
|
|
1472
1570
|
if (!params.forceRefresh && hasUsableOAuthCredential(credential)) return credential;
|
|
1473
|
-
const refreshed = await refreshOAuthCredentialForRuntime({
|
|
1571
|
+
const refreshed = await refreshOAuthCredentialForRuntime({
|
|
1572
|
+
credential,
|
|
1573
|
+
cfg: params.config
|
|
1574
|
+
});
|
|
1474
1575
|
if (!refreshed?.access?.trim()) throw new Error(`Codex app-server auth profile "${params.profileId}" could not refresh. Sign in again with OpenClaw, then retry.`);
|
|
1475
1576
|
assertCodexOAuthRefreshWorkspace(params.profileId, refreshed, params.expectedAccountId);
|
|
1476
1577
|
if (!isDeepStrictEqual(params.store.profiles[params.profileId], credential)) throw new Error(`Codex app-server auth profile "${params.profileId}" changed while refreshing. Retry with the newly selected OpenAI profile.`);
|
|
@@ -1486,15 +1587,11 @@ async function resolveScopedOAuthCredential(params) {
|
|
|
1486
1587
|
}
|
|
1487
1588
|
function assertCodexOAuthRefreshWorkspace(profileId, credential, expectedAccountId) {
|
|
1488
1589
|
if (!expectedAccountId) return;
|
|
1489
|
-
|
|
1490
|
-
if (loginParams.type !== "chatgptAuthTokens" || loginParams.chatgptAccountId !== expectedAccountId) throw new Error("ChatGPT workspace changed during Codex token refresh. Retry to start a client for the selected workspace.");
|
|
1590
|
+
if (buildChatgptAuthTokensParams(profileId, credential, credential.access.trim()).chatgptAccountId !== expectedAccountId) throw new Error("ChatGPT workspace changed during Codex token refresh. Retry to start a client for the selected workspace.");
|
|
1491
1591
|
}
|
|
1492
1592
|
function isCodexAppServerAuthProvider(provider) {
|
|
1493
1593
|
return provider.trim().toLowerCase() === CODEX_APP_SERVER_AUTH_PROVIDER;
|
|
1494
1594
|
}
|
|
1495
|
-
function isCodexAppServerAuthProfileCredential(credential) {
|
|
1496
|
-
return isCodexAppServerAuthProvider(credential.provider);
|
|
1497
|
-
}
|
|
1498
1595
|
function shouldClearOpenAiApiKeyForCodexAuthProfile(params) {
|
|
1499
1596
|
const profileId = params.authProfileId?.trim();
|
|
1500
1597
|
return isCodexSubscriptionCredential(profileId ? params.store.profiles[profileId] : params.store.profiles[OPENAI_CODEX_DEFAULT_PROFILE_ID]);
|
|
@@ -1503,15 +1600,6 @@ function isCodexSubscriptionCredential(credential) {
|
|
|
1503
1600
|
if (!credential || !isCodexAppServerAuthProvider(credential.provider)) return false;
|
|
1504
1601
|
return credential.type === "oauth" || credential.type === "token";
|
|
1505
1602
|
}
|
|
1506
|
-
function withClearedEnvironmentVariables(startOptions, envVars) {
|
|
1507
|
-
const clearEnv = startOptions.clearEnv ?? [];
|
|
1508
|
-
const missingEnvVars = envVars.filter((envVar) => !clearEnv.includes(envVar));
|
|
1509
|
-
if (missingEnvVars.length === 0) return startOptions;
|
|
1510
|
-
return {
|
|
1511
|
-
...startOptions,
|
|
1512
|
-
clearEnv: [...clearEnv, ...missingEnvVars]
|
|
1513
|
-
};
|
|
1514
|
-
}
|
|
1515
1603
|
function buildChatgptAuthTokensParams(profileId, credential, accessToken) {
|
|
1516
1604
|
const storedAccountId = resolveExplicitChatgptAccountId(credential);
|
|
1517
1605
|
const tokenAccountId = resolveOpenAICodexAuthIdentity({ access: accessToken }).accountId;
|
|
@@ -1555,7 +1643,7 @@ function createThreadOwnerToken(threadId, onInvalidated) {
|
|
|
1555
1643
|
} catch (error) {
|
|
1556
1644
|
embeddedAgentLog$1.warn("codex thread ownership invalidation failed", {
|
|
1557
1645
|
threadId,
|
|
1558
|
-
reason: formatErrorMessage(error)
|
|
1646
|
+
reason: formatErrorMessage$1(error)
|
|
1559
1647
|
});
|
|
1560
1648
|
}
|
|
1561
1649
|
}
|
|
@@ -1601,6 +1689,21 @@ function forgetThreadOwnership(runtime, threadId, owner) {
|
|
|
1601
1689
|
if (forgotten) owner.invalidate();
|
|
1602
1690
|
return forgotten;
|
|
1603
1691
|
}
|
|
1692
|
+
/** Compaction discards client-authored catalog refreshes, not creation policy. */
|
|
1693
|
+
function revertRetainedThreadSkillsCatalog(runtime, threadId) {
|
|
1694
|
+
const retained = runtime.retainedThreads.get(threadId);
|
|
1695
|
+
if (retained?.ephemeralPolicy) retained.ephemeralPolicy = {
|
|
1696
|
+
...retained.ephemeralPolicy,
|
|
1697
|
+
skillsInstructions: retained.ephemeralPolicy.nativeSkillsInstructions
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
function createCodexEphemeralThreadPolicy({ developerInstructions, skillsInstructions }) {
|
|
1701
|
+
return {
|
|
1702
|
+
developerInstructions,
|
|
1703
|
+
skillsInstructions,
|
|
1704
|
+
nativeSkillsInstructions: skillsInstructions
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1604
1707
|
//#endregion
|
|
1605
1708
|
//#region extensions/codex/src/app-server/rate-limit-cache.ts
|
|
1606
1709
|
const DEFAULT_CODEX_RATE_LIMIT_CACHE_MAX_AGE_MS = 6e5;
|
|
@@ -1867,7 +1970,7 @@ function scheduleRetainedThreadEviction(client, runtime) {
|
|
|
1867
1970
|
runtime.evictionTimer = setTimeout(() => {
|
|
1868
1971
|
runtime.evictionTimer = void 0;
|
|
1869
1972
|
evictExpiredRetainedThreads(client, runtime).catch((error) => {
|
|
1870
|
-
embeddedAgentLog$1.warn("codex retained thread expiry failed", { reason: formatErrorMessage(error) });
|
|
1973
|
+
embeddedAgentLog$1.warn("codex retained thread expiry failed", { reason: formatErrorMessage$1(error) });
|
|
1871
1974
|
});
|
|
1872
1975
|
}, Math.max(0, expiresAt - Date.now()));
|
|
1873
1976
|
runtime.evictionTimer.unref?.();
|
|
@@ -1961,7 +2064,7 @@ async function retainCodexAppServerLiveThread(client, threadId, releaseThread, c
|
|
|
1961
2064
|
scheduleRetainedThreadEviction(client, runtime);
|
|
1962
2065
|
embeddedAgentLog$1.warn("codex retained thread capacity eviction failed", {
|
|
1963
2066
|
threadId,
|
|
1964
|
-
reason: formatErrorMessage(error)
|
|
2067
|
+
reason: formatErrorMessage$1(error)
|
|
1965
2068
|
});
|
|
1966
2069
|
return false;
|
|
1967
2070
|
}
|
|
@@ -2065,6 +2168,11 @@ function claimCodexAppServerThreadOwnership(client, runtime, threadId, retained,
|
|
|
2065
2168
|
}
|
|
2066
2169
|
};
|
|
2067
2170
|
}
|
|
2171
|
+
/** Standalone incognito compaction retains its separately owned subscription. */
|
|
2172
|
+
function revertCodexAppServerLiveThreadSkillsCatalog(client, threadId) {
|
|
2173
|
+
const runtime = configuredClients.get(client);
|
|
2174
|
+
if (runtime && !runtime.closed) revertRetainedThreadSkillsCatalog(runtime, threadId);
|
|
2175
|
+
}
|
|
2068
2176
|
/** Distinguish active claimed ownership from an already-evicted idle subscription. */
|
|
2069
2177
|
function hasCodexAppServerLiveThread(client, threadId) {
|
|
2070
2178
|
return hasThreadOwnership(configuredClients.get(client), threadId);
|
|
@@ -2154,25 +2262,156 @@ function protectCodexAppServerLiveThread(client, threadId) {
|
|
|
2154
2262
|
evictExcessIdleThreads(client, runtime).catch((error) => {
|
|
2155
2263
|
embeddedAgentLog$1.warn("codex retained thread unpin eviction failed", {
|
|
2156
2264
|
threadId,
|
|
2157
|
-
reason: formatErrorMessage(error)
|
|
2265
|
+
reason: formatErrorMessage$1(error)
|
|
2158
2266
|
});
|
|
2159
2267
|
});
|
|
2160
2268
|
};
|
|
2161
2269
|
}
|
|
2162
2270
|
//#endregion
|
|
2271
|
+
//#region extensions/codex/src/app-server/client-startup-retry.ts
|
|
2272
|
+
/** Recovers physical startup, including acquisitions before thread preparation. */
|
|
2273
|
+
async function withCodexWebSocketOpenRetry(params) {
|
|
2274
|
+
const startedAt = performance.now();
|
|
2275
|
+
for (let attempt = 1;; attempt += 1) try {
|
|
2276
|
+
return await params.start(resolveRemainingAcquireTimeout(params.timeoutMs, startedAt));
|
|
2277
|
+
} catch (error) {
|
|
2278
|
+
if (params.signal.aborted || attempt >= 3 || !isCodexWebSocketOpenFailure(error)) throw error;
|
|
2279
|
+
const remaining = resolveRemainingAcquireTimeout(params.timeoutMs, startedAt);
|
|
2280
|
+
const delay = 1e3 * 2 ** (attempt - 1);
|
|
2281
|
+
await sleepWithAbort(remaining > 0 ? Math.min(delay, remaining) : delay, params.signal);
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
function resolveRemainingAcquireTimeout(timeoutMs, startedAt) {
|
|
2285
|
+
if (!(timeoutMs > 0)) return timeoutMs;
|
|
2286
|
+
const remaining = timeoutMs - (performance.now() - startedAt);
|
|
2287
|
+
if (remaining <= 0) throw new CodexAppServerStartupError("timed_out", "codex app-server initialize timed out");
|
|
2288
|
+
return remaining;
|
|
2289
|
+
}
|
|
2290
|
+
async function withCodexAppServerAcquireDeadline(timeoutMs, promise, signal, timeoutMessage = "codex app-server initialize timed out", timeoutErrorFactory) {
|
|
2291
|
+
if (signal?.aborted) throw new CodexAppServerStartupError("aborted", "codex app-server initialize aborted");
|
|
2292
|
+
const timed = withTimeout(promise, timeoutMs, timeoutMessage, () => timeoutErrorFactory?.() ?? new CodexAppServerStartupError("timed_out", timeoutMessage));
|
|
2293
|
+
if (!signal) return await timed;
|
|
2294
|
+
return await new Promise((resolve, reject) => {
|
|
2295
|
+
const onAbort = () => reject(new CodexAppServerStartupError("aborted", "codex app-server initialize aborted"));
|
|
2296
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2297
|
+
timed.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2300
|
+
//#endregion
|
|
2301
|
+
//#region extensions/codex/src/app-server/inference-environment.ts
|
|
2302
|
+
function supportsInferenceEnvironment(native) {
|
|
2303
|
+
if (native.CODEX_CA_CERTIFICATE?.trim() || native.SSL_CERT_FILE?.trim()) return false;
|
|
2304
|
+
const names = [
|
|
2305
|
+
"HTTP_PROXY",
|
|
2306
|
+
"HTTPS_PROXY",
|
|
2307
|
+
"ALL_PROXY",
|
|
2308
|
+
"NO_PROXY"
|
|
2309
|
+
];
|
|
2310
|
+
for (const upper of names) {
|
|
2311
|
+
const lower = upper.toLowerCase();
|
|
2312
|
+
if (native[upper] !== process.env[upper] || native[lower] !== process.env[lower] || native[upper] !== void 0 && native[lower] !== void 0 && native[upper] !== native[lower]) return false;
|
|
2313
|
+
}
|
|
2314
|
+
const value = (name) => (native[name] ?? native[name.toLowerCase()])?.trim() || void 0;
|
|
2315
|
+
const http = value("HTTP_PROXY");
|
|
2316
|
+
const https = value("HTTPS_PROXY");
|
|
2317
|
+
const all = value("ALL_PROXY");
|
|
2318
|
+
if (!http && !https && !all) return true;
|
|
2319
|
+
const noProxy = native.NO_PROXY ?? native.no_proxy ?? "";
|
|
2320
|
+
if (noProxy === "*") return true;
|
|
2321
|
+
if (native.REQUEST_METHOD !== void 0 || names.slice(0, 3).some((name) => {
|
|
2322
|
+
const raw = native[name] ?? native[name.toLowerCase()];
|
|
2323
|
+
return raw !== void 0 && raw !== raw.trim();
|
|
2324
|
+
})) return false;
|
|
2325
|
+
if ((https ?? all) !== (https ?? http ?? all)) return false;
|
|
2326
|
+
const bypasses = noProxy.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
2327
|
+
if (bypasses.some((entry) => ![
|
|
2328
|
+
"127.0.0.1",
|
|
2329
|
+
"localhost",
|
|
2330
|
+
"::1",
|
|
2331
|
+
"[::1]"
|
|
2332
|
+
].includes(entry))) return false;
|
|
2333
|
+
if ((http || all) && !bypasses.includes("127.0.0.1")) return false;
|
|
2334
|
+
return /^https?:\/\//.test(https ?? all ?? "");
|
|
2335
|
+
}
|
|
2336
|
+
//#endregion
|
|
2337
|
+
//#region extensions/codex/src/app-server/inference-provider-config.ts
|
|
2338
|
+
function providerKind(name) {
|
|
2339
|
+
if (name === "Amazon Bedrock" || name === "Amazon Bedrock Runtime") return;
|
|
2340
|
+
return name === "OpenAI" ? "openai" : typeof name === "string" && name.toLowerCase() === "azure" ? "azure" : "other";
|
|
2341
|
+
}
|
|
2342
|
+
function hasProviderAws(config, provider) {
|
|
2343
|
+
return readProviderField(config, provider, "aws") != null || Object.keys(config ?? {}).some((key) => key.startsWith(`model_providers.${provider}.aws.`));
|
|
2344
|
+
}
|
|
2345
|
+
function configuredProviders(...configs) {
|
|
2346
|
+
const providers = /* @__PURE__ */ new Set(["openai"]);
|
|
2347
|
+
for (const config of configs) {
|
|
2348
|
+
for (const provider of Object.keys(isJsonObject(config?.model_providers) ? config.model_providers : {})) providers.add(provider);
|
|
2349
|
+
for (const key of Object.keys(config ?? {})) {
|
|
2350
|
+
const provider = /^model_providers\.([^.]+)(?:\.|$)/.exec(key)?.[1];
|
|
2351
|
+
if (provider) providers.add(provider);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
return providers;
|
|
2355
|
+
}
|
|
2356
|
+
function projectProviderRoutes(config, providers) {
|
|
2357
|
+
let projected = config ?? {};
|
|
2358
|
+
for (const [provider, route] of providers) projected = withProviderBaseUrl(projected, provider, route.baseUrl);
|
|
2359
|
+
return projected;
|
|
2360
|
+
}
|
|
2361
|
+
function readProviderBaseUrl(config, provider) {
|
|
2362
|
+
if (provider === "openai") return config?.openai_base_url;
|
|
2363
|
+
return readProviderField(config, provider, "base_url");
|
|
2364
|
+
}
|
|
2365
|
+
function readProviderField(config, provider, field) {
|
|
2366
|
+
const selected = (isJsonObject(config?.model_providers) ? config.model_providers : void 0)?.[provider];
|
|
2367
|
+
const flat = config?.[`model_providers.${provider}`];
|
|
2368
|
+
return config?.[`model_providers.${provider}.${field}`] ?? (isJsonObject(flat) ? flat[field] : void 0) ?? (isJsonObject(selected) ? selected[field] : void 0);
|
|
2369
|
+
}
|
|
2370
|
+
function withProviderBaseUrl(config, provider, baseUrl) {
|
|
2371
|
+
if (provider === "openai") return {
|
|
2372
|
+
...config,
|
|
2373
|
+
openai_base_url: baseUrl
|
|
2374
|
+
};
|
|
2375
|
+
const providers = isJsonObject(config?.model_providers) ? config.model_providers : {};
|
|
2376
|
+
const selected = providers[provider];
|
|
2377
|
+
const providerKey = `model_providers.${provider}`;
|
|
2378
|
+
const baseUrlKey = `${providerKey}.base_url`;
|
|
2379
|
+
const flat = config?.[providerKey];
|
|
2380
|
+
return {
|
|
2381
|
+
...config,
|
|
2382
|
+
model_providers: {
|
|
2383
|
+
...providers,
|
|
2384
|
+
[provider]: {
|
|
2385
|
+
...isJsonObject(selected) ? selected : {},
|
|
2386
|
+
base_url: baseUrl
|
|
2387
|
+
}
|
|
2388
|
+
},
|
|
2389
|
+
...isJsonObject(flat) ? { [providerKey]: {
|
|
2390
|
+
...flat,
|
|
2391
|
+
base_url: baseUrl
|
|
2392
|
+
} } : {},
|
|
2393
|
+
...config?.[baseUrlKey] !== void 0 ? { [baseUrlKey]: baseUrl } : {}
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
//#endregion
|
|
2163
2397
|
//#region extensions/codex/src/app-server/inference-routing.ts
|
|
2164
2398
|
const owners = defineCodexBuildState("openclaw.codexAppServerInferenceOwners", () => /* @__PURE__ */ new WeakMap())();
|
|
2165
2399
|
const MAX_ROUTES = 8;
|
|
2166
2400
|
const MAX_THREADS = 256;
|
|
2167
|
-
const NATIVE_OPENAI_UPSTREAMS = /* @__PURE__ */ new Set(["https://api.openai.com/v1", "https://chatgpt.com/backend-api/codex"]);
|
|
2168
2401
|
/** Only managed native stdio startup calls this; locality or metadata cannot opt a client in. */
|
|
2169
|
-
function ownCodexInferenceClient(client) {
|
|
2402
|
+
function ownCodexInferenceClient(client, startOptions = {}, oauth) {
|
|
2170
2403
|
if (owners.has(client)) return;
|
|
2404
|
+
if (!supportsInferenceEnvironment(resolveCodexAppServerSpawnEnv(startOptions))) {
|
|
2405
|
+
if (oauth) throw new Error("ChatGPT subscription sharing requires the managed public Responses inference route with a compatible proxy and trust configuration.");
|
|
2406
|
+
return;
|
|
2407
|
+
}
|
|
2171
2408
|
const owner = {
|
|
2172
2409
|
closed: false,
|
|
2410
|
+
memoryConfigured: false,
|
|
2173
2411
|
routes: /* @__PURE__ */ new Map(),
|
|
2174
2412
|
threads: /* @__PURE__ */ new Map(),
|
|
2175
|
-
handles: /* @__PURE__ */ new
|
|
2413
|
+
handles: /* @__PURE__ */ new Map(),
|
|
2414
|
+
oauth
|
|
2176
2415
|
};
|
|
2177
2416
|
owners.set(client, owner);
|
|
2178
2417
|
const close = () => {
|
|
@@ -2191,10 +2430,11 @@ function ownCodexInferenceClient(client) {
|
|
|
2191
2430
|
});
|
|
2192
2431
|
}
|
|
2193
2432
|
async function prepareCodexInferenceRoute(params) {
|
|
2194
|
-
const
|
|
2433
|
+
const client = params.client;
|
|
2434
|
+
const owner = owners.get(client);
|
|
2195
2435
|
if (!owner) return;
|
|
2196
2436
|
const assertClient = () => {
|
|
2197
|
-
if (owner.closed || owners.get(
|
|
2437
|
+
if (owner.closed || owners.get(client) !== owner) throw new Error("Codex inference route ownership changed; reconnect before retrying");
|
|
2198
2438
|
};
|
|
2199
2439
|
const assertCurrent = () => {
|
|
2200
2440
|
assertClient();
|
|
@@ -2204,40 +2444,102 @@ async function prepareCodexInferenceRoute(params) {
|
|
|
2204
2444
|
assertCurrent();
|
|
2205
2445
|
const snapshot = params.effectiveConfig ?? await readCodexEffectiveConfig(params.client, params.cwd, { signal: params.signal });
|
|
2206
2446
|
assertCurrent();
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2447
|
+
const unsupported = () => {
|
|
2448
|
+
if (owner.oauth) throw new Error("ChatGPT subscription sharing requires the managed public Responses inference route.");
|
|
2449
|
+
};
|
|
2450
|
+
const selectedProvider = params.modelProvider ?? params.config?.model_provider ?? snapshot.config.model_provider ?? "openai";
|
|
2451
|
+
if (owner.oauth && (snapshot.config.model_provider != null && snapshot.config.model_provider !== "openai" || selectedProvider !== "openai" && selectedProvider !== "openclaw_token_sharing")) return unsupported();
|
|
2452
|
+
const provider = owner.oauth ? CODEX_RESPONSES_OAUTH_PROVIDER : selectedProvider;
|
|
2453
|
+
if (typeof provider !== "string" || !provider.trim()) return unsupported();
|
|
2454
|
+
const customProvider = !owner.oauth && provider !== "openai";
|
|
2455
|
+
const providerField = (field) => readProviderField(params.config, provider, field) ?? readProviderField(snapshot.config, provider, field);
|
|
2456
|
+
const nativeProviderName = owner.oauth ? "OpenClaw subscription sharing" : customProvider ? providerField("name") : "OpenAI";
|
|
2457
|
+
const kind = providerKind(nativeProviderName);
|
|
2458
|
+
const wireApi = customProvider ? providerField("wire_api") : "responses";
|
|
2459
|
+
if (!kind || customProvider && ([
|
|
2460
|
+
"amazon-bedrock",
|
|
2461
|
+
"amazon-bedrock-runtime",
|
|
2462
|
+
"ollama",
|
|
2463
|
+
"lmstudio"
|
|
2464
|
+
].includes(provider) || hasProviderAws(params.config, provider) || hasProviderAws(snapshot.config, provider) || wireApi != null && wireApi !== "responses")) return unsupported();
|
|
2465
|
+
if ((params.config?.["features.respect_system_proxy"] ?? (isJsonObject(params.config?.features) ? params.config.features.respect_system_proxy : void 0) ?? (isJsonObject(snapshot.config.features) ? snapshot.config.features.respect_system_proxy : void 0)) === true) return unsupported();
|
|
2466
|
+
const configured = customProvider ? providerField("base_url") : params.config?.openai_base_url ?? snapshot.config.openai_base_url;
|
|
2210
2467
|
if (configured != null && typeof configured !== "string") throw new Error("Codex inference upstream configuration is invalid");
|
|
2468
|
+
if (typeof configured === "string" && (!configured.trim() || configured.includes("?") || configured.includes("#"))) return unsupported();
|
|
2211
2469
|
if (configured) {
|
|
2212
2470
|
let target;
|
|
2213
2471
|
try {
|
|
2214
2472
|
target = new URL(configured);
|
|
2215
2473
|
} catch {
|
|
2216
|
-
return;
|
|
2474
|
+
return unsupported();
|
|
2217
2475
|
}
|
|
2218
|
-
if (
|
|
2476
|
+
if (target.protocol !== "https:" || target.username || target.password || target.hash || target.search || owner.oauth && target.href.replace(/\/$/, "") !== "https://api.openai.com/v1") return unsupported();
|
|
2219
2477
|
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2478
|
+
if (customProvider && !configured) return unsupported();
|
|
2479
|
+
const configKey = customProvider ? `model_providers.${provider}.base_url` : "openai_base_url";
|
|
2480
|
+
if (Object.entries(snapshot.origins ?? {}).filter(([key]) => customProvider ? key === "model_providers" || key === `model_providers.${provider}` || key === configKey : key === configKey).some(([, origin]) => !origin || !CODEX_SESSION_OVERRIDABLE_LAYER_TYPES.has(origin.name.type))) return unsupported();
|
|
2481
|
+
const account = customProvider ? void 0 : await params.client.request("account/read", { refreshToken: false }, {
|
|
2223
2482
|
signal: params.signal,
|
|
2224
2483
|
assertCurrent
|
|
2225
2484
|
});
|
|
2226
2485
|
assertCurrent();
|
|
2227
|
-
const type =
|
|
2228
|
-
if (
|
|
2229
|
-
if (
|
|
2230
|
-
owner.authRoute
|
|
2486
|
+
const type = account?.account?.type;
|
|
2487
|
+
if (owner.oauth && type !== "apiKey") return unsupported();
|
|
2488
|
+
if (!customProvider && type !== "apiKey" && type !== "chatgpt") return unsupported();
|
|
2489
|
+
if (!customProvider && owner.authRoute && owner.authRoute !== type) throw new Error("Codex native account route changed; reconnect before retrying");
|
|
2490
|
+
if (type === "apiKey" || type === "chatgpt") owner.authRoute = type;
|
|
2231
2491
|
const target = new URL(configured || (type === "apiKey" ? "https://api.openai.com/v1" : "https://chatgpt.com/backend-api/codex"));
|
|
2232
|
-
const
|
|
2492
|
+
const { isBlockedHostnameOrIp } = await import("openclaw/plugin-sdk/ssrf-runtime");
|
|
2493
|
+
assertCurrent();
|
|
2494
|
+
if (isBlockedHostnameOrIp(target.hostname)) return unsupported();
|
|
2495
|
+
const nativeBaseUrl = typeof configured === "string" ? configured.toLowerCase() : target.href;
|
|
2496
|
+
const preserveAzureUrlFeatures = typeof nativeProviderName === "string" && nativeProviderName.toLowerCase() === "azure" || [
|
|
2497
|
+
"openai.azure.",
|
|
2498
|
+
"cognitiveservices.azure.",
|
|
2499
|
+
"aoai.azure.",
|
|
2500
|
+
"azure-api.",
|
|
2501
|
+
"azurefd.",
|
|
2502
|
+
"windows.net/openai"
|
|
2503
|
+
].some((marker) => nativeBaseUrl.includes(marker));
|
|
2504
|
+
const preserveCodexBackendRoutes = nativeProviderName === "OpenAI" && (configured == null || configured.replace(/\/+$/, "").endsWith("/backend-api/codex"));
|
|
2505
|
+
const memoryFeature = params.config?.["features.memories"] ?? (isJsonObject(params.config?.features) ? params.config.features.memories : void 0) ?? (isJsonObject(snapshot.config.features) ? snapshot.config.features.memories : void 0);
|
|
2506
|
+
owner.memoryConfigured ||= memoryFeature === true;
|
|
2507
|
+
const modelPolicyEnforced = params.modelPolicyEnforced !== false;
|
|
2508
|
+
const key = JSON.stringify([
|
|
2509
|
+
provider,
|
|
2510
|
+
target.toString(),
|
|
2511
|
+
kind,
|
|
2512
|
+
preserveAzureUrlFeatures,
|
|
2513
|
+
preserveCodexBackendRoutes,
|
|
2514
|
+
modelPolicyEnforced
|
|
2515
|
+
]);
|
|
2233
2516
|
let pending = owner.routes.get(key);
|
|
2234
2517
|
if (!pending) {
|
|
2235
|
-
if (owner.routes.size >= MAX_ROUTES)
|
|
2236
|
-
|
|
2518
|
+
if (owner.routes.size >= MAX_ROUTES) {
|
|
2519
|
+
if (params.optionalProjection) return unsupported();
|
|
2520
|
+
throw new Error("Codex inference route limit reached; start a fresh managed native connection before retrying.");
|
|
2521
|
+
}
|
|
2522
|
+
pending = Promise.all([
|
|
2523
|
+
import("./inference-proxy-DhcXOgUE.mjs"),
|
|
2524
|
+
import("./native-subagent-monitor-Dpsmf2wz.mjs").then((n) => n.n),
|
|
2525
|
+
import("./inference-dispatch-CMZtjQyz.mjs")
|
|
2526
|
+
]).then(([{ createCodexInferenceProxy }, native, { createCodexInferenceModelBinding }]) => {
|
|
2237
2527
|
assertClient();
|
|
2238
2528
|
return createCodexInferenceProxy({
|
|
2239
2529
|
upstream: target,
|
|
2240
|
-
assertCurrent: assertClient
|
|
2530
|
+
assertCurrent: assertClient,
|
|
2531
|
+
oauth: owner.oauth,
|
|
2532
|
+
preserveAzureUrlFeatures,
|
|
2533
|
+
preserveCodexBackendRoutes,
|
|
2534
|
+
bindModelExecution: createCodexInferenceModelBinding({
|
|
2535
|
+
client,
|
|
2536
|
+
provider,
|
|
2537
|
+
modelPolicyEnforced,
|
|
2538
|
+
assertCurrent: assertClient,
|
|
2539
|
+
memoryConfigured: () => owner.memoryConfigured,
|
|
2540
|
+
captureModelSource: native.codexNativeSubagentMonitorRuntime.captureModelSource,
|
|
2541
|
+
resolveModelThreadId: native.codexNativeSubagentMonitorRuntime.resolveModelThreadId
|
|
2542
|
+
})
|
|
2241
2543
|
});
|
|
2242
2544
|
});
|
|
2243
2545
|
owner.routes.set(key, pending);
|
|
@@ -2246,14 +2548,41 @@ async function prepareCodexInferenceRoute(params) {
|
|
|
2246
2548
|
assertCurrent();
|
|
2247
2549
|
route.assertCurrent();
|
|
2248
2550
|
params.client.protectPrivateTransportSecret(new URL(route.baseUrl).pathname.split("/")[1] ?? "");
|
|
2249
|
-
owner.handles.
|
|
2551
|
+
owner.handles.set(route, {
|
|
2552
|
+
provider,
|
|
2553
|
+
kind,
|
|
2554
|
+
modelPolicyEnforced
|
|
2555
|
+
});
|
|
2250
2556
|
return route;
|
|
2251
2557
|
}
|
|
2252
2558
|
/** Prepare a managed thread without changing an attached or unsupported native profile. */
|
|
2253
2559
|
async function prepareCodexInferenceThreadConfig(params) {
|
|
2254
2560
|
const { binding } = params;
|
|
2255
|
-
|
|
2256
|
-
|
|
2561
|
+
const owner = owners.get(params.client);
|
|
2562
|
+
if (!owner) return;
|
|
2563
|
+
if (owner.closed) throw new Error("Codex inference route ownership changed; reconnect before retrying");
|
|
2564
|
+
const preserved = binding?.connectionScope === "supervision" || binding?.preserveNativeModel === true;
|
|
2565
|
+
let retained = preserved && binding?.clientId === params.clientId ? owner.threads.get(binding.threadId) : void 0;
|
|
2566
|
+
if (preserved) {
|
|
2567
|
+
if (owner.oauth) throw new Error("ChatGPT subscription sharing cannot attach to a native Codex thread.");
|
|
2568
|
+
if (!retained) return;
|
|
2569
|
+
retained.route.assertCurrent();
|
|
2570
|
+
if (owner.handles.get(retained.route)?.modelPolicyEnforced !== (params.modelPolicyEnforced !== false)) retained = void 0;
|
|
2571
|
+
else if (!params.operatorBacked) return {
|
|
2572
|
+
route: retained.route,
|
|
2573
|
+
providers: retained.providers,
|
|
2574
|
+
config: projectProviderRoutes(params.config, retained.providers)
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
params.signal?.throwIfAborted();
|
|
2578
|
+
params.assertCurrent();
|
|
2579
|
+
const effectiveConfig = params.effectiveConfig ?? await readCodexEffectiveConfig(params.client, params.cwd, { signal: params.signal });
|
|
2580
|
+
params.signal?.throwIfAborted();
|
|
2581
|
+
params.assertCurrent();
|
|
2582
|
+
const route = retained?.route ?? await prepareCodexInferenceRoute({
|
|
2583
|
+
...params,
|
|
2584
|
+
effectiveConfig
|
|
2585
|
+
});
|
|
2257
2586
|
if (!route) return;
|
|
2258
2587
|
if (binding?.clientId === params.clientId && !getCodexInferenceThread(params.client, binding.threadId)) {
|
|
2259
2588
|
const { thread } = await params.client.request("thread/read", {
|
|
@@ -2266,23 +2595,65 @@ async function prepareCodexInferenceThreadConfig(params) {
|
|
|
2266
2595
|
params.assertCurrent();
|
|
2267
2596
|
if (thread.id !== binding.threadId || thread.status?.type !== "notLoaded") throw new Error("Codex loaded thread has no owned inference route; reconnect before retrying");
|
|
2268
2597
|
}
|
|
2269
|
-
|
|
2270
|
-
|
|
2598
|
+
const provider = owner.handles.get(route)?.provider;
|
|
2599
|
+
if (!provider) throw new Error("Codex inference provider ownership changed");
|
|
2600
|
+
if (owner.oauth) return {
|
|
2271
2601
|
route,
|
|
2272
2602
|
config: {
|
|
2273
2603
|
...params.config,
|
|
2274
|
-
|
|
2604
|
+
model_provider: CODEX_RESPONSES_OAUTH_PROVIDER,
|
|
2605
|
+
model_providers: { [CODEX_RESPONSES_OAUTH_PROVIDER]: responsesOAuthProvider(route) }
|
|
2275
2606
|
}
|
|
2276
2607
|
};
|
|
2608
|
+
if (!params.operatorBacked) return {
|
|
2609
|
+
route,
|
|
2610
|
+
config: withProviderBaseUrl(params.config, provider, route.baseUrl)
|
|
2611
|
+
};
|
|
2612
|
+
const providers = new Map(retained?.providers ?? [[provider, route]]);
|
|
2613
|
+
for (const candidate of configuredProviders(effectiveConfig.config, params.config)) {
|
|
2614
|
+
if (providers.has(candidate)) continue;
|
|
2615
|
+
const sibling = await prepareCodexInferenceRoute({
|
|
2616
|
+
...params,
|
|
2617
|
+
effectiveConfig,
|
|
2618
|
+
modelProvider: candidate,
|
|
2619
|
+
optionalProjection: true
|
|
2620
|
+
});
|
|
2621
|
+
if (sibling) providers.set(candidate, sibling);
|
|
2622
|
+
}
|
|
2623
|
+
return {
|
|
2624
|
+
route,
|
|
2625
|
+
config: projectProviderRoutes(params.config, providers),
|
|
2626
|
+
providers
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
function responsesOAuthProvider(route) {
|
|
2630
|
+
return {
|
|
2631
|
+
name: "OpenClaw subscription sharing",
|
|
2632
|
+
base_url: route.baseUrl,
|
|
2633
|
+
wire_api: "responses",
|
|
2634
|
+
requires_openai_auth: true,
|
|
2635
|
+
supports_websockets: false
|
|
2636
|
+
};
|
|
2277
2637
|
}
|
|
2278
2638
|
/** Validate the exact private handle and unchanged upstream, not a localhost string exception. */
|
|
2279
|
-
function assertCodexInferenceRouteConfig(client, route, config) {
|
|
2639
|
+
function assertCodexInferenceRouteConfig(client, route, config, modelProvider, providers) {
|
|
2280
2640
|
if (!route) return;
|
|
2281
2641
|
const owner = owners.get(client);
|
|
2282
|
-
|
|
2642
|
+
const provider = (owner?.handles.get(route))?.provider;
|
|
2643
|
+
const selectedProvider = modelProvider ?? config?.model_provider ?? provider;
|
|
2644
|
+
const systemProxy = config?.["features.respect_system_proxy"] ?? (isJsonObject(config?.features) ? config.features.respect_system_proxy : void 0);
|
|
2645
|
+
if (!owner || owner.closed || !provider || selectedProvider !== provider || systemProxy === true || readProviderBaseUrl(config, provider) !== route.baseUrl) throw new Error("Codex parent-local inference route was overridden; no turn was sent");
|
|
2646
|
+
if (owner.oauth && (config?.model_provider !== "openclaw_token_sharing" || !isJsonObject(config?.model_providers) || !isDeepStrictEqual(config.model_providers["openclaw_token_sharing"], responsesOAuthProvider(route)) || Object.keys(config).some((key) => key.startsWith(`model_providers.openclaw_token_sharing`)))) throw new Error("Codex parent-local inference route was overridden; no turn was sent");
|
|
2283
2647
|
route.assertCurrent();
|
|
2648
|
+
for (const [candidate, sibling] of providers ?? [[provider, route]]) {
|
|
2649
|
+
const siblingDefinition = owner.handles.get(sibling);
|
|
2650
|
+
const name = readProviderField(config, candidate, "name");
|
|
2651
|
+
const wireApi = readProviderField(config, candidate, "wire_api");
|
|
2652
|
+
if (siblingDefinition?.provider !== candidate || readProviderBaseUrl(config, candidate) !== sibling.baseUrl || candidate !== "openai" && (name !== void 0 && providerKind(name) !== siblingDefinition.kind || wireApi !== void 0 && wireApi !== "responses" || hasProviderAws(config, candidate))) throw new Error("Codex native provider inference route was overridden; no turn was sent");
|
|
2653
|
+
sibling.assertCurrent();
|
|
2654
|
+
}
|
|
2284
2655
|
}
|
|
2285
|
-
function bindCodexInferenceThread(client, threadId, route) {
|
|
2656
|
+
function bindCodexInferenceThread(client, threadId, route, providers) {
|
|
2286
2657
|
const owner = owners.get(client);
|
|
2287
2658
|
if (!owner && !route) return;
|
|
2288
2659
|
if (!owner || owner.closed) throw new Error("Codex inference client is closed");
|
|
@@ -2291,30 +2662,53 @@ function bindCodexInferenceThread(client, threadId, route) {
|
|
|
2291
2662
|
return;
|
|
2292
2663
|
}
|
|
2293
2664
|
route.assertCurrent();
|
|
2665
|
+
const provider = owner.handles.get(route)?.provider;
|
|
2666
|
+
if (!provider) throw new Error("Codex inference provider ownership changed");
|
|
2294
2667
|
if (!owner.threads.has(threadId) && owner.threads.size >= MAX_THREADS) throw new Error("Codex inference thread limit reached; reconnect before retrying");
|
|
2295
|
-
|
|
2668
|
+
const projected = new Map(providers ?? [[provider, route]]);
|
|
2669
|
+
if (projected.get(provider) !== route) throw new Error("Codex inference provider ownership changed");
|
|
2670
|
+
const assertCurrent = () => {
|
|
2671
|
+
if (owner.closed || owners.get(client) !== owner) throw new Error("Codex inference client is closed");
|
|
2672
|
+
for (const [candidate, routed] of projected) {
|
|
2673
|
+
if (owner.handles.get(routed)?.provider !== candidate) throw new Error("Codex inference provider ownership changed");
|
|
2674
|
+
routed.assertCurrent();
|
|
2675
|
+
}
|
|
2676
|
+
};
|
|
2677
|
+
assertCurrent();
|
|
2678
|
+
const qualification = owner.handles.get(route)?.modelPolicyEnforced ? Object.freeze({
|
|
2679
|
+
assertCurrent,
|
|
2680
|
+
hasProvider: (candidate) => {
|
|
2681
|
+
assertCurrent();
|
|
2682
|
+
const projectedRoute = projected.get(candidate);
|
|
2683
|
+
return projectedRoute !== void 0 && owner.handles.get(projectedRoute)?.modelPolicyEnforced === true;
|
|
2684
|
+
}
|
|
2685
|
+
}) : void 0;
|
|
2686
|
+
owner.threads.set(threadId, {
|
|
2687
|
+
route,
|
|
2688
|
+
providers: projected,
|
|
2689
|
+
qualification
|
|
2690
|
+
});
|
|
2296
2691
|
}
|
|
2297
2692
|
function getCodexInferenceThread(client, threadId) {
|
|
2298
2693
|
const owner = owners.get(client);
|
|
2299
2694
|
if (owner?.closed) throw new Error("Codex inference client is closed");
|
|
2300
|
-
return owner?.threads.get(threadId);
|
|
2695
|
+
return owner?.threads.get(threadId)?.route;
|
|
2696
|
+
}
|
|
2697
|
+
/** Configuration provenance for native restore admission, independent of the input's source. */
|
|
2698
|
+
function getCodexInferenceThreadQualification(client, threadId) {
|
|
2699
|
+
const owner = owners.get(client);
|
|
2700
|
+
if (owner?.closed) throw new Error("Codex inference client is closed");
|
|
2701
|
+
return owner?.threads.get(threadId)?.qualification;
|
|
2301
2702
|
}
|
|
2302
2703
|
//#endregion
|
|
2303
2704
|
//#region extensions/codex/src/app-server/native-config-fence.ts
|
|
2705
|
+
/** Serializes this Gateway's native config writes with its config-loading requests. */
|
|
2304
2706
|
const CODEX_NATIVE_CONFIG_FENCE_STATE = Symbol.for("openclaw.codexNativeConfigFenceState");
|
|
2305
|
-
function getFenceState() {
|
|
2306
|
-
const globalState = globalThis;
|
|
2307
|
-
globalState[CODEX_NATIVE_CONFIG_FENCE_STATE] ??= /* @__PURE__ */ new Map();
|
|
2308
|
-
return globalState[CODEX_NATIVE_CONFIG_FENCE_STATE];
|
|
2309
|
-
}
|
|
2310
2707
|
/** Acquires the per-CODEX_HOME fence and returns an idempotent release. */
|
|
2311
2708
|
async function acquireCodexNativeConfigFence(key, options = {}) {
|
|
2312
|
-
const state =
|
|
2709
|
+
const state = resolveGlobalMap(CODEX_NATIVE_CONFIG_FENCE_STATE);
|
|
2313
2710
|
const previous = state.get(key) ?? Promise.resolve();
|
|
2314
|
-
|
|
2315
|
-
const current = new Promise((resolve) => {
|
|
2316
|
-
resolveCurrent = resolve;
|
|
2317
|
-
});
|
|
2711
|
+
const { promise: current, resolve: resolveCurrent } = createDeferred();
|
|
2318
2712
|
state.set(key, current);
|
|
2319
2713
|
try {
|
|
2320
2714
|
await waitForPreviousFence(previous, options);
|
|
@@ -2407,9 +2801,14 @@ function resolveCodexAppServerSpawnIdentity(startOptions, resolvedNativeCommand)
|
|
|
2407
2801
|
}
|
|
2408
2802
|
//#endregion
|
|
2409
2803
|
//#region extensions/codex/src/app-server/thread-lifecycle-errors.ts
|
|
2804
|
+
const CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED = "CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED";
|
|
2805
|
+
/** True when a pre-write context restart must replay on the newly selected owner. */
|
|
2806
|
+
function isCodexContextRestartSelectionChangedError(error) {
|
|
2807
|
+
return error instanceof Error && "code" in error && error.code === "CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED";
|
|
2808
|
+
}
|
|
2410
2809
|
var CodexThreadStartRequestError = class extends Error {
|
|
2411
2810
|
constructor(cause) {
|
|
2412
|
-
super(`thread/start: ${formatErrorMessage(cause)}`, { cause });
|
|
2811
|
+
super(`thread/start: ${formatErrorMessage$1(cause)}`, { cause });
|
|
2413
2812
|
this.name = "CodexThreadStartRequestError";
|
|
2414
2813
|
}
|
|
2415
2814
|
};
|
|
@@ -2433,10 +2832,6 @@ var CodexAdoptedThreadActiveError = class extends AgentHarnessPreflightError$1 {
|
|
|
2433
2832
|
};
|
|
2434
2833
|
//#endregion
|
|
2435
2834
|
//#region extensions/codex/src/app-server/shared-client.ts
|
|
2436
|
-
/**
|
|
2437
|
-
* Owns shared and isolated Codex app-server client startup, auth application,
|
|
2438
|
-
* lease tracking, and teardown.
|
|
2439
|
-
*/
|
|
2440
2835
|
var shared_client_exports = /* @__PURE__ */ __exportAll({
|
|
2441
2836
|
assertCodexAppServerClientStartSelectionCurrent: () => assertCodexAppServerClientStartSelectionCurrent,
|
|
2442
2837
|
captureCodexAppServerClientLifetime: () => captureCodexAppServerClientLifetime,
|
|
@@ -2455,7 +2850,6 @@ var shared_client_exports = /* @__PURE__ */ __exportAll({
|
|
|
2455
2850
|
releaseCodexAppServerClientLease: () => releaseCodexAppServerClientLease,
|
|
2456
2851
|
releaseLeasedSharedCodexAppServerClient: () => releaseLeasedSharedCodexAppServerClient,
|
|
2457
2852
|
resolveCodexNativeConfigFenceKey: () => resolveCodexNativeConfigFenceKey,
|
|
2458
|
-
retainSharedCodexAppServerClientByInstanceId: () => retainSharedCodexAppServerClientByInstanceId,
|
|
2459
2853
|
retainSharedCodexAppServerClientIfCurrent: () => retainSharedCodexAppServerClientIfCurrent,
|
|
2460
2854
|
retireSharedCodexAppServerClientIfCurrent: () => retireSharedCodexAppServerClientIfCurrent,
|
|
2461
2855
|
waitForCodexAppServerClientDesktopGenerationDrain: () => waitForCodexAppServerClientDesktopGenerationDrain,
|
|
@@ -2463,12 +2857,6 @@ var shared_client_exports = /* @__PURE__ */ __exportAll({
|
|
|
2463
2857
|
});
|
|
2464
2858
|
const SHARED_CODEX_APP_SERVER_CLIENT_DISPOSER = codexBuildSymbol("openclaw.codexAppServerClientDisposer");
|
|
2465
2859
|
const CODEX_APP_SERVER_INITIALIZE_TIMEOUT_MESSAGE = "codex app-server initialize timed out";
|
|
2466
|
-
function ownCodexStartup(lifetime, operation) {
|
|
2467
|
-
lifetime.pending.add(operation);
|
|
2468
|
-
const release = () => lifetime.pending.delete(operation);
|
|
2469
|
-
operation.then(release, release);
|
|
2470
|
-
return operation;
|
|
2471
|
-
}
|
|
2472
2860
|
async function prepareCodexAppServerClient(options) {
|
|
2473
2861
|
const lifetime = getSharedCodexAppServerClientState().startup;
|
|
2474
2862
|
const abandonSignal = options?.abandonSignal ? AbortSignal.any([lifetime.controller.signal, options.abandonSignal]) : lifetime.controller.signal;
|
|
@@ -2497,11 +2885,9 @@ function readCodexAppServerClientProcessIdentity(client) {
|
|
|
2497
2885
|
...runtimeIdentity?.userAgent ? { userAgent: runtimeIdentity.userAgent } : {}
|
|
2498
2886
|
};
|
|
2499
2887
|
}
|
|
2500
|
-
/** Returns the lifecycle fingerprint that owns a managed desktop client. */
|
|
2501
2888
|
function readCodexAppServerClientDesktopGenerationFingerprint(client) {
|
|
2502
2889
|
return readCodexAppServerClientDesktopGeneration(client)?.fingerprint;
|
|
2503
2890
|
}
|
|
2504
|
-
/** Returns the lifecycle generation that owns a managed desktop client. */
|
|
2505
2891
|
function readCodexAppServerClientDesktopGeneration(client) {
|
|
2506
2892
|
return getSharedCodexAppServerClientState().startMetadata.get(client)?.desktopGeneration;
|
|
2507
2893
|
}
|
|
@@ -2548,19 +2934,14 @@ function assertCodexAppServerClientStartSelectionCurrent(params) {
|
|
|
2548
2934
|
});
|
|
2549
2935
|
if ((metadata.startOptions.managedCommandOrder ?? "package-first") !== (current.managedCommandOrder ?? "package-first")) throw new CodexAppServerStartSelectionChangedError();
|
|
2550
2936
|
}
|
|
2551
|
-
/** Resolves the per-CODEX_HOME key used to serialize native config loading. */
|
|
2552
2937
|
function resolveCodexNativeConfigFenceKey(params) {
|
|
2553
2938
|
const metadata = params.client ? getSharedCodexAppServerClientState().startMetadata.get(params.client) : void 0;
|
|
2554
2939
|
const startOptions = metadata?.startOptions ?? params.startOptions;
|
|
2555
2940
|
if (!startOptions || startOptions.transport !== "stdio") return;
|
|
2556
|
-
const configuredHome = startOptions.env?.CODEX_HOME?.trim();
|
|
2941
|
+
const configuredHome = startOptions.codexHome ?? startOptions.env?.CODEX_HOME?.trim();
|
|
2557
2942
|
const codexHome = configuredHome ? configuredHome : startOptions.homeScope === "user" ? resolveCodexAppServerUserHomeDir() : resolveCodexAppServerHomeDir(params.agentDir ?? metadata?.agentDir ?? resolveDefaultAgentDir(params.config ?? {}));
|
|
2558
2943
|
return codexHome ? `codex-home:${path.resolve(codexHome)}` : void 0;
|
|
2559
2944
|
}
|
|
2560
|
-
function inferAuthRequirement(preparedAuth) {
|
|
2561
|
-
if (preparedAuth?.kind === "api-key") return "api-key";
|
|
2562
|
-
return preparedAuth?.kind === "profile" ? "subscription" : void 0;
|
|
2563
|
-
}
|
|
2564
2945
|
async function resolveCodexAppServerClientStartContext(options) {
|
|
2565
2946
|
const requestedStartOptions = options?.startOptions ?? resolveCodexAppServerRuntimeOptions({ pluginConfig: options?.pluginConfig }).start;
|
|
2566
2947
|
const agentDir = options?.agentDir ?? (requestedStartOptions.homeScope === "user" ? void 0 : resolveDefaultAgentDir(options?.config ?? {}));
|
|
@@ -2568,12 +2949,12 @@ async function resolveCodexAppServerClientStartContext(options) {
|
|
|
2568
2949
|
const preparedAuth = options?.preparedAuth;
|
|
2569
2950
|
const preparedApiKey = preparedAuth?.kind === "api-key" ? preparedAuth.apiKey.trim() : void 0;
|
|
2570
2951
|
if (preparedAuth && options?.authProfileId !== void 0) throw new Error("Prepared Codex auth cannot also select a legacy auth profile.");
|
|
2571
|
-
if (preparedAuth?.kind === "profile" && !preparedAuth.store.profiles[preparedAuth.profileId]) throw new
|
|
2952
|
+
if (preparedAuth?.kind === "profile" && !preparedAuth.store.profiles[preparedAuth.profileId]) throw new CodexAppServerAuthProfileUnavailableError(formatCodexAuthProfileUnavailableMessage(preparedAuth.profileId));
|
|
2572
2953
|
if (preparedAuth?.kind === "api-key" && !preparedApiKey) throw new Error("Prepared Codex API-key auth is missing its resolved key.");
|
|
2573
2954
|
if (preparedAuth && requestedStartOptions.homeScope === "user") throw new Error("Prepared Codex auth requires an isolated app-server home.");
|
|
2574
|
-
const preparedAuthRequirement =
|
|
2955
|
+
const preparedAuthRequirement = preparedAuth && (preparedAuth.kind === "api-key" || isCodexResponsesOAuth(preparedAuth) ? "api-key" : "subscription");
|
|
2575
2956
|
if (options?.authRequirement && preparedAuthRequirement && options.authRequirement !== preparedAuthRequirement) throw new Error("Prepared Codex auth does not satisfy the requested auth requirement.");
|
|
2576
|
-
|
|
2957
|
+
let authRequirement = options?.authRequirement ?? preparedAuthRequirement;
|
|
2577
2958
|
const usesNativeAuth = !preparedAuth && (options?.authProfileId === null || requestedStartOptions.homeScope === "user");
|
|
2578
2959
|
const requestedAuthProfileId = preparedAuth?.kind === "profile" ? preparedAuth.profileId : options?.authProfileId ?? void 0;
|
|
2579
2960
|
const authProfileStore = preparedAuth?.kind === "profile" ? preparedAuth.store : !usesNativeAuth && preparedAuth?.kind !== "api-key" ? resolveCodexAppServerAuthProfileStore({
|
|
@@ -2594,7 +2975,7 @@ async function resolveCodexAppServerClientStartContext(options) {
|
|
|
2594
2975
|
agentDir,
|
|
2595
2976
|
config: options?.config
|
|
2596
2977
|
}) : void 0;
|
|
2597
|
-
if (preparedAuth?.kind === "profile" && !preparedAuthProfileSnapshot) throw new
|
|
2978
|
+
if (preparedAuth?.kind === "profile" && !preparedAuthProfileSnapshot) throw new CodexAppServerAuthProfileUnavailableError(`Prepared Codex auth profile "${preparedAuth.profileId}" is unusable. Repair or replace the selected OpenAI profile, then retry.`);
|
|
2598
2979
|
const resolvedPreparedAuth = preparedAuth?.kind === "api-key" ? {
|
|
2599
2980
|
kind: "api-key",
|
|
2600
2981
|
apiKey: preparedApiKey
|
|
@@ -2604,10 +2985,25 @@ async function resolveCodexAppServerClientStartContext(options) {
|
|
|
2604
2985
|
store: authProfileStore,
|
|
2605
2986
|
snapshot: preparedAuthProfileSnapshot
|
|
2606
2987
|
} : void 0;
|
|
2988
|
+
if (isCodexResponsesOAuth(resolvedPreparedAuth)) {
|
|
2989
|
+
if (authRequirement && authRequirement !== "api-key") throw new Error("ChatGPT subscription sharing requires the public OpenAI API route.");
|
|
2990
|
+
authRequirement = "api-key";
|
|
2991
|
+
}
|
|
2607
2992
|
const agentStartOptions = resolveCodexAppServerStartOptionsForAgent({
|
|
2608
2993
|
startOptions: requestedStartOptions,
|
|
2609
2994
|
agentDir
|
|
2610
2995
|
});
|
|
2996
|
+
const startOptions = await bridgeCodexAppServerStartOptions({
|
|
2997
|
+
startOptions: await resolveManagedCodexAppServerStartOptions(agentStartOptions),
|
|
2998
|
+
agentId: options?.agentId,
|
|
2999
|
+
agentDir,
|
|
3000
|
+
authProfileId: usesNativeAuth || preparedAuth?.kind === "api-key" ? null : authProfileId,
|
|
3001
|
+
...(preparedAuth || isCodexResponsesOAuth(resolvedPreparedAuth)) && resolvedPreparedAuth ? { preparedAuth: resolvedPreparedAuth } : {},
|
|
3002
|
+
authRequirement,
|
|
3003
|
+
config: options?.config,
|
|
3004
|
+
pluginConfig: options?.pluginConfig,
|
|
3005
|
+
...authProfileStore ? { authProfileStore } : {}
|
|
3006
|
+
});
|
|
2611
3007
|
return {
|
|
2612
3008
|
agentDir,
|
|
2613
3009
|
usesNativeAuth,
|
|
@@ -2616,17 +3012,7 @@ async function resolveCodexAppServerClientStartContext(options) {
|
|
|
2616
3012
|
requestedStartOptions,
|
|
2617
3013
|
preparedAuth: resolvedPreparedAuth,
|
|
2618
3014
|
authRequirement,
|
|
2619
|
-
startOptions
|
|
2620
|
-
startOptions: await resolveManagedCodexAppServerStartOptions(agentStartOptions),
|
|
2621
|
-
agentId: options?.agentId,
|
|
2622
|
-
agentDir,
|
|
2623
|
-
authProfileId: usesNativeAuth || preparedAuth?.kind === "api-key" ? null : authProfileId,
|
|
2624
|
-
...preparedAuth && resolvedPreparedAuth ? { preparedAuth: resolvedPreparedAuth } : {},
|
|
2625
|
-
authRequirement,
|
|
2626
|
-
config: options?.config,
|
|
2627
|
-
pluginConfig: options?.pluginConfig,
|
|
2628
|
-
...authProfileStore ? { authProfileStore } : {}
|
|
2629
|
-
}),
|
|
3015
|
+
startOptions,
|
|
2630
3016
|
...options?.pluginConfig !== void 0 ? { pluginConfig: options.pluginConfig } : {},
|
|
2631
3017
|
...desktopGeneration ? { desktopGeneration } : {}
|
|
2632
3018
|
};
|
|
@@ -2644,7 +3030,6 @@ function getSharedCodexAppServerClient(options) {
|
|
|
2644
3030
|
function getLeasedSharedCodexAppServerClient(options) {
|
|
2645
3031
|
return acquireSharedCodexAppServerClient(options, true);
|
|
2646
3032
|
}
|
|
2647
|
-
/** Releases one outstanding lease for a shared Codex app-server client. */
|
|
2648
3033
|
function releaseLeasedSharedCodexAppServerClient(client) {
|
|
2649
3034
|
const entry = getSharedCodexAppServerClientState().entriesByClient.get(client);
|
|
2650
3035
|
if (!entry || entry.anonymousLeases === 0) return false;
|
|
@@ -2716,7 +3101,8 @@ async function acquireSharedCodexAppServerClient(options, leased = false) {
|
|
|
2716
3101
|
const state = getSharedCodexAppServerClientState();
|
|
2717
3102
|
const { context, lifetime, abandonSignal, startedAt, assertCurrent } = await prepareCodexAppServerClient(options);
|
|
2718
3103
|
assertCurrent();
|
|
2719
|
-
const {
|
|
3104
|
+
const { usesNativeAuth, ...startContext } = context;
|
|
3105
|
+
const { agentDir, authProfileId, authProfileStore, preparedAuth, authRequirement, startOptions, desktopGeneration } = startContext;
|
|
2720
3106
|
const remainingTimeoutMs = resolveRemainingAcquireTimeout(timeoutMs, startedAt);
|
|
2721
3107
|
const authIdentityCacheKey = preparedAuth?.kind === "api-key" ? resolveCodexAppServerPreparedApiKeyCacheKey(preparedAuth.apiKey) : preparedAuth?.snapshot.secretFreeCacheKey ?? (authRequirement === "api-key" && !authProfileId ? resolveCodexAppServerFallbackApiKeyCacheKey({ startOptions }) : void 0);
|
|
2722
3108
|
const baseKey = `${codexAppServerStartOptionsKey(startOptions, {
|
|
@@ -2758,17 +3144,10 @@ async function acquireSharedCodexAppServerClient(options, leased = false) {
|
|
|
2758
3144
|
if (options.abandonSignal.aborted) abandon();
|
|
2759
3145
|
}
|
|
2760
3146
|
const startup = entry.startup ?? (entry.startup = createSharedCodexAppServerClientStartup({
|
|
3147
|
+
...startContext,
|
|
2761
3148
|
lifetime,
|
|
2762
3149
|
entry,
|
|
2763
|
-
requestedStartOptions,
|
|
2764
|
-
startOptions,
|
|
2765
|
-
desktopGeneration,
|
|
2766
|
-
...pluginConfig !== void 0 ? { pluginConfig } : {},
|
|
2767
|
-
agentDir,
|
|
2768
3150
|
authProfileId: usesNativeAuth || preparedAuth?.kind === "api-key" ? null : authProfileId,
|
|
2769
|
-
authProfileStore,
|
|
2770
|
-
preparedAuth,
|
|
2771
|
-
authRequirement,
|
|
2772
3151
|
runtimeArtifactMode,
|
|
2773
3152
|
...options?.expectedRuntimeArtifact ? { expectedRuntimeArtifact: options.expectedRuntimeArtifact } : {},
|
|
2774
3153
|
abandonSignal: entry.startupAbort.signal,
|
|
@@ -2782,7 +3161,7 @@ async function acquireSharedCodexAppServerClient(options, leased = false) {
|
|
|
2782
3161
|
agentDir,
|
|
2783
3162
|
authProfileId: usesNativeAuth ? void 0 : authProfileId,
|
|
2784
3163
|
...authProfileStore ? { authProfileStore } : {},
|
|
2785
|
-
authMode: preparedAuth?.kind === "api-key" ? "prepared-api-key" : "profile",
|
|
3164
|
+
authMode: preparedAuth?.kind === "api-key" || isCodexResponsesOAuth(preparedAuth) ? "prepared-api-key" : "profile",
|
|
2786
3165
|
config: options?.config
|
|
2787
3166
|
});
|
|
2788
3167
|
if (leased) {
|
|
@@ -2793,6 +3172,7 @@ async function acquireSharedCodexAppServerClient(options, leased = false) {
|
|
|
2793
3172
|
} catch (error) {
|
|
2794
3173
|
releasePendingAcquire();
|
|
2795
3174
|
retirePendingSharedClientEntryIfUnclaimed(entry);
|
|
3175
|
+
await waitForUnclaimedSharedClientStartup(entry);
|
|
2796
3176
|
throw error;
|
|
2797
3177
|
} finally {
|
|
2798
3178
|
cleanupAbandonSignal?.();
|
|
@@ -2800,30 +3180,17 @@ async function acquireSharedCodexAppServerClient(options, leased = false) {
|
|
|
2800
3180
|
releasePendingAcquire();
|
|
2801
3181
|
}
|
|
2802
3182
|
}
|
|
2803
|
-
async function withCodexAppServerAcquireDeadline(timeoutMs, promise, signal, timeoutMessage = CODEX_APP_SERVER_INITIALIZE_TIMEOUT_MESSAGE, timeoutErrorFactory) {
|
|
2804
|
-
if (signal?.aborted) throw new CodexAppServerStartupError("aborted", "codex app-server initialize aborted");
|
|
2805
|
-
const timed = withTimeout(promise, timeoutMs, timeoutMessage, () => timeoutErrorFactory?.() ?? new CodexAppServerStartupError("timed_out", timeoutMessage));
|
|
2806
|
-
if (!signal) return await timed;
|
|
2807
|
-
return await new Promise((resolve, reject) => {
|
|
2808
|
-
const onAbort = () => reject(new CodexAppServerStartupError("aborted", "codex app-server initialize aborted"));
|
|
2809
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
2810
|
-
timed.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
2811
|
-
});
|
|
2812
|
-
}
|
|
2813
3183
|
function buildCodexAppServerInitializeTimeoutError(client) {
|
|
2814
3184
|
const stderr = client?.getStderrDiagnostic();
|
|
2815
3185
|
return new CodexAppServerStartupError("timed_out", stderr ? `${CODEX_APP_SERVER_INITIALIZE_TIMEOUT_MESSAGE}; stderr=${JSON.stringify(stderr)}` : CODEX_APP_SERVER_INITIALIZE_TIMEOUT_MESSAGE);
|
|
2816
3186
|
}
|
|
2817
|
-
function resolveRemainingAcquireTimeout(timeoutMs, startedAt) {
|
|
2818
|
-
if (!(timeoutMs > 0)) return timeoutMs;
|
|
2819
|
-
const remaining = timeoutMs - (performance.now() - startedAt);
|
|
2820
|
-
if (remaining <= 0) throw new CodexAppServerStartupError("timed_out", "codex app-server initialize timed out");
|
|
2821
|
-
return remaining;
|
|
2822
|
-
}
|
|
2823
3187
|
function createSharedCodexAppServerClientStartup(params) {
|
|
2824
3188
|
const initialized = createDeferred();
|
|
2825
3189
|
const ready = ownCodexStartup(params.lifetime, startInitializedCodexAppServerClient({
|
|
2826
3190
|
...params,
|
|
3191
|
+
onStartingClient: (starting) => {
|
|
3192
|
+
params.entry.startupTransport = starting;
|
|
3193
|
+
},
|
|
2827
3194
|
onStartedClient: (startedClient) => {
|
|
2828
3195
|
const state = getSharedCodexAppServerClientState();
|
|
2829
3196
|
if (params.entry.client) state.entriesByClient.delete(params.entry.client);
|
|
@@ -2863,18 +3230,11 @@ async function createIsolatedCodexAppServerClient(options) {
|
|
|
2863
3230
|
const { context, lifetime, abandonSignal, startedAt, assertCurrent } = await prepareCodexAppServerClient(options);
|
|
2864
3231
|
assertCurrent();
|
|
2865
3232
|
const timeoutMs = options?.timeoutMs ?? 0;
|
|
2866
|
-
const {
|
|
3233
|
+
const { usesNativeAuth, ...startContext } = context;
|
|
2867
3234
|
return await ownCodexStartup(lifetime, startInitializedCodexAppServerClient({
|
|
3235
|
+
...startContext,
|
|
2868
3236
|
lifetime,
|
|
2869
|
-
|
|
2870
|
-
startOptions,
|
|
2871
|
-
...desktopGeneration ? { desktopGeneration } : {},
|
|
2872
|
-
...pluginConfig !== void 0 ? { pluginConfig } : {},
|
|
2873
|
-
agentDir,
|
|
2874
|
-
authProfileId: usesNativeAuth || preparedAuth?.kind === "api-key" ? null : authProfileId,
|
|
2875
|
-
authProfileStore,
|
|
2876
|
-
preparedAuth,
|
|
2877
|
-
authRequirement,
|
|
3237
|
+
authProfileId: usesNativeAuth || context.preparedAuth?.kind === "api-key" ? null : context.authProfileId,
|
|
2878
3238
|
runtimeArtifactMode: options?.runtimeArtifactMode ?? (options?.expectedRuntimeArtifact ? "capture" : void 0),
|
|
2879
3239
|
...options?.expectedRuntimeArtifact ? { expectedRuntimeArtifact: options.expectedRuntimeArtifact } : {},
|
|
2880
3240
|
config: options?.config,
|
|
@@ -2895,7 +3255,17 @@ function trackIsolatedCodexAppServerClient(client) {
|
|
|
2895
3255
|
notifyDesktopGenerationDrainChecks(state);
|
|
2896
3256
|
});
|
|
2897
3257
|
}
|
|
2898
|
-
|
|
3258
|
+
function startInitializedCodexAppServerClient(params) {
|
|
3259
|
+
return withCodexWebSocketOpenRetry({
|
|
3260
|
+
timeoutMs: params.timeoutMs ?? 0,
|
|
3261
|
+
signal: params.abandonSignal ? AbortSignal.any([params.lifetime.controller.signal, params.abandonSignal]) : params.lifetime.controller.signal,
|
|
3262
|
+
start: (timeoutMs) => startInitializedCodexAppServerClientOnce({
|
|
3263
|
+
...params,
|
|
3264
|
+
timeoutMs
|
|
3265
|
+
})
|
|
3266
|
+
});
|
|
3267
|
+
}
|
|
3268
|
+
async function startInitializedCodexAppServerClientOnce(params) {
|
|
2899
3269
|
const acquireStartedAt = performance.now();
|
|
2900
3270
|
const timeoutMs = params.timeoutMs ?? 0;
|
|
2901
3271
|
const abandonSignal = params.abandonSignal ? AbortSignal.any([params.lifetime.controller.signal, params.abandonSignal]) : params.lifetime.controller.signal;
|
|
@@ -2906,7 +3276,8 @@ async function startInitializedCodexAppServerClient(params) {
|
|
|
2906
3276
|
const startOptionsCandidates = resolveManagedFallbackStartOptions(params.startOptions);
|
|
2907
3277
|
for (const [index, startOptions] of startOptionsCandidates.entries()) {
|
|
2908
3278
|
params.assertCurrent?.();
|
|
2909
|
-
const
|
|
3279
|
+
const desktopCommand = isManagedCodexDesktopCommand(startOptions.command);
|
|
3280
|
+
const desktopGeneration = params.desktopGeneration ?? (desktopCommand ? await waitForStartup(waitForCodexDesktopGeneration) : void 0);
|
|
2910
3281
|
const assertStartupCurrent = () => {
|
|
2911
3282
|
params.assertCurrent?.();
|
|
2912
3283
|
if (abandonSignal.aborted) throw new CodexAppServerStartupError("aborted", "codex app-server initialize aborted");
|
|
@@ -2941,7 +3312,7 @@ async function startInitializedCodexAppServerClient(params) {
|
|
|
2941
3312
|
} finally {
|
|
2942
3313
|
artifactDrain?.cancel();
|
|
2943
3314
|
}
|
|
2944
|
-
const runtimeArtifactModule = params.runtimeArtifactMode ? await import("./runtime-artifact-
|
|
3315
|
+
const runtimeArtifactModule = params.runtimeArtifactMode ? await import("./runtime-artifact-DDQ-HGXf.mjs") : void 0;
|
|
2945
3316
|
const runtimeArtifactBeforeStart = runtimeArtifactModule ? await runtimeArtifactModule.captureCodexAppServerRuntimeArtifactBeforeStart({
|
|
2946
3317
|
startOptions,
|
|
2947
3318
|
spawnIdentity: resolveCodexAppServerSpawnIdentity(startOptions),
|
|
@@ -2955,14 +3326,21 @@ async function startInitializedCodexAppServerClient(params) {
|
|
|
2955
3326
|
let starting;
|
|
2956
3327
|
let client;
|
|
2957
3328
|
try {
|
|
2958
|
-
client = await waitForStartup(() =>
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
3329
|
+
client = await waitForStartup(() => {
|
|
3330
|
+
starting = CodexAppServerClient.start(startOptions, () => {
|
|
3331
|
+
assertStartupCurrent();
|
|
3332
|
+
resolveRemainingAcquireTimeout(timeoutMs, acquireStartedAt);
|
|
3333
|
+
});
|
|
3334
|
+
params.onStartingClient?.(starting);
|
|
3335
|
+
return starting;
|
|
3336
|
+
});
|
|
2962
3337
|
} catch (error) {
|
|
2963
|
-
if (starting) ownCodexStartup(params.lifetime, starting.then((lateClient) => lateClient.closeAndWait(), () => {}));
|
|
3338
|
+
if (starting) await ownCodexStartup(params.lifetime, starting.then((lateClient) => lateClient.closeAndWait(), () => {}));
|
|
2964
3339
|
throw error;
|
|
2965
3340
|
}
|
|
3341
|
+
client.addCloseHandler((closedClient) => {
|
|
3342
|
+
ownCodexStartup(params.lifetime, closedClient.waitForCloseWork());
|
|
3343
|
+
});
|
|
2966
3344
|
let ready = false;
|
|
2967
3345
|
try {
|
|
2968
3346
|
const nativeCommandAtStart = startOptions.commandSource === "resolved-managed" ? resolveManagedCodexNativeCommand(startOptions.command) : void 0;
|
|
@@ -3001,7 +3379,7 @@ async function startInitializedCodexAppServerClient(params) {
|
|
|
3001
3379
|
ensureCodexAppServerClientRuntime(client, {
|
|
3002
3380
|
agentDir: params.agentDir,
|
|
3003
3381
|
authProfileId: params.authProfileId ?? void 0,
|
|
3004
|
-
authMode: params.preparedAuth?.kind === "api-key" ? "prepared-api-key" : "profile",
|
|
3382
|
+
authMode: params.preparedAuth?.kind === "api-key" || isCodexResponsesOAuth(params.preparedAuth) ? "prepared-api-key" : "profile",
|
|
3005
3383
|
...params.authProfileStore ? { authProfileStore: params.authProfileStore } : {},
|
|
3006
3384
|
config: params.config,
|
|
3007
3385
|
onAuthRefreshFailure: () => retireSharedCodexAppServerClientIfCurrent(client)
|
|
@@ -3018,7 +3396,18 @@ async function startInitializedCodexAppServerClient(params) {
|
|
|
3018
3396
|
assertCurrent: assertStartupCurrent,
|
|
3019
3397
|
...params.authProfileStore ? { authProfileStore: params.authProfileStore } : {}
|
|
3020
3398
|
}));
|
|
3021
|
-
|
|
3399
|
+
const ownsInference = startOptions.transport === "stdio" && nativeCommandAtStart && !isCodexAppServerProxyLaunch(startOptions.args);
|
|
3400
|
+
if (isCodexResponsesOAuth(params.preparedAuth) && (!ownsInference || desktopGeneration || desktopCommand)) throw new Error("ChatGPT subscription sharing requires a managed local Codex process.");
|
|
3401
|
+
if (ownsInference) {
|
|
3402
|
+
const prepared = params.preparedAuth;
|
|
3403
|
+
ownCodexInferenceClient(client, startOptions, isCodexResponsesOAuth(prepared) && prepared?.kind === "profile" ? createCodexResponsesOAuth({
|
|
3404
|
+
profileId: prepared.profileId,
|
|
3405
|
+
store: prepared.store,
|
|
3406
|
+
fingerprint: prepared.snapshot.secretFreeCacheKey,
|
|
3407
|
+
agentDir: params.agentDir,
|
|
3408
|
+
config: params.config
|
|
3409
|
+
}) : void 0);
|
|
3410
|
+
}
|
|
3022
3411
|
recordCodexAppServerAuthHandoff(client, authHandoff);
|
|
3023
3412
|
if (runtimeArtifactModule && runtimeArtifact) runtimeArtifactModule.bindCodexAppServerRuntimeArtifact(client, runtimeArtifact);
|
|
3024
3413
|
assertStartupCurrent();
|
|
@@ -3036,7 +3425,7 @@ async function startInitializedCodexAppServerClient(params) {
|
|
|
3036
3425
|
ready = true;
|
|
3037
3426
|
return client;
|
|
3038
3427
|
} finally {
|
|
3039
|
-
if (!ready) ownCodexStartup(params.lifetime, client.closeAndWait());
|
|
3428
|
+
if (!ready) await ownCodexStartup(params.lifetime, client.closeAndWait());
|
|
3040
3429
|
}
|
|
3041
3430
|
}
|
|
3042
3431
|
throw new Error("Managed Codex app-server fallback candidates were exhausted.");
|
|
@@ -3062,7 +3451,6 @@ function resolveManagedFallbackStartOptions(startOptions) {
|
|
|
3062
3451
|
function shouldTryManagedFallbackStartOption(error, startOptions, index, startOptionsCandidates) {
|
|
3063
3452
|
return startOptions.commandSource === "resolved-managed" && index < startOptionsCandidates.length - 1 && isUnsupportedCodexAppServerVersionError(error);
|
|
3064
3453
|
}
|
|
3065
|
-
/** Clears and closes the shared entry only if it still owns the supplied client. */
|
|
3066
3454
|
function clearSharedCodexAppServerClientIfCurrent(client) {
|
|
3067
3455
|
if (!client) return false;
|
|
3068
3456
|
const state = getSharedCodexAppServerClientState();
|
|
@@ -3085,24 +3473,10 @@ function captureSharedClientRegistration(client) {
|
|
|
3085
3473
|
const generation = readCodexAppServerClientDesktopGeneration(client);
|
|
3086
3474
|
return () => entry !== void 0 && state.clients.get(entry.key) === entry && entry.client === client && !entry.closeWhenIdle && !entry.closeError && !client.getCloseError() && (!generation || isCodexDesktopGenerationCurrent(generation));
|
|
3087
3475
|
}
|
|
3088
|
-
/** Retains the matching shared client and returns a release callback. */
|
|
3089
3476
|
function retainSharedCodexAppServerClientIfCurrent(client) {
|
|
3090
3477
|
const entry = getCurrentSharedClientEntry(client);
|
|
3091
3478
|
return entry ? retainSharedClientEntry(entry) : void 0;
|
|
3092
3479
|
}
|
|
3093
|
-
/** Retains the live shared client whose initialized instance id matches a thread binding. */
|
|
3094
|
-
function retainSharedCodexAppServerClientByInstanceId(clientId) {
|
|
3095
|
-
const normalizedClientId = clientId?.trim();
|
|
3096
|
-
if (!normalizedClientId) return;
|
|
3097
|
-
for (const entry of getSharedCodexAppServerClientState().clients.values()) {
|
|
3098
|
-
const client = entry.client;
|
|
3099
|
-
if (client?.getInstanceId() !== normalizedClientId || entry.closeWhenIdle || entry.closeError) continue;
|
|
3100
|
-
return {
|
|
3101
|
-
client,
|
|
3102
|
-
release: retainSharedClientEntry(entry)
|
|
3103
|
-
};
|
|
3104
|
-
}
|
|
3105
|
-
}
|
|
3106
3480
|
/** Captures physical ownership, independently of unrelated thread and reader leases. */
|
|
3107
3481
|
function captureCodexAppServerClientLifetime(client, requiredOwnership) {
|
|
3108
3482
|
const state = getSharedCodexAppServerClientState();
|
|
@@ -3127,10 +3501,7 @@ function createOlderDesktopGenerationDrainWait(params) {
|
|
|
3127
3501
|
};
|
|
3128
3502
|
const state = getSharedCodexAppServerClientState();
|
|
3129
3503
|
let settled = false;
|
|
3130
|
-
|
|
3131
|
-
const promise = new Promise((resolve) => {
|
|
3132
|
-
resolveWait = resolve;
|
|
3133
|
-
});
|
|
3504
|
+
const { promise, resolve: resolveWait } = createDeferred();
|
|
3134
3505
|
const cancel = () => {
|
|
3135
3506
|
if (settled) return;
|
|
3136
3507
|
settled = true;
|
|
@@ -3152,18 +3523,13 @@ function createOlderDesktopGenerationDrainWait(params) {
|
|
|
3152
3523
|
};
|
|
3153
3524
|
}
|
|
3154
3525
|
function hasLiveOlderDesktopGenerationClient(params) {
|
|
3155
|
-
for (const
|
|
3156
|
-
for (const client of params.state.isolatedClients) if (isOlderDesktopGenerationClientForHome(client, params.generation, params.targetHome)) return true;
|
|
3526
|
+
for (const clients of [params.state.liveClients, params.state.isolatedClients]) for (const client of clients) if (isOlderDesktopGenerationClientForHome(client, params.generation, params.targetHome)) return true;
|
|
3157
3527
|
return false;
|
|
3158
3528
|
}
|
|
3159
3529
|
function isOlderDesktopGenerationClientForHome(client, generation, targetHome) {
|
|
3160
3530
|
const metadata = getSharedCodexAppServerClientState().startMetadata.get(client);
|
|
3161
3531
|
return Boolean(metadata?.desktopGeneration && metadata.desktopGeneration.epoch < generation.epoch && resolveCodexNativeConfigFenceKey({ client }) === targetHome);
|
|
3162
3532
|
}
|
|
3163
|
-
function notifyDesktopGenerationDrainChecks(state) {
|
|
3164
|
-
for (const check of state.desktopGenerationDrainChecks) check();
|
|
3165
|
-
}
|
|
3166
|
-
/** Clears a matching shared client and waits for its process to exit. */
|
|
3167
3533
|
async function clearSharedCodexAppServerClientIfCurrentAndWait(client, options) {
|
|
3168
3534
|
if (!client) return false;
|
|
3169
3535
|
const state = getSharedCodexAppServerClientState();
|
|
@@ -3173,7 +3539,6 @@ async function clearSharedCodexAppServerClientIfCurrentAndWait(client, options)
|
|
|
3173
3539
|
await client.closeAndWait(options);
|
|
3174
3540
|
return true;
|
|
3175
3541
|
}
|
|
3176
|
-
/** Clears all shared clients and waits for their processes to exit. */
|
|
3177
3542
|
async function clearSharedCodexAppServerClientAndWait(options) {
|
|
3178
3543
|
const state = getSharedCodexAppServerClientState();
|
|
3179
3544
|
const lifetime = state.startup;
|
|
@@ -3205,7 +3570,6 @@ function getOrCreateSharedClientEntry(state, key) {
|
|
|
3205
3570
|
}
|
|
3206
3571
|
return entry;
|
|
3207
3572
|
}
|
|
3208
|
-
/** Clears a matching shared client only when no lease or acquire currently claims it. */
|
|
3209
3573
|
function clearSharedCodexAppServerClientIfCurrentAndUnclaimed(client) {
|
|
3210
3574
|
const entry = getCurrentSharedClientEntry(client);
|
|
3211
3575
|
return {
|
|
@@ -3215,20 +3579,6 @@ function clearSharedCodexAppServerClientIfCurrentAndUnclaimed(client) {
|
|
|
3215
3579
|
pendingAcquires: entry?.pendingAcquires ?? 0
|
|
3216
3580
|
};
|
|
3217
3581
|
}
|
|
3218
|
-
function retainSharedClientEntry(entry, counter = "activeLeases") {
|
|
3219
|
-
let released = false;
|
|
3220
|
-
entry[counter] += 1;
|
|
3221
|
-
return () => {
|
|
3222
|
-
if (released) return;
|
|
3223
|
-
released = true;
|
|
3224
|
-
releaseSharedClientEntry(entry, counter);
|
|
3225
|
-
};
|
|
3226
|
-
}
|
|
3227
|
-
function releaseSharedClientEntry(entry, counter) {
|
|
3228
|
-
entry[counter] -= 1;
|
|
3229
|
-
closeRetiredSharedClientEntryIfIdle(entry);
|
|
3230
|
-
notifyDesktopGenerationDrainChecks(getSharedCodexAppServerClientState());
|
|
3231
|
-
}
|
|
3232
3582
|
function closeSharedClientEntryIfUnclaimed(entry) {
|
|
3233
3583
|
if (entry.activeLeases > 0 || entry.pendingAcquires > 0) return false;
|
|
3234
3584
|
const state = getSharedCodexAppServerClientState();
|
|
@@ -3237,14 +3587,5 @@ function closeSharedClientEntryIfUnclaimed(entry) {
|
|
|
3237
3587
|
entry.client?.close();
|
|
3238
3588
|
return Boolean(entry.client);
|
|
3239
3589
|
}
|
|
3240
|
-
function retirePendingSharedClientEntryIfUnclaimed(entry) {
|
|
3241
|
-
if (entry.activeLeases > 0 || entry.pendingAcquires > 0) return;
|
|
3242
|
-
entry.startupAbort?.abort(/* @__PURE__ */ new Error("Codex app-server startup was abandoned"));
|
|
3243
|
-
entry.closeWhenIdle = true;
|
|
3244
|
-
const state = getSharedCodexAppServerClientState();
|
|
3245
|
-
if (state.clients.get(entry.key) === entry) state.clients.delete(entry.key);
|
|
3246
|
-
if (!entry.client) return;
|
|
3247
|
-
closeRetiredSharedClientEntry(entry);
|
|
3248
|
-
}
|
|
3249
3590
|
//#endregion
|
|
3250
|
-
export {
|
|
3591
|
+
export { reconcileCodexComputerUseStartArtifacts as $, bindCodexInferenceThread as A, isCodexAppServerClientRuntimeLive as B, CodexThreadBindingConflictError as C, nativeHookRelayUnregisterQueue as D, isCodexContextRestartSelectionChangedError as E, consumeCodexAppServerLiveThread as F, releaseCodexAppServerLiveThread as G, prepareCodexWorkspaceReferences as H, ensureCodexAppServerClientRuntime as I, unsubscribeCodexAppServerLiveThread as J, retainCodexAppServerLiveThread as K, forgetCodexWorkspaceReferences as L, getCodexInferenceThreadQualification as M, prepareCodexInferenceThreadConfig as N, acquireCodexNativeConfigFence as O, claimCodexAppServerLiveThread as P, createCodexEphemeralThreadPolicy as Q, hasCodexAppServerLiveThread as R, CodexAdoptedThreadActiveError as S, CodexThreadStartRequestError as T, protectCodexAppServerLiveThread as U, isCodexAppServerLiveThreadClaimed as V, readCodexClientSessionMeta as W, readRecentCodexRateLimits as X, readCodexRateLimitsRevision as Y, rememberCodexRateLimitsRead as Z, retainSharedCodexAppServerClientIfCurrent as _, clearSharedCodexAppServerClientIfCurrentAndUnclaimed as a, isLegacyCodexComputerUsePluginDisabled as at, withLeasedCodexAppServerClientStartSelectionRetry as b, getLeasedSharedCodexAppServerClient as c, assertNotSymlink as ct, readCodexAppServerClientDesktopGeneration as d, TURN_TERMINAL_SETTLEMENT_TIMEOUT_MS as dt, resolveCodexAppServerAuthAccountCacheKey as et, readCodexAppServerClientDesktopGenerationFingerprint as f, isCodexAppServerStartupError as ft, resolveCodexNativeConfigFenceKey as g, releaseLeasedSharedCodexAppServerClient as h, withCodexStartupTimeout as ht, clearSharedCodexAppServerClientIfCurrent as i, hasLegacyCodexComputerUseMcpPolicy as it, getCodexInferenceThread as j, assertCodexInferenceRouteConfig as k, getSharedCodexAppServerClient as l, CodexAppServerStartupError as lt, releaseCodexAppServerClientLease as m, resolveCodexStartupTimeoutMs as mt, captureCodexAppServerClientLifetime as n, resolveBundledComputerUseMarketplacePath as nt, clearSharedCodexAppServerClientIfCurrentAndWait as o, resolveManagedCodexComputerUseConfig as ot, readCodexAppServerClientProcessIdentity as p, resolveCodexGatewayTimeoutWithGraceMs as pt, revertCodexAppServerLiveThreadSkillsCatalog as q, captureSharedCodexAppServerCatalogLifetime as r, resolveClientManagedBundledMarketplacePath as rt, createIsolatedCodexAppServerClient as s, ensureCodexComputerUseSharedPluginCache as st, assertCodexAppServerClientStartSelectionCurrent as t, resolveCodexAppServerPreparedAuthHandoff as tt, isCodexAppServerStartSelectionChangedError as u, TURN_FINALIZE_DRAIN_ABORT_GRACE_MS as ut, shared_client_exports as v, CodexThreadClientReplacementError as w, CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED as x, waitForCodexAppServerClientDesktopGenerationDrain as y, hasCodexAppServerSiblingThreadWork as z };
|