@openclaw/codex 2026.6.11 → 2026.7.1-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-server-policy-BPTiVNsW.js → app-server-policy-C968Kgin.js} +5 -5
- package/dist/{attempt-notifications-BYnndyFl.js → attempt-notifications-BGsEIIDI.js} +29 -40
- package/dist/{command-handlers-DEPTJYk0.js → command-handlers-Dv-pzAmg.js} +278 -237
- package/dist/command-rpc-eLM_mtC7.js +784 -0
- package/dist/compact-4mVBriKT.js +603 -0
- package/dist/{computer-use-ClrJwoFp.js → computer-use-Bmaz333N.js} +3 -3
- package/dist/{config-CszD0vP3.js → config-CYEDnLJ2.js} +44 -18
- package/dist/conversation-binding-data-BHRpYbX3.js +121 -0
- package/dist/doctor-contract-api.js +590 -22
- package/dist/harness.js +29 -10
- package/dist/index.js +1134 -35
- package/dist/{media-understanding-provider-ZUQHN7gT.js → media-understanding-provider-BHOu9-Gg.js} +13 -8
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-CAmfBWRl.js → models-DDmO1zwd.js} +2 -2
- package/dist/notification-correlation-Bo7KB3ks.js +35 -0
- package/dist/{plugin-app-cache-key-C9ILt7bl.js → plugin-app-cache-key-BrhVdeEf.js} +13 -5
- package/dist/{protocol-dh-ETiNd.js → protocol-2POPqAY4.js} +2 -1
- package/dist/{protocol-validators-B19q5BIX.js → protocol-validators-dZQ-UTOa.js} +205 -56
- package/dist/provider-capabilities-DzI8fgEu.js +6172 -0
- package/dist/provider-catalog.js +4 -2
- package/dist/{provider-B-OHpbD3.js → provider-zjPfx5Fs.js} +94 -23
- package/dist/provider.js +2 -2
- package/dist/{request-Dcd452Nk.js → request-BcJyl8KL.js} +2 -2
- package/dist/{run-attempt-CXxtFC9d.js → run-attempt-DNSNk_G8.js} +1250 -2992
- package/dist/{sandbox-guard-3tnjhjFb.js → sandbox-guard-DA2TQfZW.js} +5 -3
- package/dist/session-binding-C1ZXdP-x.js +627 -0
- package/dist/session-binding-meta-B7aEMU7g.js +6 -0
- package/dist/shared-client-4ICy3U6d.js +1853 -0
- package/dist/{side-question-C-DhgJOd.js → side-question-CMOpEhvY.js} +134 -142
- package/dist/{thread-lifecycle-DtT3-ehU.js → thread-lifecycle-qWE88Dn2.js} +803 -481
- package/dist/{web-search-provider.runtime-CkwxrXQj.js → web-search-provider.runtime-BSlriav6.js} +3 -3
- package/npm-shrinkwrap.json +34 -34
- package/openclaw.plugin.json +23 -3
- package/package.json +9 -6
- package/dist/client-CLrtWgrD.js +0 -729
- package/dist/client-factory-Bm6HsGob.js +0 -18
- package/dist/compact-aQJ0ZKhe.js +0 -278
- package/dist/conversation-binding-t0XMLz-j.js +0 -1557
- package/dist/provider-capabilities-D7hnTO76.js +0 -3201
- package/dist/rate-limit-cache-C7qmZ0Jh.js +0 -26
- package/dist/session-binding-HOuPt-E0.js +0 -338
- package/dist/shared-client-Dtx5i7Ez.js +0 -1013
|
@@ -1,1013 +0,0 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
|
-
import { d as resolveCodexAppServerRuntimeOptions, r as codexAppServerStartOptionsKey } from "./config-CszD0vP3.js";
|
|
3
|
-
import { c as resolveCodexAppServerSpawnEnv, o as MANAGED_CODEX_APP_SERVER_PACKAGE, t as CodexAppServerClient } from "./client-CLrtWgrD.js";
|
|
4
|
-
import { createRequire } from "node:module";
|
|
5
|
-
import fs, { access } from "node:fs/promises";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import { createHash } from "node:crypto";
|
|
8
|
-
import fsSync, { constants, readFileSync } from "node:fs";
|
|
9
|
-
import os from "node:os";
|
|
10
|
-
import { ensureAuthProfileStore, findPersistedAuthProfileCredential, loadAuthProfileStoreForSecretsRuntime, refreshOAuthCredentialForRuntime, resolveApiKeyForProfile, resolveAuthProfileOrder, resolveDefaultAgentDir, resolvePersistedAuthProfileOwnerAgentDir, resolveProviderIdForAuth } from "openclaw/plugin-sdk/agent-runtime";
|
|
11
|
-
import { isDeepStrictEqual } from "node:util";
|
|
12
|
-
import { hasUsableOAuthCredential } from "openclaw/plugin-sdk/provider-auth";
|
|
13
|
-
import { fileURLToPath } from "node:url";
|
|
14
|
-
import { withTimeout } from "openclaw/plugin-sdk/security-runtime";
|
|
15
|
-
//#region extensions/codex/src/app-server/auth-bridge.ts
|
|
16
|
-
const CODEX_APP_SERVER_AUTH_PROVIDER = "openai";
|
|
17
|
-
const OPENAI_CODEX_APP_SERVER_AUTH_PROVIDER = "openai-codex";
|
|
18
|
-
const LEGACY_CODEX_APP_SERVER_AUTH_PROVIDER = "codex-cli";
|
|
19
|
-
const CODEX_APP_SERVER_EXTERNAL_CLI_PROVIDER_IDS = [CODEX_APP_SERVER_AUTH_PROVIDER, LEGACY_CODEX_APP_SERVER_AUTH_PROVIDER];
|
|
20
|
-
const OPENAI_PROVIDER = "openai";
|
|
21
|
-
const OPENAI_CODEX_DEFAULT_PROFILE_ID = "openai:default";
|
|
22
|
-
const CODEX_HOME_ENV_VAR = "CODEX_HOME";
|
|
23
|
-
const HOME_ENV_VAR = "HOME";
|
|
24
|
-
const CODEX_APP_SERVER_HOME_DIRNAME = "codex-home";
|
|
25
|
-
const CODEX_APP_SERVER_API_KEY_ENV_VARS = ["CODEX_API_KEY", "OPENAI_API_KEY"];
|
|
26
|
-
const CODEX_APP_SERVER_HOME_ENV_VARS = [CODEX_HOME_ENV_VAR, HOME_ENV_VAR];
|
|
27
|
-
const CODEX_AUTH_JSON_FILENAME = "auth.json";
|
|
28
|
-
const CODEX_HOME_DIRNAME = ".codex";
|
|
29
|
-
const scopedOAuthRefreshQueues = /* @__PURE__ */ new WeakMap();
|
|
30
|
-
async function bridgeCodexAppServerStartOptions(params) {
|
|
31
|
-
if (params.startOptions.transport !== "stdio") return params.startOptions;
|
|
32
|
-
const isolatedStartOptions = await withAgentCodexHomeEnvironment(params.startOptions, params.agentDir);
|
|
33
|
-
if (params.authProfileId === null) return isolatedStartOptions;
|
|
34
|
-
const store = resolveCodexAppServerAuthProfileStore({
|
|
35
|
-
agentDir: params.agentDir,
|
|
36
|
-
authProfileId: params.authProfileId,
|
|
37
|
-
authProfileStore: params.authProfileStore,
|
|
38
|
-
config: params.config
|
|
39
|
-
});
|
|
40
|
-
return shouldClearOpenAiApiKeyForCodexAuthProfile({
|
|
41
|
-
store,
|
|
42
|
-
authProfileId: resolveCodexAppServerAuthProfileId({
|
|
43
|
-
authProfileId: params.authProfileId,
|
|
44
|
-
store,
|
|
45
|
-
config: params.config
|
|
46
|
-
}),
|
|
47
|
-
config: params.config
|
|
48
|
-
}) ? withClearedEnvironmentVariables(isolatedStartOptions, CODEX_APP_SERVER_API_KEY_ENV_VARS) : isolatedStartOptions;
|
|
49
|
-
}
|
|
50
|
-
function resolveCodexAppServerAuthProfileId(params) {
|
|
51
|
-
const requested = params.authProfileId?.trim();
|
|
52
|
-
if (requested) return requested;
|
|
53
|
-
return resolveAuthProfileOrder({
|
|
54
|
-
cfg: params.config,
|
|
55
|
-
store: params.store,
|
|
56
|
-
provider: CODEX_APP_SERVER_AUTH_PROVIDER
|
|
57
|
-
})[0]?.trim();
|
|
58
|
-
}
|
|
59
|
-
function resolveCodexAppServerAuthProfileIdForAgent(params) {
|
|
60
|
-
const store = resolveCodexAppServerAuthProfileStore({
|
|
61
|
-
agentDir: params.agentDir?.trim() || resolveDefaultAgentDir(params.config ?? {}),
|
|
62
|
-
authProfileId: params.authProfileId,
|
|
63
|
-
authProfileStore: params.authProfileStore,
|
|
64
|
-
config: params.config
|
|
65
|
-
});
|
|
66
|
-
return resolveCodexAppServerAuthProfileId({
|
|
67
|
-
authProfileId: params.authProfileId,
|
|
68
|
-
store,
|
|
69
|
-
config: params.config
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
function ensureCodexAppServerAuthProfileStore(params) {
|
|
73
|
-
return ensureAuthProfileStore(params.agentDir, {
|
|
74
|
-
allowKeychainPrompt: false,
|
|
75
|
-
config: params.config,
|
|
76
|
-
externalCliProviderIds: CODEX_APP_SERVER_EXTERNAL_CLI_PROVIDER_IDS,
|
|
77
|
-
...params.authProfileId ? { externalCliProfileIds: [params.authProfileId] } : {}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
function resolveCodexAppServerAuthProfileStore(params) {
|
|
81
|
-
if (params.authProfileStore) {
|
|
82
|
-
const providedProfileId = resolveCodexAppServerAuthProfileId({
|
|
83
|
-
authProfileId: params.authProfileId,
|
|
84
|
-
store: params.authProfileStore,
|
|
85
|
-
config: params.config
|
|
86
|
-
});
|
|
87
|
-
if (providedProfileId && params.authProfileStore.profiles[providedProfileId]) return params.authProfileStore;
|
|
88
|
-
}
|
|
89
|
-
const overlaidStore = ensureCodexAppServerAuthProfileStore({
|
|
90
|
-
agentDir: params.agentDir,
|
|
91
|
-
authProfileId: params.authProfileId,
|
|
92
|
-
config: params.config
|
|
93
|
-
});
|
|
94
|
-
if (!params.authProfileStore) return overlaidStore;
|
|
95
|
-
const order = params.authProfileStore.order || overlaidStore.order ? {
|
|
96
|
-
...overlaidStore.order,
|
|
97
|
-
...params.authProfileStore.order
|
|
98
|
-
} : void 0;
|
|
99
|
-
const profiles = {
|
|
100
|
-
...overlaidStore.profiles,
|
|
101
|
-
...params.authProfileStore.profiles
|
|
102
|
-
};
|
|
103
|
-
const suppliedProfileIds = new Set(Object.keys(params.authProfileStore.profiles));
|
|
104
|
-
const mergeRuntimeProfileIds = (overlaidIds, suppliedIds) => [...(overlaidIds ?? []).filter((profileId) => !suppliedProfileIds.has(profileId)), ...suppliedIds ?? []];
|
|
105
|
-
const runtimePersistedProfileIds = mergeRuntimeProfileIds(overlaidStore.runtimePersistedProfileIds, params.authProfileStore.runtimePersistedProfileIds).filter((profileId) => profiles[profileId]);
|
|
106
|
-
const runtimeExternalProfileIds = mergeRuntimeProfileIds(overlaidStore.runtimeExternalProfileIds, params.authProfileStore.runtimeExternalProfileIds).filter((profileId) => profiles[profileId]);
|
|
107
|
-
const runtimeExternalProfileIdsAuthoritative = overlaidStore.runtimeExternalProfileIdsAuthoritative === true || params.authProfileStore.runtimeExternalProfileIdsAuthoritative === true;
|
|
108
|
-
return {
|
|
109
|
-
...params.authProfileStore,
|
|
110
|
-
...order ? { order } : {},
|
|
111
|
-
profiles,
|
|
112
|
-
...runtimePersistedProfileIds.length > 0 ? { runtimePersistedProfileIds: [...new Set(runtimePersistedProfileIds)] } : {},
|
|
113
|
-
...runtimeExternalProfileIds.length > 0 || runtimeExternalProfileIdsAuthoritative ? {
|
|
114
|
-
runtimeExternalProfileIds: [...new Set(runtimeExternalProfileIds)],
|
|
115
|
-
...runtimeExternalProfileIdsAuthoritative ? { runtimeExternalProfileIdsAuthoritative: true } : {}
|
|
116
|
-
} : {}
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
async function resolveCodexAppServerAuthAccountCacheKey(params) {
|
|
120
|
-
const agentDir = params.agentDir?.trim() || resolveDefaultAgentDir(params.config ?? {});
|
|
121
|
-
const store = resolveCodexAppServerAuthProfileStore({
|
|
122
|
-
agentDir,
|
|
123
|
-
authProfileId: params.authProfileId,
|
|
124
|
-
authProfileStore: params.authProfileStore,
|
|
125
|
-
config: params.config
|
|
126
|
-
});
|
|
127
|
-
const profileId = resolveCodexAppServerAuthProfileId({
|
|
128
|
-
authProfileId: params.authProfileId,
|
|
129
|
-
store,
|
|
130
|
-
config: params.config
|
|
131
|
-
});
|
|
132
|
-
if (!profileId) return;
|
|
133
|
-
const credential = store.profiles[profileId];
|
|
134
|
-
if (!credential || !isCodexAppServerAuthProfileCredential(credential, params.config)) return;
|
|
135
|
-
if (credential.type === "api_key") {
|
|
136
|
-
const apiKey = (await resolveApiKeyForProfile({
|
|
137
|
-
store,
|
|
138
|
-
profileId,
|
|
139
|
-
agentDir
|
|
140
|
-
}))?.apiKey?.trim();
|
|
141
|
-
return apiKey ? `${resolveChatgptAccountId(profileId, credential)}:${fingerprintApiKeyAuthProfileCacheKey(apiKey)}` : resolveChatgptAccountId(profileId, credential);
|
|
142
|
-
}
|
|
143
|
-
if (credential.type === "token") {
|
|
144
|
-
const accessToken = (await resolveApiKeyForProfile({
|
|
145
|
-
store,
|
|
146
|
-
profileId,
|
|
147
|
-
agentDir
|
|
148
|
-
}))?.apiKey?.trim();
|
|
149
|
-
return accessToken ? `${resolveChatgptAccountId(profileId, credential)}:${fingerprintTokenAuthProfileCacheKey(accessToken)}` : resolveChatgptAccountId(profileId, credential);
|
|
150
|
-
}
|
|
151
|
-
return resolveChatgptAccountId(profileId, credential);
|
|
152
|
-
}
|
|
153
|
-
function resolveCodexAppServerEnvApiKeyCacheKey(params) {
|
|
154
|
-
if (params.startOptions.transport !== "stdio") return;
|
|
155
|
-
const apiKey = readFirstNonEmptyEnvEntry(resolveCodexAppServerSpawnEnv(params.startOptions, params.baseEnv ?? process.env, params.platform ?? process.platform), CODEX_APP_SERVER_API_KEY_ENV_VARS);
|
|
156
|
-
if (!apiKey) return;
|
|
157
|
-
const hash = createHash("sha256");
|
|
158
|
-
hash.update("openclaw:codex:app-server-env-api-key:v1");
|
|
159
|
-
hash.update("\0");
|
|
160
|
-
hash.update(apiKey.key);
|
|
161
|
-
hash.update("\0");
|
|
162
|
-
hash.update(apiKey.value);
|
|
163
|
-
return `${apiKey.key}:sha256:${hash.digest("hex")}`;
|
|
164
|
-
}
|
|
165
|
-
function resolveCodexAppServerFallbackApiKeyCacheKey(params) {
|
|
166
|
-
if (params.startOptions.transport !== "stdio") return;
|
|
167
|
-
return resolveCodexAppServerEnvApiKeyCacheKey(params) ?? resolveCodexCliAuthFileApiKeyCacheKey(params.baseEnv ?? process.env);
|
|
168
|
-
}
|
|
169
|
-
function fingerprintApiKeyAuthProfileCacheKey(apiKey) {
|
|
170
|
-
const hash = createHash("sha256");
|
|
171
|
-
hash.update("openclaw:codex:app-server-auth-profile-api-key:v1");
|
|
172
|
-
hash.update("\0");
|
|
173
|
-
hash.update(apiKey);
|
|
174
|
-
return `api_key:sha256:${hash.digest("hex")}`;
|
|
175
|
-
}
|
|
176
|
-
function fingerprintTokenAuthProfileCacheKey(accessToken) {
|
|
177
|
-
const hash = createHash("sha256");
|
|
178
|
-
hash.update("openclaw:codex:app-server-auth-profile-token:v1");
|
|
179
|
-
hash.update("\0");
|
|
180
|
-
hash.update(accessToken);
|
|
181
|
-
return `token:sha256:${hash.digest("hex")}`;
|
|
182
|
-
}
|
|
183
|
-
function fingerprintCodexCliAuthFileApiKeyCacheKey(apiKey) {
|
|
184
|
-
const hash = createHash("sha256");
|
|
185
|
-
hash.update("openclaw:codex:app-server-cli-auth-json-api-key:v1");
|
|
186
|
-
hash.update("\0");
|
|
187
|
-
hash.update(apiKey);
|
|
188
|
-
return `CODEX_AUTH_JSON:sha256:${hash.digest("hex")}`;
|
|
189
|
-
}
|
|
190
|
-
function resolveCodexAppServerHomeDir(agentDir) {
|
|
191
|
-
return path.join(path.resolve(agentDir), CODEX_APP_SERVER_HOME_DIRNAME);
|
|
192
|
-
}
|
|
193
|
-
async function withAgentCodexHomeEnvironment(startOptions, agentDir) {
|
|
194
|
-
const codexHome = startOptions.env?.[CODEX_HOME_ENV_VAR]?.trim() ? startOptions.env[CODEX_HOME_ENV_VAR] : resolveCodexAppServerHomeDir(agentDir);
|
|
195
|
-
const nativeHome = startOptions.env?.[HOME_ENV_VAR]?.trim() ? startOptions.env[HOME_ENV_VAR] : void 0;
|
|
196
|
-
await fs.mkdir(codexHome, { recursive: true });
|
|
197
|
-
if (nativeHome) await fs.mkdir(nativeHome, { recursive: true });
|
|
198
|
-
const nextStartOptions = {
|
|
199
|
-
...startOptions,
|
|
200
|
-
env: {
|
|
201
|
-
...startOptions.env,
|
|
202
|
-
[CODEX_HOME_ENV_VAR]: codexHome,
|
|
203
|
-
...nativeHome ? { [HOME_ENV_VAR]: nativeHome } : {}
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
const clearEnv = withoutClearedCodexHomeEnv(startOptions.clearEnv);
|
|
207
|
-
if (clearEnv) nextStartOptions.clearEnv = clearEnv;
|
|
208
|
-
else delete nextStartOptions.clearEnv;
|
|
209
|
-
return nextStartOptions;
|
|
210
|
-
}
|
|
211
|
-
function withoutClearedCodexHomeEnv(clearEnv) {
|
|
212
|
-
if (!clearEnv) return;
|
|
213
|
-
const reserved = new Set(CODEX_APP_SERVER_HOME_ENV_VARS);
|
|
214
|
-
const filtered = clearEnv.filter((envVar) => !reserved.has(envVar.trim().toUpperCase()));
|
|
215
|
-
return filtered.length === clearEnv.length ? clearEnv : filtered;
|
|
216
|
-
}
|
|
217
|
-
async function applyCodexAppServerAuthProfile(params) {
|
|
218
|
-
if (params.authProfileId === null) return;
|
|
219
|
-
const loginParams = await resolveCodexAppServerAuthProfileLoginParams({
|
|
220
|
-
agentDir: params.agentDir,
|
|
221
|
-
authProfileId: params.authProfileId,
|
|
222
|
-
authProfileStore: params.authProfileStore,
|
|
223
|
-
config: params.config
|
|
224
|
-
});
|
|
225
|
-
if (!loginParams) {
|
|
226
|
-
if (params.startOptions?.transport !== "stdio") return;
|
|
227
|
-
const env = resolveCodexAppServerSpawnEnv(params.startOptions, process.env);
|
|
228
|
-
const fallbackLoginParams = await resolveCodexAppServerFallbackApiKeyLoginParams({
|
|
229
|
-
client: params.client,
|
|
230
|
-
env,
|
|
231
|
-
codexCliAuthEnv: process.env
|
|
232
|
-
});
|
|
233
|
-
if (fallbackLoginParams) await params.client.request("account/login/start", fallbackLoginParams);
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
await params.client.request("account/login/start", loginParams);
|
|
237
|
-
}
|
|
238
|
-
function resolveCodexAppServerAuthProfileLoginParams(params) {
|
|
239
|
-
return resolveCodexAppServerAuthProfileLoginParamsInternal(params);
|
|
240
|
-
}
|
|
241
|
-
async function refreshCodexAppServerAuthTokens(params) {
|
|
242
|
-
const loginParams = await resolveCodexAppServerAuthProfileLoginParamsInternal({
|
|
243
|
-
...params,
|
|
244
|
-
forceOAuthRefresh: true
|
|
245
|
-
});
|
|
246
|
-
if (!loginParams || loginParams.type !== "chatgptAuthTokens") throw new Error("Codex app-server ChatGPT token refresh requires an OAuth auth profile.");
|
|
247
|
-
return {
|
|
248
|
-
accessToken: loginParams.accessToken,
|
|
249
|
-
chatgptAccountId: loginParams.chatgptAccountId,
|
|
250
|
-
chatgptPlanType: loginParams.chatgptPlanType ?? null
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
async function resolveCodexAppServerAuthProfileLoginParamsInternal(params) {
|
|
254
|
-
const store = resolveCodexAppServerAuthProfileStore({
|
|
255
|
-
agentDir: params.agentDir,
|
|
256
|
-
authProfileId: params.authProfileId,
|
|
257
|
-
authProfileStore: params.authProfileStore,
|
|
258
|
-
config: params.config
|
|
259
|
-
});
|
|
260
|
-
const profileId = resolveCodexAppServerAuthProfileId({
|
|
261
|
-
authProfileId: params.authProfileId,
|
|
262
|
-
store,
|
|
263
|
-
config: params.config
|
|
264
|
-
});
|
|
265
|
-
if (!profileId) return;
|
|
266
|
-
const credential = store.profiles[profileId];
|
|
267
|
-
if (!credential) throw new Error(`Codex app-server auth profile "${profileId}" was not found.`);
|
|
268
|
-
if (!isCodexAppServerAuthProfileCredential(credential, params.config)) throw new Error(`Codex app-server auth profile "${profileId}" must be OpenAI Codex auth or an OpenAI API-key backup.`);
|
|
269
|
-
const loginParams = await resolveLoginParamsForCredential(profileId, credential, {
|
|
270
|
-
agentDir: params.agentDir,
|
|
271
|
-
store,
|
|
272
|
-
preferStoreCredential: Boolean(params.authProfileStore?.profiles[profileId]),
|
|
273
|
-
forceOAuthRefresh: params.forceOAuthRefresh === true,
|
|
274
|
-
config: params.config
|
|
275
|
-
});
|
|
276
|
-
if (!loginParams) throw new Error(`Codex app-server auth profile "${profileId}" does not contain usable credentials.`);
|
|
277
|
-
return loginParams;
|
|
278
|
-
}
|
|
279
|
-
async function resolveCodexAppServerFallbackApiKeyLoginParams(params) {
|
|
280
|
-
const apiKey = readFirstNonEmptyEnv(params.env, CODEX_APP_SERVER_API_KEY_ENV_VARS) ?? await readCodexCliAuthFileApiKey(params.codexCliAuthEnv);
|
|
281
|
-
if (!apiKey) return;
|
|
282
|
-
if ((await params.client.request("account/read", { refreshToken: false })).account) return;
|
|
283
|
-
return {
|
|
284
|
-
type: "apiKey",
|
|
285
|
-
apiKey
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
function resolveCodexCliAuthFilePath(env) {
|
|
289
|
-
const configuredCodexHome = env[CODEX_HOME_ENV_VAR]?.trim();
|
|
290
|
-
if (configuredCodexHome) return path.join(resolveHomeRelativePath(configuredCodexHome, env), CODEX_AUTH_JSON_FILENAME);
|
|
291
|
-
const home = env[HOME_ENV_VAR]?.trim() || env.USERPROFILE?.trim() || os.homedir();
|
|
292
|
-
return path.join(home, CODEX_HOME_DIRNAME, CODEX_AUTH_JSON_FILENAME);
|
|
293
|
-
}
|
|
294
|
-
function resolveHomeRelativePath(value, env) {
|
|
295
|
-
if (value === "~" || value.startsWith("~/") || value.startsWith("~\\")) {
|
|
296
|
-
const home = env[HOME_ENV_VAR]?.trim() || env.USERPROFILE?.trim() || os.homedir();
|
|
297
|
-
return path.join(home, value.slice(value === "~" ? 1 : 2));
|
|
298
|
-
}
|
|
299
|
-
return value;
|
|
300
|
-
}
|
|
301
|
-
function parseCodexCliAuthFileApiKey(raw) {
|
|
302
|
-
let parsed;
|
|
303
|
-
try {
|
|
304
|
-
parsed = JSON.parse(raw);
|
|
305
|
-
} catch {
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
if (!parsed || typeof parsed !== "object") return;
|
|
309
|
-
const apiKey = parsed.OPENAI_API_KEY;
|
|
310
|
-
return typeof apiKey === "string" && apiKey.trim() ? apiKey.trim() : void 0;
|
|
311
|
-
}
|
|
312
|
-
async function readCodexCliAuthFileApiKey(env) {
|
|
313
|
-
try {
|
|
314
|
-
return parseCodexCliAuthFileApiKey(await fs.readFile(resolveCodexCliAuthFilePath(env), "utf8"));
|
|
315
|
-
} catch {
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
function resolveCodexCliAuthFileApiKeyCacheKey(env) {
|
|
320
|
-
try {
|
|
321
|
-
const apiKey = parseCodexCliAuthFileApiKey(fsSync.readFileSync(resolveCodexCliAuthFilePath(env), "utf8"));
|
|
322
|
-
return apiKey ? fingerprintCodexCliAuthFileApiKeyCacheKey(apiKey) : void 0;
|
|
323
|
-
} catch {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
async function resolveLoginParamsForCredential(profileId, credential, params) {
|
|
328
|
-
if (credential.type === "api_key") {
|
|
329
|
-
const apiKey = (await resolveApiKeyForProfile({
|
|
330
|
-
store: params.preferStoreCredential ? params.store : ensureAuthProfileStore(params.agentDir, { allowKeychainPrompt: false }),
|
|
331
|
-
profileId,
|
|
332
|
-
agentDir: params.agentDir
|
|
333
|
-
}))?.apiKey?.trim();
|
|
334
|
-
return apiKey ? {
|
|
335
|
-
type: "apiKey",
|
|
336
|
-
apiKey
|
|
337
|
-
} : void 0;
|
|
338
|
-
}
|
|
339
|
-
if (credential.type === "token") {
|
|
340
|
-
const accessToken = (await resolveApiKeyForProfile({
|
|
341
|
-
store: params.preferStoreCredential ? params.store : ensureAuthProfileStore(params.agentDir, { allowKeychainPrompt: false }),
|
|
342
|
-
profileId,
|
|
343
|
-
agentDir: params.agentDir
|
|
344
|
-
}))?.apiKey?.trim();
|
|
345
|
-
return accessToken ? buildChatgptAuthTokensParams(profileId, credential, accessToken) : void 0;
|
|
346
|
-
}
|
|
347
|
-
if (credential.type !== "oauth") return;
|
|
348
|
-
const resolvedCredential = await resolveOAuthCredentialForCodexAppServer(profileId, credential, {
|
|
349
|
-
agentDir: params.agentDir,
|
|
350
|
-
store: params.store,
|
|
351
|
-
preferStoreCredential: params.preferStoreCredential,
|
|
352
|
-
forceRefresh: params.forceOAuthRefresh,
|
|
353
|
-
config: params.config
|
|
354
|
-
});
|
|
355
|
-
const accessToken = resolvedCredential.access?.trim();
|
|
356
|
-
return accessToken ? buildChatgptAuthTokensParams(profileId, resolvedCredential, accessToken) : void 0;
|
|
357
|
-
}
|
|
358
|
-
async function resolveOAuthCredentialForCodexAppServer(profileId, credential, params) {
|
|
359
|
-
const ownerAgentDir = resolvePersistedAuthProfileOwnerAgentDir({
|
|
360
|
-
agentDir: params.agentDir,
|
|
361
|
-
profileId
|
|
362
|
-
});
|
|
363
|
-
const persistedCredential = findPersistedAuthProfileCredential({
|
|
364
|
-
agentDir: ownerAgentDir,
|
|
365
|
-
profileId
|
|
366
|
-
});
|
|
367
|
-
const useScopedCredential = params.preferStoreCredential && shouldUseScopedOAuthCredential({
|
|
368
|
-
store: params.store,
|
|
369
|
-
profileId,
|
|
370
|
-
persistedCredential,
|
|
371
|
-
suppliedCredential: credential,
|
|
372
|
-
config: params.config
|
|
373
|
-
});
|
|
374
|
-
const store = useScopedCredential ? params.store : ensureCodexAppServerAuthProfileStore({
|
|
375
|
-
agentDir: ownerAgentDir,
|
|
376
|
-
authProfileId: profileId,
|
|
377
|
-
config: params.config
|
|
378
|
-
});
|
|
379
|
-
const persistedOAuthCredential = !useScopedCredential && persistedCredential?.type === "oauth" && isCodexAppServerAuthProvider(persistedCredential.provider, params.config) ? persistedCredential : void 0;
|
|
380
|
-
const ownerCredential = store.profiles[profileId];
|
|
381
|
-
const overlaidOAuthCredential = ownerCredential?.type === "oauth" && isCodexAppServerAuthProvider(ownerCredential.provider, params.config) ? ownerCredential : void 0;
|
|
382
|
-
if (useScopedCredential && overlaidOAuthCredential) return await resolveScopedOAuthCredential({
|
|
383
|
-
store,
|
|
384
|
-
profileId,
|
|
385
|
-
credential: overlaidOAuthCredential,
|
|
386
|
-
forceRefresh: params.forceRefresh
|
|
387
|
-
});
|
|
388
|
-
if (params.forceRefresh && !persistedOAuthCredential && overlaidOAuthCredential) {
|
|
389
|
-
const refreshedRuntimeCredential = await refreshOAuthCredentialForRuntime({ credential: overlaidOAuthCredential });
|
|
390
|
-
if (!refreshedRuntimeCredential?.access?.trim()) throw new Error(`Codex app-server auth profile "${profileId}" could not refresh.`);
|
|
391
|
-
store.profiles[profileId] = refreshedRuntimeCredential;
|
|
392
|
-
return refreshedRuntimeCredential;
|
|
393
|
-
}
|
|
394
|
-
const resolved = await resolveApiKeyForProfile({
|
|
395
|
-
store,
|
|
396
|
-
profileId,
|
|
397
|
-
agentDir: ownerAgentDir,
|
|
398
|
-
forceRefresh: params.forceRefresh && Boolean(persistedOAuthCredential)
|
|
399
|
-
});
|
|
400
|
-
const refreshed = useScopedCredential ? void 0 : loadAuthProfileStoreForSecretsRuntime(ownerAgentDir).profiles[profileId];
|
|
401
|
-
const refreshedOAuthCredential = refreshed?.type === "oauth" && isCodexAppServerAuthProvider(refreshed.provider, params.config) ? refreshed : void 0;
|
|
402
|
-
if (refreshedOAuthCredential && isDeepStrictEqual(params.store.profiles[profileId], credential)) params.store.profiles[profileId] = refreshedOAuthCredential;
|
|
403
|
-
const storedCredential = store.profiles[profileId];
|
|
404
|
-
const candidate = refreshedOAuthCredential ? refreshedOAuthCredential : storedCredential?.type === "oauth" && isCodexAppServerAuthProvider(storedCredential.provider, params.config) ? storedCredential : credential;
|
|
405
|
-
return resolved?.apiKey ? {
|
|
406
|
-
...candidate,
|
|
407
|
-
access: resolved.apiKey
|
|
408
|
-
} : candidate;
|
|
409
|
-
}
|
|
410
|
-
function shouldUseScopedOAuthCredential(params) {
|
|
411
|
-
if (!params.store.runtimePersistedProfileIds?.includes(params.profileId)) return true;
|
|
412
|
-
const persisted = params.persistedCredential;
|
|
413
|
-
if (persisted?.type !== "oauth") return true;
|
|
414
|
-
if (resolveProviderIdForAuth(persisted.provider, { config: params.config }) !== resolveProviderIdForAuth(params.suppliedCredential.provider, { config: params.config })) return true;
|
|
415
|
-
return !isDeepStrictEqual(persisted, params.suppliedCredential) && !hasMatchingOAuthIdentity(persisted, params.suppliedCredential);
|
|
416
|
-
}
|
|
417
|
-
function hasMatchingOAuthIdentity(persisted, supplied) {
|
|
418
|
-
const persistedAccountId = persisted.accountId?.trim();
|
|
419
|
-
const suppliedAccountId = supplied.accountId?.trim();
|
|
420
|
-
if (persistedAccountId && suppliedAccountId) return persistedAccountId === suppliedAccountId;
|
|
421
|
-
const persistedEmail = persisted.email?.trim().toLowerCase();
|
|
422
|
-
const suppliedEmail = supplied.email?.trim().toLowerCase();
|
|
423
|
-
return Boolean(persistedEmail && suppliedEmail && persistedEmail === suppliedEmail);
|
|
424
|
-
}
|
|
425
|
-
async function resolveScopedOAuthCredential(params) {
|
|
426
|
-
const existingRefresh = scopedOAuthRefreshQueues.get(params.store)?.get(params.profileId);
|
|
427
|
-
if (existingRefresh) return await existingRefresh;
|
|
428
|
-
if (!params.forceRefresh && hasUsableOAuthCredential(params.credential)) return params.credential;
|
|
429
|
-
const storeRefreshes = scopedOAuthRefreshQueues.get(params.store) ?? /* @__PURE__ */ new Map();
|
|
430
|
-
scopedOAuthRefreshQueues.set(params.store, storeRefreshes);
|
|
431
|
-
const refresh = (async () => {
|
|
432
|
-
const current = params.store.profiles[params.profileId];
|
|
433
|
-
const credential = current?.type === "oauth" ? current : params.credential;
|
|
434
|
-
if (!params.forceRefresh && hasUsableOAuthCredential(credential)) return credential;
|
|
435
|
-
const refreshed = await refreshOAuthCredentialForRuntime({ credential });
|
|
436
|
-
if (!refreshed?.access?.trim()) throw new Error(`Codex app-server auth profile "${params.profileId}" could not refresh.`);
|
|
437
|
-
if (!isDeepStrictEqual(params.store.profiles[params.profileId], credential)) throw new Error(`Codex app-server auth profile "${params.profileId}" changed while refreshing.`);
|
|
438
|
-
params.store.profiles[params.profileId] = refreshed;
|
|
439
|
-
return refreshed;
|
|
440
|
-
})();
|
|
441
|
-
storeRefreshes.set(params.profileId, refresh);
|
|
442
|
-
try {
|
|
443
|
-
return await refresh;
|
|
444
|
-
} finally {
|
|
445
|
-
if (storeRefreshes.get(params.profileId) === refresh) storeRefreshes.delete(params.profileId);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
function isCodexAppServerAuthProvider(provider, config) {
|
|
449
|
-
const resolvedProvider = resolveProviderIdForAuth(provider, { config });
|
|
450
|
-
return resolvedProvider === CODEX_APP_SERVER_AUTH_PROVIDER || resolvedProvider === OPENAI_CODEX_APP_SERVER_AUTH_PROVIDER || resolvedProvider === LEGACY_CODEX_APP_SERVER_AUTH_PROVIDER;
|
|
451
|
-
}
|
|
452
|
-
function isOpenAIApiKeyBackupCredential(credential, config) {
|
|
453
|
-
return credential.type === "api_key" && resolveProviderIdForAuth(credential.provider, { config }) === OPENAI_PROVIDER;
|
|
454
|
-
}
|
|
455
|
-
function isCodexAppServerAuthProfileCredential(credential, config) {
|
|
456
|
-
return isCodexAppServerAuthProvider(credential.provider, config) || isOpenAIApiKeyBackupCredential(credential, config);
|
|
457
|
-
}
|
|
458
|
-
function shouldClearOpenAiApiKeyForCodexAuthProfile(params) {
|
|
459
|
-
const profileId = params.authProfileId?.trim();
|
|
460
|
-
return isCodexSubscriptionCredential(profileId ? params.store.profiles[profileId] : params.store.profiles[OPENAI_CODEX_DEFAULT_PROFILE_ID], params.config);
|
|
461
|
-
}
|
|
462
|
-
function isCodexSubscriptionCredential(credential, config) {
|
|
463
|
-
if (!credential || !isCodexAppServerAuthProvider(credential.provider, config)) return false;
|
|
464
|
-
return credential.type === "oauth" || credential.type === "token";
|
|
465
|
-
}
|
|
466
|
-
function withClearedEnvironmentVariables(startOptions, envVars) {
|
|
467
|
-
const clearEnv = startOptions.clearEnv ?? [];
|
|
468
|
-
const missingEnvVars = envVars.filter((envVar) => !clearEnv.includes(envVar));
|
|
469
|
-
if (missingEnvVars.length === 0) return startOptions;
|
|
470
|
-
return {
|
|
471
|
-
...startOptions,
|
|
472
|
-
clearEnv: [...clearEnv, ...missingEnvVars]
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
function readFirstNonEmptyEnv(env, keys) {
|
|
476
|
-
return readFirstNonEmptyEnvEntry(env, keys)?.value;
|
|
477
|
-
}
|
|
478
|
-
function readFirstNonEmptyEnvEntry(env, keys) {
|
|
479
|
-
for (const key of keys) {
|
|
480
|
-
const value = env[key]?.trim();
|
|
481
|
-
if (value) return {
|
|
482
|
-
key,
|
|
483
|
-
value
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
function buildChatgptAuthTokensParams(profileId, credential, accessToken) {
|
|
488
|
-
return {
|
|
489
|
-
type: "chatgptAuthTokens",
|
|
490
|
-
accessToken,
|
|
491
|
-
chatgptAccountId: resolveChatgptAccountId(profileId, credential),
|
|
492
|
-
chatgptPlanType: resolveChatgptPlanType(credential)
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
function resolveChatgptPlanType(credential) {
|
|
496
|
-
const record = credential;
|
|
497
|
-
const planType = record.chatgptPlanType ?? record.planType;
|
|
498
|
-
return typeof planType === "string" && planType.trim() ? planType.trim() : null;
|
|
499
|
-
}
|
|
500
|
-
function resolveChatgptAccountId(profileId, credential) {
|
|
501
|
-
if ("accountId" in credential && typeof credential.accountId === "string") {
|
|
502
|
-
const accountId = credential.accountId.trim();
|
|
503
|
-
if (accountId) return accountId;
|
|
504
|
-
}
|
|
505
|
-
return credential.email?.trim() || profileId;
|
|
506
|
-
}
|
|
507
|
-
//#endregion
|
|
508
|
-
//#region extensions/codex/src/app-server/managed-binary.ts
|
|
509
|
-
/**
|
|
510
|
-
* Resolves the managed Codex app-server binary shipped with or installed beside
|
|
511
|
-
* the Codex plugin before stdio startup.
|
|
512
|
-
*/
|
|
513
|
-
const CODEX_PLUGIN_ROOT = resolveDefaultCodexPluginRoot(path.dirname(fileURLToPath(import.meta.url)));
|
|
514
|
-
/** Rewrites managed stdio start options to point at an executable Codex binary path. */
|
|
515
|
-
async function resolveManagedCodexAppServerStartOptions(startOptions, options = {}) {
|
|
516
|
-
if (startOptions.transport !== "stdio" || startOptions.commandSource !== "managed") return startOptions;
|
|
517
|
-
const platform = options.platform ?? process.platform;
|
|
518
|
-
const paths = resolveManagedCodexAppServerPaths({
|
|
519
|
-
platform,
|
|
520
|
-
pluginRoot: options.pluginRoot
|
|
521
|
-
});
|
|
522
|
-
const pathExists = options.pathExists ?? commandPathExists;
|
|
523
|
-
const commandPath = await findManagedCodexAppServerCommandPath({
|
|
524
|
-
candidateCommandPaths: paths.candidateCommandPaths,
|
|
525
|
-
pathExists,
|
|
526
|
-
platform
|
|
527
|
-
});
|
|
528
|
-
return {
|
|
529
|
-
...startOptions,
|
|
530
|
-
command: commandPath,
|
|
531
|
-
commandSource: "resolved-managed"
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
/** Returns the preferred and fallback managed Codex binary paths for a plugin root. */
|
|
535
|
-
function resolveManagedCodexAppServerPaths(params) {
|
|
536
|
-
const platform = params.platform ?? process.platform;
|
|
537
|
-
const candidateCommandPaths = resolveManagedCodexAppServerCommandCandidates(params.pluginRoot ?? CODEX_PLUGIN_ROOT, platform);
|
|
538
|
-
return {
|
|
539
|
-
commandPath: candidateCommandPaths[0] ?? "",
|
|
540
|
-
candidateCommandPaths
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
function resolveManagedCodexAppServerCommandCandidates(pluginRoot, platform) {
|
|
544
|
-
const pathApi = pathForPlatform(platform);
|
|
545
|
-
const commandName = platform === "win32" ? "codex.cmd" : "codex";
|
|
546
|
-
const roots = resolveManagedCodexAppServerCandidateRoots(pluginRoot, platform);
|
|
547
|
-
return [...new Set([...roots.map((root) => pathApi.join(root, "node_modules", ".bin", commandName)), ...resolveManagedCodexPackageBinCandidates(roots, platform)])];
|
|
548
|
-
}
|
|
549
|
-
function resolveDefaultCodexPluginRoot(moduleDir) {
|
|
550
|
-
const moduleBaseName = path.basename(moduleDir);
|
|
551
|
-
if (moduleBaseName === "dist" || moduleBaseName === "dist-runtime") return path.dirname(moduleDir);
|
|
552
|
-
return path.resolve(moduleDir, "..", "..");
|
|
553
|
-
}
|
|
554
|
-
function resolveManagedCodexAppServerCandidateRoots(pluginRoot, platform) {
|
|
555
|
-
const pathApi = pathForPlatform(platform);
|
|
556
|
-
const directRoots = [
|
|
557
|
-
pluginRoot,
|
|
558
|
-
pathApi.dirname(pluginRoot),
|
|
559
|
-
pathApi.dirname(pathApi.dirname(pluginRoot)),
|
|
560
|
-
isDistExtensionRoot(pluginRoot, platform) ? pathApi.dirname(pathApi.dirname(pathApi.dirname(pluginRoot))) : null
|
|
561
|
-
].filter((root) => Boolean(root));
|
|
562
|
-
return [...new Set([...directRoots, ...resolveNearestNodeModulesProjectRoots(directRoots, platform)])];
|
|
563
|
-
}
|
|
564
|
-
function resolveNearestNodeModulesProjectRoots(roots, platform) {
|
|
565
|
-
const pathApi = pathForPlatform(platform);
|
|
566
|
-
const projectRoots = [];
|
|
567
|
-
for (const root of roots) {
|
|
568
|
-
let current = pathApi.resolve(root);
|
|
569
|
-
while (true) {
|
|
570
|
-
if (pathApi.basename(current) === "node_modules") {
|
|
571
|
-
projectRoots.push(pathApi.dirname(current));
|
|
572
|
-
break;
|
|
573
|
-
}
|
|
574
|
-
const parent = pathApi.dirname(current);
|
|
575
|
-
if (parent === current) break;
|
|
576
|
-
current = parent;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
return projectRoots;
|
|
580
|
-
}
|
|
581
|
-
function resolveManagedCodexPackageBinCandidates(roots, platform) {
|
|
582
|
-
if (platform === "win32") return [];
|
|
583
|
-
const candidates = [];
|
|
584
|
-
for (const root of roots) {
|
|
585
|
-
const candidate = resolveManagedCodexPackageBinCandidate(root);
|
|
586
|
-
if (candidate) candidates.push(candidate);
|
|
587
|
-
}
|
|
588
|
-
return candidates;
|
|
589
|
-
}
|
|
590
|
-
function resolveManagedCodexPackageBinCandidate(root) {
|
|
591
|
-
try {
|
|
592
|
-
const packageJsonPath = createRequire(path.join(root, "package.json")).resolve(`${MANAGED_CODEX_APP_SERVER_PACKAGE}/package.json`);
|
|
593
|
-
const packageRoot = path.dirname(packageJsonPath);
|
|
594
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
595
|
-
const binPath = typeof packageJson.bin === "string" ? packageJson.bin : isRecord(packageJson.bin) && typeof packageJson.bin.codex === "string" ? packageJson.bin.codex : null;
|
|
596
|
-
return binPath ? path.resolve(packageRoot, binPath) : null;
|
|
597
|
-
} catch {
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
function isRecord(value) {
|
|
602
|
-
return typeof value === "object" && value !== null;
|
|
603
|
-
}
|
|
604
|
-
function isDistExtensionRoot(pluginRoot, platform) {
|
|
605
|
-
const pathApi = pathForPlatform(platform);
|
|
606
|
-
const extensionsDir = pathApi.dirname(pluginRoot);
|
|
607
|
-
const distDir = pathApi.dirname(extensionsDir);
|
|
608
|
-
return pathApi.basename(extensionsDir) === "extensions" && (pathApi.basename(distDir) === "dist" || pathApi.basename(distDir) === "dist-runtime");
|
|
609
|
-
}
|
|
610
|
-
function pathForPlatform(platform) {
|
|
611
|
-
return platform === "win32" ? path.win32 : path.posix;
|
|
612
|
-
}
|
|
613
|
-
async function findManagedCodexAppServerCommandPath(params) {
|
|
614
|
-
for (const commandPath of params.candidateCommandPaths) if (await params.pathExists(commandPath, params.platform)) return commandPath;
|
|
615
|
-
throw new Error([
|
|
616
|
-
`Managed Codex app-server binary was not found for ${MANAGED_CODEX_APP_SERVER_PACKAGE}.`,
|
|
617
|
-
"Reinstall or update OpenClaw, or run pnpm install in a source checkout.",
|
|
618
|
-
"Set plugins.entries.codex.config.appServer.command or OPENCLAW_CODEX_APP_SERVER_BIN to use a custom Codex binary."
|
|
619
|
-
].join(" "));
|
|
620
|
-
}
|
|
621
|
-
async function commandPathExists(filePath, platform) {
|
|
622
|
-
try {
|
|
623
|
-
await access(filePath, platform === "win32" ? constants.F_OK : constants.X_OK);
|
|
624
|
-
return true;
|
|
625
|
-
} catch {
|
|
626
|
-
return false;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
//#endregion
|
|
630
|
-
//#region extensions/codex/src/app-server/timeout.ts
|
|
631
|
-
/**
|
|
632
|
-
* Thin Codex app-server timeout adapter around OpenClaw's shared security
|
|
633
|
-
* runtime timeout helper.
|
|
634
|
-
*/
|
|
635
|
-
/** Awaits a promise with a Codex-specific timeout error message. */
|
|
636
|
-
async function withTimeout$1(promise, timeoutMs, timeoutMessage) {
|
|
637
|
-
return await withTimeout(promise, timeoutMs, { message: timeoutMessage });
|
|
638
|
-
}
|
|
639
|
-
//#endregion
|
|
640
|
-
//#region extensions/codex/src/app-server/shared-client.ts
|
|
641
|
-
/**
|
|
642
|
-
* Owns shared and isolated Codex app-server client startup, auth application,
|
|
643
|
-
* lease tracking, and teardown.
|
|
644
|
-
*/
|
|
645
|
-
var shared_client_exports = /* @__PURE__ */ __exportAll({
|
|
646
|
-
clearSharedCodexAppServerClient: () => clearSharedCodexAppServerClient,
|
|
647
|
-
clearSharedCodexAppServerClientAndWait: () => clearSharedCodexAppServerClientAndWait,
|
|
648
|
-
clearSharedCodexAppServerClientIfCurrent: () => clearSharedCodexAppServerClientIfCurrent,
|
|
649
|
-
clearSharedCodexAppServerClientIfCurrentAndUnclaimed: () => clearSharedCodexAppServerClientIfCurrentAndUnclaimed,
|
|
650
|
-
clearSharedCodexAppServerClientIfCurrentAndWait: () => clearSharedCodexAppServerClientIfCurrentAndWait,
|
|
651
|
-
createIsolatedCodexAppServerClient: () => createIsolatedCodexAppServerClient,
|
|
652
|
-
detachSharedCodexAppServerClientIfCurrent: () => detachSharedCodexAppServerClientIfCurrent,
|
|
653
|
-
getLeasedSharedCodexAppServerClient: () => getLeasedSharedCodexAppServerClient,
|
|
654
|
-
getSharedCodexAppServerClient: () => getSharedCodexAppServerClient,
|
|
655
|
-
releaseLeasedSharedCodexAppServerClient: () => releaseLeasedSharedCodexAppServerClient,
|
|
656
|
-
resetSharedCodexAppServerClientForTests: () => resetSharedCodexAppServerClientForTests,
|
|
657
|
-
retainSharedCodexAppServerClientIfCurrent: () => retainSharedCodexAppServerClientIfCurrent,
|
|
658
|
-
retireSharedCodexAppServerClientIfCurrent: () => retireSharedCodexAppServerClientIfCurrent
|
|
659
|
-
});
|
|
660
|
-
const SHARED_CODEX_APP_SERVER_CLIENT_STATE = Symbol.for("openclaw.codexAppServerClientState");
|
|
661
|
-
function getSharedCodexAppServerClientState() {
|
|
662
|
-
const globalState = globalThis;
|
|
663
|
-
const state = globalState[SHARED_CODEX_APP_SERVER_CLIENT_STATE];
|
|
664
|
-
const keyedState = readKeyedSharedCodexAppServerClientState(state);
|
|
665
|
-
if (keyedState) {
|
|
666
|
-
const clients = keyedState.clients;
|
|
667
|
-
for (const entry of clients.values()) {
|
|
668
|
-
entry.activeLeases ??= 0;
|
|
669
|
-
entry.pendingAcquires ??= 0;
|
|
670
|
-
entry.closeWhenIdle ??= false;
|
|
671
|
-
}
|
|
672
|
-
const nextState = {
|
|
673
|
-
clients,
|
|
674
|
-
leasedReleases: keyedState.leasedReleases instanceof WeakMap ? keyedState.leasedReleases : /* @__PURE__ */ new WeakMap()
|
|
675
|
-
};
|
|
676
|
-
globalState[SHARED_CODEX_APP_SERVER_CLIENT_STATE] = nextState;
|
|
677
|
-
return nextState;
|
|
678
|
-
}
|
|
679
|
-
const legacyState = readLegacySharedCodexAppServerClientState(state);
|
|
680
|
-
const clients = /* @__PURE__ */ new Map();
|
|
681
|
-
if (legacyState?.key && (legacyState.client || legacyState.promise)) {
|
|
682
|
-
const legacyKey = legacyState.key;
|
|
683
|
-
clients.set(legacyKey, {
|
|
684
|
-
client: legacyState.client,
|
|
685
|
-
promise: legacyState.promise,
|
|
686
|
-
activeLeases: 0,
|
|
687
|
-
pendingAcquires: 0,
|
|
688
|
-
closeWhenIdle: false
|
|
689
|
-
});
|
|
690
|
-
legacyState.client?.addCloseHandler((closedClient) => clearSharedClientEntryIfCurrent(legacyKey, closedClient));
|
|
691
|
-
}
|
|
692
|
-
const nextState = {
|
|
693
|
-
clients,
|
|
694
|
-
leasedReleases: /* @__PURE__ */ new WeakMap()
|
|
695
|
-
};
|
|
696
|
-
globalState[SHARED_CODEX_APP_SERVER_CLIENT_STATE] = nextState;
|
|
697
|
-
return nextState;
|
|
698
|
-
}
|
|
699
|
-
function readKeyedSharedCodexAppServerClientState(value) {
|
|
700
|
-
return value !== null && typeof value === "object" && value.clients instanceof Map ? value : void 0;
|
|
701
|
-
}
|
|
702
|
-
function readLegacySharedCodexAppServerClientState(value) {
|
|
703
|
-
if (value === null || typeof value !== "object") return;
|
|
704
|
-
return value;
|
|
705
|
-
}
|
|
706
|
-
async function resolveCodexAppServerClientStartContext(options) {
|
|
707
|
-
const agentDir = options?.agentDir ?? resolveDefaultAgentDir(options?.config ?? {});
|
|
708
|
-
const usesNativeAuth = options?.authProfileId === null;
|
|
709
|
-
const requestedAuthProfileId = options?.authProfileId === null ? void 0 : options?.authProfileId;
|
|
710
|
-
const authProfileStore = !usesNativeAuth && options?.authProfileStore ? resolveCodexAppServerAuthProfileStore({
|
|
711
|
-
agentDir,
|
|
712
|
-
authProfileId: requestedAuthProfileId,
|
|
713
|
-
authProfileStore: options.authProfileStore,
|
|
714
|
-
config: options.config
|
|
715
|
-
}) : options?.authProfileStore;
|
|
716
|
-
const authProfileId = usesNativeAuth ? void 0 : resolveCodexAppServerAuthProfileIdForAgent({
|
|
717
|
-
authProfileId: requestedAuthProfileId,
|
|
718
|
-
agentDir,
|
|
719
|
-
config: options?.config,
|
|
720
|
-
...authProfileStore ? { authProfileStore } : {}
|
|
721
|
-
});
|
|
722
|
-
return {
|
|
723
|
-
agentDir,
|
|
724
|
-
usesNativeAuth,
|
|
725
|
-
authProfileId,
|
|
726
|
-
authProfileStore,
|
|
727
|
-
startOptions: await bridgeCodexAppServerStartOptions({
|
|
728
|
-
startOptions: await resolveManagedCodexAppServerStartOptions(options?.startOptions ?? resolveCodexAppServerRuntimeOptions().start),
|
|
729
|
-
agentDir,
|
|
730
|
-
authProfileId: usesNativeAuth ? null : authProfileId,
|
|
731
|
-
config: options?.config,
|
|
732
|
-
...authProfileStore ? { authProfileStore } : {}
|
|
733
|
-
})
|
|
734
|
-
};
|
|
735
|
-
}
|
|
736
|
-
/** Gets or starts a shared Codex app-server client without retaining a lease. */
|
|
737
|
-
async function getSharedCodexAppServerClient(options) {
|
|
738
|
-
return (await acquireSharedCodexAppServerClient(options)).client;
|
|
739
|
-
}
|
|
740
|
-
/** Gets or starts a shared Codex app-server client and records a release lease. */
|
|
741
|
-
async function getLeasedSharedCodexAppServerClient(options) {
|
|
742
|
-
const acquired = await acquireSharedCodexAppServerClient(options, { leased: true });
|
|
743
|
-
const state = getSharedCodexAppServerClientState();
|
|
744
|
-
const releases = state.leasedReleases.get(acquired.client) ?? [];
|
|
745
|
-
releases.push(acquired.release);
|
|
746
|
-
state.leasedReleases.set(acquired.client, releases);
|
|
747
|
-
return acquired.client;
|
|
748
|
-
}
|
|
749
|
-
/** Releases one outstanding lease for a shared Codex app-server client. */
|
|
750
|
-
function releaseLeasedSharedCodexAppServerClient(client) {
|
|
751
|
-
const state = getSharedCodexAppServerClientState();
|
|
752
|
-
const releases = state.leasedReleases.get(client);
|
|
753
|
-
if (!releases) return false;
|
|
754
|
-
const release = releases.pop();
|
|
755
|
-
if (!release) return false;
|
|
756
|
-
if (releases.length === 0) state.leasedReleases.delete(client);
|
|
757
|
-
release();
|
|
758
|
-
return true;
|
|
759
|
-
}
|
|
760
|
-
async function acquireSharedCodexAppServerClient(options, leaseOptions) {
|
|
761
|
-
const { agentDir, usesNativeAuth, authProfileId, startOptions } = await resolveCodexAppServerClientStartContext(options);
|
|
762
|
-
const fallbackApiKeyCacheKey = authProfileId ? void 0 : resolveCodexAppServerFallbackApiKeyCacheKey({ startOptions });
|
|
763
|
-
const key = codexAppServerStartOptionsKey(startOptions, {
|
|
764
|
-
authProfileId,
|
|
765
|
-
agentDir: usesNativeAuth ? void 0 : agentDir,
|
|
766
|
-
fallbackApiKeyCacheKey
|
|
767
|
-
});
|
|
768
|
-
const state = getSharedCodexAppServerClientState();
|
|
769
|
-
const entry = getOrCreateSharedClientEntry(state, key);
|
|
770
|
-
const releasePendingAcquire = retainPendingSharedClientAcquire(entry);
|
|
771
|
-
let cleanupAbandonSignal;
|
|
772
|
-
if (options?.abandonSignal) {
|
|
773
|
-
const abandon = () => {
|
|
774
|
-
releasePendingAcquire();
|
|
775
|
-
closeSharedClientEntryIfUnclaimed(key, entry);
|
|
776
|
-
};
|
|
777
|
-
options.abandonSignal.addEventListener("abort", abandon, { once: true });
|
|
778
|
-
cleanupAbandonSignal = () => options.abandonSignal?.removeEventListener("abort", abandon);
|
|
779
|
-
if (options.abandonSignal.aborted) abandon();
|
|
780
|
-
}
|
|
781
|
-
const sharedPromise = entry.promise ?? (entry.promise = (async () => {
|
|
782
|
-
const client = CodexAppServerClient.start(startOptions);
|
|
783
|
-
entry.client = client;
|
|
784
|
-
options?.onStartedClient?.(client);
|
|
785
|
-
client.setActiveSharedLeaseCountProviderForUnscopedNotifications(() => entry.activeLeases);
|
|
786
|
-
client.addCloseHandler((closedClient) => clearSharedClientEntryIfCurrent(key, closedClient));
|
|
787
|
-
try {
|
|
788
|
-
await client.initialize();
|
|
789
|
-
await applyCodexAppServerAuthProfile({
|
|
790
|
-
client,
|
|
791
|
-
agentDir,
|
|
792
|
-
authProfileId: usesNativeAuth ? null : authProfileId,
|
|
793
|
-
startOptions,
|
|
794
|
-
config: options?.config
|
|
795
|
-
});
|
|
796
|
-
return client;
|
|
797
|
-
} catch (error) {
|
|
798
|
-
client.close();
|
|
799
|
-
throw error;
|
|
800
|
-
}
|
|
801
|
-
})());
|
|
802
|
-
try {
|
|
803
|
-
const client = await withTimeout$1(sharedPromise, options?.timeoutMs ?? 0, "codex app-server initialize timed out");
|
|
804
|
-
client.setActiveSharedLeaseCountProviderForUnscopedNotifications(() => entry.activeLeases);
|
|
805
|
-
const release = leaseOptions?.leased ? retainSharedClientEntry(entry) : void 0;
|
|
806
|
-
return release ? {
|
|
807
|
-
client,
|
|
808
|
-
release
|
|
809
|
-
} : { client };
|
|
810
|
-
} catch (error) {
|
|
811
|
-
const currentEntry = state.clients.get(key);
|
|
812
|
-
if (currentEntry?.promise === sharedPromise) clearSharedClientEntry(key, currentEntry);
|
|
813
|
-
throw error;
|
|
814
|
-
} finally {
|
|
815
|
-
cleanupAbandonSignal?.();
|
|
816
|
-
releasePendingAcquire();
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
/** Starts a non-shared Codex app-server client owned entirely by the caller. */
|
|
820
|
-
async function createIsolatedCodexAppServerClient(options) {
|
|
821
|
-
const { agentDir, usesNativeAuth, authProfileId, authProfileStore, startOptions } = await resolveCodexAppServerClientStartContext(options);
|
|
822
|
-
const client = CodexAppServerClient.start(startOptions);
|
|
823
|
-
if (authProfileId) client.addRequestHandler(async (request) => {
|
|
824
|
-
if (request.method !== "account/chatgptAuthTokens/refresh") return;
|
|
825
|
-
return await refreshCodexAppServerAuthTokens({
|
|
826
|
-
agentDir,
|
|
827
|
-
authProfileId,
|
|
828
|
-
...authProfileStore ? { authProfileStore } : {},
|
|
829
|
-
config: options?.config
|
|
830
|
-
});
|
|
831
|
-
});
|
|
832
|
-
const initialize = client.initialize();
|
|
833
|
-
try {
|
|
834
|
-
await withTimeout$1(initialize, options?.timeoutMs ?? 0, "codex app-server initialize timed out");
|
|
835
|
-
await applyCodexAppServerAuthProfile({
|
|
836
|
-
client,
|
|
837
|
-
agentDir,
|
|
838
|
-
authProfileId: usesNativeAuth ? null : authProfileId,
|
|
839
|
-
startOptions,
|
|
840
|
-
config: options?.config,
|
|
841
|
-
...authProfileStore ? { authProfileStore } : {}
|
|
842
|
-
});
|
|
843
|
-
return client;
|
|
844
|
-
} catch (error) {
|
|
845
|
-
client.close();
|
|
846
|
-
initialize.catch(() => void 0);
|
|
847
|
-
throw error;
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
/** Clears and closes all shared clients for deterministic tests. */
|
|
851
|
-
function resetSharedCodexAppServerClientForTests() {
|
|
852
|
-
const state = getSharedCodexAppServerClientState();
|
|
853
|
-
const clients = collectSharedClients(state);
|
|
854
|
-
state.clients.clear();
|
|
855
|
-
state.leasedReleases = /* @__PURE__ */ new WeakMap();
|
|
856
|
-
for (const client of clients) client.close();
|
|
857
|
-
}
|
|
858
|
-
/** Clears and closes all shared clients. */
|
|
859
|
-
function clearSharedCodexAppServerClient() {
|
|
860
|
-
const state = getSharedCodexAppServerClientState();
|
|
861
|
-
const clients = collectSharedClients(state);
|
|
862
|
-
state.clients.clear();
|
|
863
|
-
for (const client of clients) client.close();
|
|
864
|
-
}
|
|
865
|
-
/** Clears and closes the shared entry only if it still owns the supplied client. */
|
|
866
|
-
function clearSharedCodexAppServerClientIfCurrent(client) {
|
|
867
|
-
if (!client) return false;
|
|
868
|
-
const state = getSharedCodexAppServerClientState();
|
|
869
|
-
for (const [key, entry] of state.clients) if (entry.client === client) {
|
|
870
|
-
state.clients.delete(key);
|
|
871
|
-
client.close();
|
|
872
|
-
return true;
|
|
873
|
-
}
|
|
874
|
-
return false;
|
|
875
|
-
}
|
|
876
|
-
/** Detaches the shared entry without closing the client when it still matches. */
|
|
877
|
-
function detachSharedCodexAppServerClientIfCurrent(client) {
|
|
878
|
-
if (!client) return false;
|
|
879
|
-
const state = getSharedCodexAppServerClientState();
|
|
880
|
-
for (const [key, entry] of state.clients) if (entry.client === client) {
|
|
881
|
-
state.clients.delete(key);
|
|
882
|
-
return true;
|
|
883
|
-
}
|
|
884
|
-
return false;
|
|
885
|
-
}
|
|
886
|
-
/** Retains the matching shared client and returns a release callback. */
|
|
887
|
-
function retainSharedCodexAppServerClientIfCurrent(client) {
|
|
888
|
-
if (!client) return;
|
|
889
|
-
const state = getSharedCodexAppServerClientState();
|
|
890
|
-
for (const entry of state.clients.values()) if (entry.client === client) return retainSharedClientEntry(entry);
|
|
891
|
-
}
|
|
892
|
-
/** Marks a matching shared client to close after active leases/acquires drain. */
|
|
893
|
-
function retireSharedCodexAppServerClientIfCurrent(client) {
|
|
894
|
-
if (!client) return;
|
|
895
|
-
const state = getSharedCodexAppServerClientState();
|
|
896
|
-
for (const [key, entry] of state.clients) if (entry.client === client) {
|
|
897
|
-
state.clients.delete(key);
|
|
898
|
-
entry.closeWhenIdle = true;
|
|
899
|
-
const closed = closeRetiredSharedClientEntryIfIdle(entry);
|
|
900
|
-
return {
|
|
901
|
-
activeLeases: entry.activeLeases,
|
|
902
|
-
closed
|
|
903
|
-
};
|
|
904
|
-
}
|
|
905
|
-
const activeLeases = state.leasedReleases.get(client)?.length ?? 0;
|
|
906
|
-
if (activeLeases > 0) return {
|
|
907
|
-
activeLeases,
|
|
908
|
-
closed: false
|
|
909
|
-
};
|
|
910
|
-
}
|
|
911
|
-
/** Clears a matching shared client and waits for its process to exit. */
|
|
912
|
-
async function clearSharedCodexAppServerClientIfCurrentAndWait(client, options) {
|
|
913
|
-
if (!client) return false;
|
|
914
|
-
const state = getSharedCodexAppServerClientState();
|
|
915
|
-
for (const [key, entry] of state.clients) if (entry.client === client) {
|
|
916
|
-
state.clients.delete(key);
|
|
917
|
-
await client.closeAndWait(options);
|
|
918
|
-
return true;
|
|
919
|
-
}
|
|
920
|
-
return false;
|
|
921
|
-
}
|
|
922
|
-
/** Clears all shared clients and waits for their processes to exit. */
|
|
923
|
-
async function clearSharedCodexAppServerClientAndWait(options) {
|
|
924
|
-
const state = getSharedCodexAppServerClientState();
|
|
925
|
-
const clients = collectSharedClients(state);
|
|
926
|
-
state.clients.clear();
|
|
927
|
-
await Promise.all(clients.map((client) => client.closeAndWait(options)));
|
|
928
|
-
}
|
|
929
|
-
function getOrCreateSharedClientEntry(state, key) {
|
|
930
|
-
let entry = state.clients.get(key);
|
|
931
|
-
if (!entry) {
|
|
932
|
-
entry = {
|
|
933
|
-
activeLeases: 0,
|
|
934
|
-
pendingAcquires: 0,
|
|
935
|
-
closeWhenIdle: false
|
|
936
|
-
};
|
|
937
|
-
state.clients.set(key, entry);
|
|
938
|
-
}
|
|
939
|
-
return entry;
|
|
940
|
-
}
|
|
941
|
-
function clearSharedClientEntry(key, entry) {
|
|
942
|
-
const state = getSharedCodexAppServerClientState();
|
|
943
|
-
if (state.clients.get(key) !== entry) return;
|
|
944
|
-
state.clients.delete(key);
|
|
945
|
-
entry.client?.close();
|
|
946
|
-
}
|
|
947
|
-
function clearSharedClientEntryIfCurrent(key, client) {
|
|
948
|
-
const state = getSharedCodexAppServerClientState();
|
|
949
|
-
if (state.clients.get(key)?.client === client) state.clients.delete(key);
|
|
950
|
-
}
|
|
951
|
-
/** Clears a matching shared client only when no lease or acquire currently claims it. */
|
|
952
|
-
function clearSharedCodexAppServerClientIfCurrentAndUnclaimed(client) {
|
|
953
|
-
if (!client) return {
|
|
954
|
-
found: false,
|
|
955
|
-
closed: false,
|
|
956
|
-
activeLeases: 0,
|
|
957
|
-
pendingAcquires: 0
|
|
958
|
-
};
|
|
959
|
-
const state = getSharedCodexAppServerClientState();
|
|
960
|
-
for (const [key, entry] of state.clients) if (entry.client === client) return {
|
|
961
|
-
found: true,
|
|
962
|
-
closed: closeSharedClientEntryIfUnclaimed(key, entry),
|
|
963
|
-
activeLeases: entry.activeLeases,
|
|
964
|
-
pendingAcquires: entry.pendingAcquires
|
|
965
|
-
};
|
|
966
|
-
return {
|
|
967
|
-
found: false,
|
|
968
|
-
closed: false,
|
|
969
|
-
activeLeases: 0,
|
|
970
|
-
pendingAcquires: 0
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
function retainPendingSharedClientAcquire(entry) {
|
|
974
|
-
let released = false;
|
|
975
|
-
entry.pendingAcquires += 1;
|
|
976
|
-
return () => {
|
|
977
|
-
if (released) return;
|
|
978
|
-
released = true;
|
|
979
|
-
entry.pendingAcquires = Math.max(0, entry.pendingAcquires - 1);
|
|
980
|
-
closeRetiredSharedClientEntryIfIdle(entry);
|
|
981
|
-
};
|
|
982
|
-
}
|
|
983
|
-
function retainSharedClientEntry(entry) {
|
|
984
|
-
let released = false;
|
|
985
|
-
entry.activeLeases += 1;
|
|
986
|
-
return () => {
|
|
987
|
-
if (released) return;
|
|
988
|
-
released = true;
|
|
989
|
-
entry.activeLeases = Math.max(0, entry.activeLeases - 1);
|
|
990
|
-
closeRetiredSharedClientEntryIfIdle(entry);
|
|
991
|
-
};
|
|
992
|
-
}
|
|
993
|
-
function closeRetiredSharedClientEntryIfIdle(entry) {
|
|
994
|
-
if (!entry.closeWhenIdle || entry.activeLeases > 0 || entry.pendingAcquires > 0 || !entry.client) return false;
|
|
995
|
-
const client = entry.client;
|
|
996
|
-
entry.closeWhenIdle = false;
|
|
997
|
-
entry.client = void 0;
|
|
998
|
-
client.close();
|
|
999
|
-
return true;
|
|
1000
|
-
}
|
|
1001
|
-
function closeSharedClientEntryIfUnclaimed(key, entry) {
|
|
1002
|
-
if (entry.activeLeases > 0 || entry.pendingAcquires > 0) return false;
|
|
1003
|
-
const state = getSharedCodexAppServerClientState();
|
|
1004
|
-
if (state.clients.get(key) !== entry) return false;
|
|
1005
|
-
state.clients.delete(key);
|
|
1006
|
-
entry.client?.close();
|
|
1007
|
-
return Boolean(entry.client);
|
|
1008
|
-
}
|
|
1009
|
-
function collectSharedClients(state) {
|
|
1010
|
-
return [...new Set([...state.clients.values()].map((entry) => entry.client).filter((client) => Boolean(client)))];
|
|
1011
|
-
}
|
|
1012
|
-
//#endregion
|
|
1013
|
-
export { getLeasedSharedCodexAppServerClient as a, shared_client_exports as c, resolveCodexAppServerAuthAccountCacheKey as d, resolveCodexAppServerAuthProfileId as f, resolveCodexAppServerHomeDir as h, createIsolatedCodexAppServerClient as i, withTimeout$1 as l, resolveCodexAppServerFallbackApiKeyCacheKey as m, clearSharedCodexAppServerClientIfCurrentAndUnclaimed as n, releaseLeasedSharedCodexAppServerClient as o, resolveCodexAppServerAuthProfileIdForAgent as p, clearSharedCodexAppServerClientIfCurrentAndWait as r, retireSharedCodexAppServerClientIfCurrent as s, clearSharedCodexAppServerClientIfCurrent as t, refreshCodexAppServerAuthTokens as u };
|