@openclaw/codex 2026.9.2 → 2026.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +2 -0
  2. package/dist/api.js +29 -26
  3. package/dist/apply-CvT8tW5i.js +356 -0
  4. package/dist/{attempt-client-cleanup-Bp1ZOnk2.js → attempt-client-cleanup-DKHzbwt5.js} +38 -214
  5. package/dist/{attempt-context-CSazkWns.js → attempt-context-D44yeDII.js} +9 -7
  6. package/dist/{event-projector-usage-ZqEVbAEY.js → attempt-notifications-C9XufG6l.js} +3 -106
  7. package/dist/{auth-binding-Ds_QVm-X.js → auth-binding-CWlKVccJ.js} +2 -2
  8. package/dist/auth-cache-key-7orQae_j.js +498 -0
  9. package/dist/auth-profile-CwzO4_RG.js +43 -0
  10. package/dist/auth-profile-selection-DFu9e167.js +45 -0
  11. package/dist/binding-connection-D3udKbIE.js +60 -0
  12. package/dist/{bounded-turn-DxGJRYbY.js → bounded-turn-C_Hneydj.js} +28 -125
  13. package/dist/build-state-BCmg9-5p.js +19 -0
  14. package/dist/{capabilities-B1mgvpP8.js → capabilities-8vx68WLH.js} +1 -1
  15. package/dist/cli-metadata.js +1 -1
  16. package/dist/client-B57KWPQx.js +4005 -0
  17. package/dist/command-dispatch-BpduNGVf.js +21 -0
  18. package/dist/{command-formatters-DSbGMXA0.js → command-formatters-lxlkNZgU.js} +4 -3
  19. package/dist/{command-handlers-DOJvGv6-.js → command-handlers-tiL9iWj7.js} +788 -135
  20. package/dist/command-rpc-CR5EOEZX.js +233 -0
  21. package/dist/{compact-Y_xBqrHN.js → compact-B_H3AOd7.js} +19 -14
  22. package/dist/config-oIORaQ5T.js +106 -0
  23. package/dist/{config-CyapYSji.js → config-options-CXMq1T-C.js} +205 -593
  24. package/dist/config-runtime-D0zGNIHu.js +11 -0
  25. package/dist/{config-utils-C57JiJrU.js → config-utils-DujwEnhg.js} +2 -4
  26. package/dist/conversation-binding-DsB4-O7U.js +900 -0
  27. package/dist/{plugin-marketplace-discovery-B7gHXJ3d.js → conversation-control-CtUu1b3m.js} +11 -143
  28. package/dist/doctor-contract-api.js +5 -5
  29. package/dist/{dynamic-tools-BwpvTxaX.js → dynamic-tools-CmN5RAip.js} +133 -76
  30. package/dist/{effective-mcp-catalog-CY9Jn4bA.js → effective-mcp-catalog-Bd73bqA8.js} +3 -3
  31. package/dist/{elicitation-input-DMkeki8d.js → elicitation-input-DPoK8F7w.js} +8 -22
  32. package/dist/ephemeral-turn-D78_VmzO.js +101 -0
  33. package/dist/{event-projector-assistant-message-B9kp6iGC.js → event-projector-assistant-message-BmBM4qBV.js} +12 -25
  34. package/dist/event-projector-items-5VhsECCO.js +200 -0
  35. package/dist/event-projector-usage-BfXmpOec.js +102 -0
  36. package/dist/{harness-Czh2Sdoc.js → harness-C7rRa7bm.js} +48 -24
  37. package/dist/harness.js +1 -1
  38. package/dist/history-rejection-B4nOXNXF.js +13 -0
  39. package/dist/incognito-session-uhrBF6wJ.js +15 -0
  40. package/dist/index.js +406 -2915
  41. package/dist/inference-context-Dr_mzvk4.js +94 -0
  42. package/dist/inference-proxy-HGXKUIre.js +311 -0
  43. package/dist/{isolated-completion-CRh-51AK.js → isolated-completion-DWVb5U2J.js} +7 -4
  44. package/dist/launch-args-RXQwn8zV.js +90 -0
  45. package/dist/{managed-binary-DcpnJF9x.js → managed-binary-D22rNEcR.js} +13 -103
  46. package/dist/managed-thread-store-D1t5jh3G.js +66 -0
  47. package/dist/media-understanding-provider.js +4 -1
  48. package/dist/migration-provider-api.js +2 -0
  49. package/dist/{model-catalog-DT5vo6qo.js → model-catalog-Dq2n8f_p.js} +6 -5
  50. package/dist/{models-CcrgSMQR.js → models-PCKOim-h.js} +3 -3
  51. package/dist/native-execution-policy-umd0tgh4.js +128 -0
  52. package/dist/native-subagent-history-DjtPloaU.js +140 -0
  53. package/dist/native-subagent-history-owner-Pcp7JuVF.js +34 -0
  54. package/dist/{native-subagent-monitor-B3bvQGcg.js → native-subagent-monitor-fQ7eIXEf.js} +115 -460
  55. package/dist/native-subagent-task-ids-BzTdT0Uw.js +387 -0
  56. package/dist/node-cli-sessions-DKaOmVUh.js +527 -0
  57. package/dist/{node-exec-server.runtime-nfbUOtUB.js → node-exec-server.runtime-Cl2vapEi.js} +55 -24
  58. package/dist/plan-7uG-Fp36.js +947 -0
  59. package/dist/plugin-activation-BYQculil.js +138 -0
  60. package/dist/{thread-resume-BLgqkUc1.js → plugin-app-cache-key-d0eKxy7b.js} +4 -52
  61. package/dist/{plugin-approval-roundtrip-aQVdyL5g.js → plugin-approval-roundtrip-Bz3FT8W5.js} +1 -1
  62. package/dist/plugin-inventory-oL4Na1ZG.js +328 -0
  63. package/dist/plugin-marketplace-discovery-P-Vndfqq.js +137 -0
  64. package/dist/{plugin-metadata-cache-BwCLTY_U.js → plugin-metadata-cache-ClPJpRio.js} +17 -9
  65. package/dist/protocol-5bh1G-H7.js +275 -0
  66. package/dist/provider-MQ8PVkuE.js +50 -0
  67. package/dist/{provider-capabilities-BpG9Ltc5.js → provider-capabilities-BvCUL69A.js} +2 -2
  68. package/dist/{rate-limits-Fx_l4eNa.js → rate-limits-CBMmWBr9.js} +1 -1
  69. package/dist/request-B7hEkBGq.js +212 -0
  70. package/dist/rpc-error-ttN_QjEm.js +30 -0
  71. package/dist/{run-attempt-BzthFQSM.js → run-attempt-CvsAAGa0.js} +664 -377
  72. package/dist/{runtime-artifact-Bh_gf6-t.js → runtime-artifact-BJ0rXBZ_.js} +27 -27
  73. package/dist/sandbox-guard-BxgyoM8M.js +166 -0
  74. package/dist/{session-binding-4Ki04sW-.js → session-binding-CI8UuilT.js} +30 -48
  75. package/dist/{session-binding-record-CyaXZvTe.js → session-binding-record-Bcvslnhf.js} +12 -26
  76. package/dist/session-catalog-adoption-tcZi1IfX.js +251 -0
  77. package/dist/session-catalog-archive-CwKg8Xz2.js +56 -0
  78. package/dist/session-catalog-ct-AH01I.js +1477 -0
  79. package/dist/session-catalog-home-id-CwhLdC03.js +18 -0
  80. package/dist/session-catalog-node-adoption-B1tgDW-m.js +182 -0
  81. package/dist/session-catalog-node-continue-Ci-DXyJP.js +238 -0
  82. package/dist/session-catalog-provenance-D-dIfRAH.js +96 -0
  83. package/dist/{session-cli-BjLhpmS1.js → session-cli-DNN_jhGc.js} +3 -2
  84. package/dist/{session-history-DWmMqiqW.js → session-history-Cz-tOOYj.js} +6 -3
  85. package/dist/{session-upstream-marker-DReTpGz6.js → session-history-import-DwyijFI6.js} +5 -50
  86. package/dist/{session-history-read-LHldwvyh.js → session-history-read-cxPUxyVV.js} +21 -7
  87. package/dist/session-history-worker-runtime.js +18 -6
  88. package/dist/session-history.worker-BeK1YOyT.js +378 -0
  89. package/dist/session-history.worker.js +1 -1
  90. package/dist/{session-initialization-BCFVnLL4.js → session-initialization-Ct0_Hzmg.js} +2 -2
  91. package/dist/{session-permission-policy-BaueHmSj.js → session-permission-policy-BBmXA8Eg.js} +4 -7
  92. package/dist/{session-retirement-DNdmYdFc.js → session-retirement-B2YfbAAT.js} +6 -5
  93. package/dist/session-upstream-marker-D15C9NHp.js +46 -0
  94. package/dist/{settled-turn-context-BOM3Y1pl.js → settled-turn-context-Du6gdUDd.js} +9 -9
  95. package/dist/{settled-turn-finalizer-BI-b1sry.js → settled-turn-finalizer-Dn91stmc.js} +8 -7
  96. package/dist/shared-client-CscigXXL.js +3109 -0
  97. package/dist/{side-question-B6DWAw1A.js → side-question-BGMJQf4U.js} +65 -170
  98. package/dist/source-CElXU0Oy.js +518 -0
  99. package/dist/thread-archive-guard-DfpBr-Sz.js +53 -0
  100. package/dist/thread-history-page-D0JIp48V.js +361 -0
  101. package/dist/{thread-lifecycle-DPK30jad.js → thread-lifecycle-DXl1-I6b.js} +187 -639
  102. package/dist/{thread-ownership-CbucAmmp.js → thread-ownership-BsDRTsyp.js} +13 -15
  103. package/dist/thread-policy-BuEQfE0g.js +54 -0
  104. package/dist/thread-resume-DsDOZpFg.js +52 -0
  105. package/dist/timeout-BVw8aaM1.js +33 -0
  106. package/dist/transcript-history-projection-v17wXgdK.js +238 -0
  107. package/dist/{transcript-mirror-_njEB84z.js → transcript-mirror-C5CKgNy6.js} +50 -260
  108. package/dist/{transcript-mirror-attestation-Byl34PC-.js → transcript-mirror-attestation-DibE0krH.js} +17 -4
  109. package/dist/{transport-stdio-CzTngV0j.js → transport-stdio-BGjHoYLr.js} +73 -43
  110. package/dist/{upstream-session-fork-1fwbNKzQ.js → upstream-session-fork-BZerMte_.js} +27 -19
  111. package/dist/{usage-i9fv5-4m.js → usage-tCk-mg_x.js} +4 -3
  112. package/dist/version-omm6IPI3.js +12 -0
  113. package/dist/{web-search-provider.runtime-DKQn67A7.js → web-search-provider.runtime-B7bGbGWq.js} +3 -3
  114. package/openclaw.plugin.json +23 -1
  115. package/package.json +4 -4
  116. package/dist/request-C8H22rPi.js +0 -475
  117. package/dist/session-catalog-B47uwekb.js +0 -3272
  118. package/dist/session-history.worker-zZVcx0ME.js +0 -267
  119. package/dist/shared-client-CKhX2kFt.js +0 -7505
  120. package/dist/thread-policy-CTalhWFy.js +0 -105
@@ -0,0 +1,947 @@
1
+ import { c as CODEX_PLUGINS_MARKETPLACE_NAME } from "./plugin-inventory-oL4Na1ZG.js";
2
+ import "./config-oIORaQ5T.js";
3
+ import { a as exists, c as sanitizeName, n as discoverCodexSource, o as readJsonObject, r as hasCodexSource, s as resolveHomePath, t as codexPluginMigrationSubscriptionWarning } from "./source-CElXU0Oy.js";
4
+ import { asBoolean, asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
+ import { createHash } from "node:crypto";
6
+ import { runCommandBuffered } from "openclaw/plugin-sdk/process-runtime";
7
+ import path from "node:path";
8
+ import fs from "node:fs/promises";
9
+ import { canonicalPathFromExistingAncestor, isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
10
+ import { readSecretFile } from "openclaw/plugin-sdk/secret-file";
11
+ import { fileURLToPath } from "node:url";
12
+ import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
13
+ import { extractErrorCode } from "openclaw/plugin-sdk/security-runtime";
14
+ import { applyAuthProfileConfig, buildApiKeyCredential, buildOauthProviderAuthResult, buildOpenAICodexCredentialExtra, hasUsableOAuthCredential, resolveOpenAICodexAuthIdentity, resolveOpenAICodexImportProfileName, updateAuthProfileStoreWithLock } from "openclaw/plugin-sdk/provider-auth";
15
+ import { MIGRATION_REASON_TARGET_EXISTS, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, mergeMigrationConfigValue, readMigrationConfigPath, resolveMigrationConfigRuntime, summarizeMigrationItems } from "openclaw/plugin-sdk/migration";
16
+ import { decodeOpenAICodexJwtPayload } from "openclaw/plugin-sdk/provider-oauth-runtime";
17
+ //#region extensions/codex/src/migration/cli-credentials.ts
18
+ /** Explicit migration reads the storage selected by the native credential owner. */
19
+ async function readAuthFile(home) {
20
+ try {
21
+ return asOptionalRecord(JSON.parse(await readSecretFile(path.join(home, "auth.json"), "Codex auth")));
22
+ } catch {
23
+ return;
24
+ }
25
+ }
26
+ async function readDirectKeyring(home, signal) {
27
+ const account = `cli|${createHash("sha256").update(home).digest("hex").slice(0, 16)}`;
28
+ const result = await runCommandBuffered([
29
+ "security",
30
+ "find-generic-password",
31
+ "-s",
32
+ "Codex Auth",
33
+ "-a",
34
+ account,
35
+ "-w"
36
+ ], {
37
+ timeoutMs: 6e4,
38
+ maxCombinedOutputBytes: 16384,
39
+ signal
40
+ });
41
+ if (result.termination !== "exit" || result.code !== 0) return;
42
+ try {
43
+ return asOptionalRecord(JSON.parse(result.stdout.toString("utf8")));
44
+ } catch {
45
+ return;
46
+ }
47
+ }
48
+ async function readSelectedCredentialStorage(options, requireApiKey) {
49
+ options.signal?.throwIfAborted();
50
+ const home = await fs.realpath(options.codexHome).catch(() => path.resolve(options.codexHome));
51
+ const { CodexAppServerClient } = await import("./client-B57KWPQx.js").then((n) => n.n);
52
+ const { resolveManagedCodexPackageEntrypoint, resolveManagedCodexNativeCommand } = await import("./managed-binary-D22rNEcR.js").then((n) => n.n);
53
+ const launcher = resolveManagedCodexPackageEntrypoint(path.dirname(fileURLToPath(import.meta.url)));
54
+ const command = launcher ? resolveManagedCodexNativeCommand(launcher) : void 0;
55
+ if (!command) return;
56
+ const signal = AbortSignal.any([AbortSignal.timeout(options.allowKeychainPrompt && process.platform === "darwin" ? 6e4 : 5e3), ...options.signal ? [options.signal] : []]);
57
+ const client = await CodexAppServerClient.start({
58
+ transport: "stdio",
59
+ command,
60
+ commandSource: "resolved-managed",
61
+ args: ["app-server"],
62
+ cwd: home,
63
+ homeScope: "user",
64
+ env: { CODEX_HOME: home },
65
+ clearEnv: [
66
+ "OPENAI_API_KEY",
67
+ "CODEX_API_KEY",
68
+ "CODEX_ACCESS_TOKEN"
69
+ ]
70
+ }, () => signal.throwIfAborted()).catch(() => {
71
+ options.signal?.throwIfAborted();
72
+ });
73
+ if (!client) return;
74
+ const abort = () => client.close();
75
+ signal.addEventListener("abort", abort, { once: true });
76
+ let credential;
77
+ try {
78
+ credential = await readNativeCredential(client, home, requireApiKey, options, signal);
79
+ } catch {}
80
+ signal.removeEventListener("abort", abort);
81
+ const closed = await client.closeAndWait({
82
+ forceKillDelayMs: 6e3,
83
+ exitTimeoutMs: 7e3
84
+ });
85
+ if (!closed.exited || closed.cleanup !== "closed") throw new Error("The Codex credential reader could not stop. No credential was imported.", { cause: options.signal?.reason });
86
+ options.signal?.throwIfAborted();
87
+ return credential;
88
+ }
89
+ async function readNativeCredential(client, home, requireApiKey, options, signal) {
90
+ signal.throwIfAborted();
91
+ await client.initialize();
92
+ if (client.getRuntimeIdentity()?.codexHome !== home) return;
93
+ const revision = client.getModelCatalogRevision();
94
+ if (requireApiKey) {
95
+ const account = await client.request("account/read", { refreshToken: false }, { signal });
96
+ if (asOptionalRecord(account.account)?.type !== "apiKey" || account.requiresOpenaiAuth !== true) return;
97
+ }
98
+ const configured = await client.request("config/read", {
99
+ includeLayers: false,
100
+ cwd: home
101
+ }, { signal });
102
+ const mode = (await client.request("configRequirements/read", {}, { signal })).requirements?.cli_auth_credentials_store ?? configured.config.cli_auth_credentials_store ?? "file";
103
+ let record;
104
+ if (mode === "file") record = await readAuthFile(home);
105
+ else if ((mode === "keyring" || mode === "auto") && process.platform === "darwin" && options.allowKeychainPrompt) {
106
+ let cursor;
107
+ let encrypted;
108
+ do {
109
+ const features = await client.request("experimentalFeature/list", {
110
+ limit: 100,
111
+ cursor
112
+ }, { signal });
113
+ const storage = features.data.find((feature) => feature.name === "secret_auth_storage");
114
+ if (storage) {
115
+ encrypted = storage.enabled;
116
+ break;
117
+ }
118
+ cursor = features.nextCursor ?? void 0;
119
+ } while (cursor);
120
+ if (encrypted !== false) return;
121
+ record = await readDirectKeyring(home, signal);
122
+ }
123
+ signal.throwIfAborted();
124
+ return !client.getCloseError() && client.getModelCatalogRevision() === revision ? record : void 0;
125
+ }
126
+ function jwtExpiry(token) {
127
+ try {
128
+ const exp = asOptionalRecord(JSON.parse(Buffer.from(token.split(".")[1] ?? "", "base64url").toString("utf8")))?.exp;
129
+ return typeof exp === "number" && Number.isFinite(exp) && exp > 0 ? exp * 1e3 : void 0;
130
+ } catch {
131
+ return;
132
+ }
133
+ }
134
+ async function readCodexCliCredentialsAsync(options) {
135
+ const data = await readSelectedCredentialStorage(options, false);
136
+ const mode = typeof data?.auth_mode === "string" ? data.auth_mode.toLowerCase() : void 0;
137
+ if (!data || mode !== void 0 && mode !== "chatgpt" && mode !== "chatgptauthtokens") return;
138
+ const tokens = asOptionalRecord(data.tokens);
139
+ if (typeof tokens?.access_token !== "string" || !tokens.access_token || typeof tokens.refresh_token !== "string" || !tokens.refresh_token) return;
140
+ const lastRefresh = typeof data.last_refresh === "string" ? Date.parse(data.last_refresh) : NaN;
141
+ return {
142
+ type: "oauth",
143
+ provider: "openai",
144
+ access: tokens.access_token,
145
+ refresh: tokens.refresh_token,
146
+ expires: jwtExpiry(tokens.access_token) ?? (Number.isFinite(lastRefresh) ? lastRefresh : Date.now()) + 36e5,
147
+ ...typeof tokens.account_id === "string" ? { accountId: tokens.account_id } : {},
148
+ ...typeof tokens.id_token === "string" ? { idToken: tokens.id_token } : {}
149
+ };
150
+ }
151
+ async function readCodexCliActiveApiKeyAsync(options) {
152
+ const data = await readSelectedCredentialStorage(options, true);
153
+ const mode = typeof data?.auth_mode === "string" ? data.auth_mode.toLowerCase() : void 0;
154
+ if (!data || mode !== void 0 && mode !== "apikey" && mode !== "api_key") return;
155
+ const key = typeof data.OPENAI_API_KEY === "string" ? data.OPENAI_API_KEY.trim() : "";
156
+ return key ? {
157
+ type: "api_key",
158
+ provider: "openai",
159
+ key
160
+ } : void 0;
161
+ }
162
+ //#endregion
163
+ //#region extensions/codex/src/migration/auth.ts
164
+ const OPENAI_PROVIDER_ID = "openai";
165
+ const OPENAI_OAUTH_ITEM_ID = "auth:openai";
166
+ const OPENAI_API_KEY_ITEM_ID = "auth:openai:api-key";
167
+ const OPENAI_CODEX_DEFAULT_MODEL = "openai/gpt-5.6-sol";
168
+ const CODEX_IMPORT_DISPLAY_NAME = "Codex import";
169
+ const CODEX_REASON_AUTH_NOT_SELECTED = "auth credential migration not selected";
170
+ const CODEX_REASON_AUTH_PROFILE_EXISTS = "auth profile exists";
171
+ const CODEX_REASON_AUTH_PROFILE_UNUSABLE = "existing OAuth profile requires sign-in";
172
+ const CODEX_REASON_AUTH_PROFILE_WRITE_FAILED = "failed to write auth profile";
173
+ const CODEX_REASON_AUTH_NO_LONGER_PRESENT = "auth credential no longer present";
174
+ const CODEX_REASON_MISSING_AUTH_METADATA = "missing auth metadata";
175
+ const CODEX_REASON_AUTH_STORAGE_NOT_IMPORTABLE = "credential storage is not importable";
176
+ var CodexAuthConfigConflict = class extends Error {};
177
+ function authItemId(credential) {
178
+ return credential.kind === "oauth" ? OPENAI_OAUTH_ITEM_ID : OPENAI_API_KEY_ITEM_ID;
179
+ }
180
+ function sourceCredentialFingerprint(credential) {
181
+ const profile = credential.kind === "oauth" ? credential.result.profiles[0]?.credential : void 0;
182
+ const source = credential.kind === "api_key" ? credential.key : profile?.type === "oauth" ? [
183
+ profile.access,
184
+ profile.refresh,
185
+ profile.accountId,
186
+ profile.idToken
187
+ ] : void 0;
188
+ return createHash("sha256").update(JSON.stringify([credential.kind, source])).digest("hex");
189
+ }
190
+ async function readModelRefs(source) {
191
+ const cache = await readJsonObject(source.modelsCachePath);
192
+ const models = Array.isArray(cache.models) ? cache.models : [];
193
+ const refs = /* @__PURE__ */ new Set();
194
+ for (const model of models) {
195
+ const slug = typeof model === "string" ? model.trim() : isRecord(model) ? normalizeOptionalString(model.slug) ?? normalizeOptionalString(model.id) ?? normalizeOptionalString(model.name) : void 0;
196
+ if (!slug) continue;
197
+ refs.add(`${OPENAI_PROVIDER_ID}/${slug}`);
198
+ }
199
+ refs.add(OPENAI_CODEX_DEFAULT_MODEL);
200
+ return [...refs].toSorted();
201
+ }
202
+ async function buildCodexOAuthCredential(source, includeConfigPatch, options) {
203
+ const credential = await readCodexCliCredentialsAsync({
204
+ codexHome: source.codexHome,
205
+ allowKeychainPrompt: options.allowKeychainPrompt,
206
+ ...options.signal ? { signal: options.signal } : {}
207
+ });
208
+ if (!credential) return null;
209
+ const identity = resolveOpenAICodexAuthIdentity({
210
+ access: credential.access,
211
+ accountId: credential.accountId
212
+ });
213
+ const configPatch = includeConfigPatch ? { agents: { defaults: { models: Object.fromEntries((await readModelRefs(source)).map((modelRef) => [modelRef, {}])) } } } : {};
214
+ const result = buildOauthProviderAuthResult({
215
+ providerId: OPENAI_PROVIDER_ID,
216
+ defaultModel: OPENAI_CODEX_DEFAULT_MODEL,
217
+ access: credential.access,
218
+ refresh: credential.refresh,
219
+ expires: credential.expires,
220
+ email: identity.email,
221
+ profileName: resolveOpenAICodexImportProfileName(identity, "codex-import"),
222
+ displayName: CODEX_IMPORT_DISPLAY_NAME,
223
+ credentialExtra: buildOpenAICodexCredentialExtra({
224
+ accountId: identity.accountId,
225
+ chatgptPlanType: identity.chatgptPlanType,
226
+ idToken: credential.idToken
227
+ }),
228
+ configPatch
229
+ });
230
+ const profile = result.profiles[0];
231
+ return profile ? {
232
+ kind: "oauth",
233
+ provider: OPENAI_PROVIDER_ID,
234
+ profileId: profile.profileId,
235
+ result
236
+ } : null;
237
+ }
238
+ async function buildCodexApiKeyCredential(source, allowKeychainPrompt, signal) {
239
+ const credential = await readCodexCliActiveApiKeyAsync({
240
+ codexHome: source.codexHome,
241
+ allowKeychainPrompt,
242
+ ...signal ? { signal } : {}
243
+ });
244
+ if (!credential) return null;
245
+ return {
246
+ kind: "api_key",
247
+ provider: OPENAI_PROVIDER_ID,
248
+ profileId: "openai:codex-import",
249
+ key: credential.key
250
+ };
251
+ }
252
+ async function readCodexAuthCredentials(source, options) {
253
+ return [options.credentialKind === "api_key" ? null : await buildCodexOAuthCredential(source, options.includeConfigPatch, {
254
+ allowKeychainPrompt: options.allowKeychainPrompt,
255
+ ...options.signal ? { signal: options.signal } : {}
256
+ }), options.credentialKind === "oauth" ? null : await buildCodexApiKeyCredential(source, options.allowKeychainPrompt, options.signal)].filter((entry) => entry !== null);
257
+ }
258
+ function findMatchingOAuthProfile(store, credential) {
259
+ const subject = oauthSubject(credential);
260
+ if (!subject) return;
261
+ for (const [profileId, existing] of Object.entries(store.profiles)) {
262
+ if (existing.type !== "oauth" || existing.provider !== credential.provider) continue;
263
+ const previous = oauthSubject(existing);
264
+ if (previous?.accountId === subject.accountId && previous.userId === subject.userId) return profileId;
265
+ }
266
+ }
267
+ function oauthSubject(credential) {
268
+ const claims = decodeOpenAICodexJwtPayload(credential.access)?.["https://api.openai.com/auth"];
269
+ if (!isRecord(claims)) return;
270
+ const accountId = normalizeOptionalString(claims.chatgpt_account_id);
271
+ const userId = normalizeOptionalString(claims.chatgpt_user_id) ?? normalizeOptionalString(claims.user_id);
272
+ return accountId && userId ? {
273
+ accountId,
274
+ userId
275
+ } : void 0;
276
+ }
277
+ function findMatchingApiKeyProfile(store, provider, key) {
278
+ for (const [profileId, existing] of Object.entries(store.profiles)) if (existing.type === "api_key" && existing.provider === provider && existing.key === key) return profileId;
279
+ }
280
+ function itemProfileTarget(credential, store) {
281
+ if (credential.kind === "oauth") {
282
+ const profile = credential.result.profiles[0];
283
+ const matched = profile?.credential.type === "oauth" ? findMatchingOAuthProfile(store, profile.credential) : void 0;
284
+ return {
285
+ profileId: matched ?? credential.profileId,
286
+ matchedExisting: Boolean(matched)
287
+ };
288
+ }
289
+ const matched = findMatchingApiKeyProfile(store, credential.provider, credential.key);
290
+ return {
291
+ profileId: matched ?? credential.profileId,
292
+ matchedExisting: Boolean(matched)
293
+ };
294
+ }
295
+ function replaceConfigDraft(draft, next) {
296
+ for (const key of Object.keys(draft)) delete draft[key];
297
+ Object.assign(draft, next);
298
+ }
299
+ function existingAuthProfileConfigIsCompatible(existing, profile) {
300
+ if (existing.provider !== profile.provider || existing.mode !== profile.mode) return false;
301
+ if (existing.email && profile.email && existing.email !== profile.email) return false;
302
+ return true;
303
+ }
304
+ function hasAuthProfileConfigConflict(config, profile, overwrite) {
305
+ if (overwrite) return false;
306
+ const existing = config.auth?.profiles?.[profile.profileId];
307
+ return Boolean(existing && !existingAuthProfileConfigIsCompatible(existing, profile));
308
+ }
309
+ function hasCurrentAuthProfileConfigConflict(ctx, profile) {
310
+ let config = ctx.config;
311
+ try {
312
+ config = resolveMigrationConfigRuntime(ctx)?.current?.() ?? config;
313
+ } catch {}
314
+ return hasAuthProfileConfigConflict(config, profile, Boolean(ctx.overwrite));
315
+ }
316
+ function applyDefaultModelIfMissing(cfg) {
317
+ const currentModel = cfg.agents?.defaults?.model;
318
+ if (typeof currentModel === "string" ? currentModel : isRecord(currentModel) ? normalizeOptionalString(currentModel.primary) : void 0) return cfg;
319
+ return {
320
+ ...cfg,
321
+ agents: {
322
+ ...cfg.agents,
323
+ defaults: {
324
+ ...cfg.agents?.defaults,
325
+ model: {
326
+ ...isRecord(currentModel) ? currentModel : {},
327
+ primary: OPENAI_CODEX_DEFAULT_MODEL
328
+ }
329
+ }
330
+ }
331
+ };
332
+ }
333
+ function applyOAuthConfigToConfig(cfg, credential, profileId) {
334
+ let next = mergeMigrationConfigValue(cfg, credential.result.configPatch);
335
+ const profile = credential.result.profiles[0];
336
+ if (profile) next = applyAuthProfileConfig(next, {
337
+ profileId,
338
+ provider: profile.credential.provider,
339
+ mode: "oauth",
340
+ ..."email" in profile.credential && profile.credential.email ? { email: profile.credential.email } : {},
341
+ ..."displayName" in profile.credential && profile.credential.displayName ? { displayName: profile.credential.displayName } : {},
342
+ preferProfileFirst: false
343
+ });
344
+ return applyDefaultModelIfMissing(next);
345
+ }
346
+ function applyApiKeyConfigToConfig(cfg, credential, profileId) {
347
+ return applyAuthProfileConfig(cfg, {
348
+ profileId,
349
+ provider: credential.provider,
350
+ mode: "api_key",
351
+ displayName: CODEX_IMPORT_DISPLAY_NAME,
352
+ preferProfileFirst: false
353
+ });
354
+ }
355
+ function resolveCodexConfigPatchMode(ctx) {
356
+ const mode = ctx.providerOptions?.configPatchMode;
357
+ return mode === "none" || mode === "return" ? mode : "apply";
358
+ }
359
+ function allowCodexKeychainPrompt(ctx) {
360
+ return ctx.providerOptions?.allowKeychainPrompt === true;
361
+ }
362
+ function resolveRequestedCredentialKind(ctx) {
363
+ const kind = ctx.providerOptions?.credentialKind;
364
+ return kind === "oauth" || kind === "api_key" ? kind : void 0;
365
+ }
366
+ function authProfileConfigForCredential(credential, profileId) {
367
+ if (credential.kind === "api_key") return {
368
+ profileId,
369
+ provider: credential.provider,
370
+ mode: "api_key",
371
+ displayName: CODEX_IMPORT_DISPLAY_NAME
372
+ };
373
+ const profile = credential.result.profiles[0];
374
+ if (!profile || profile.credential.type !== "oauth") return null;
375
+ return {
376
+ profileId,
377
+ provider: profile.credential.provider,
378
+ mode: "oauth",
379
+ ...profile.credential.email ? { email: profile.credential.email } : {},
380
+ ...profile.credential.displayName ? { displayName: profile.credential.displayName } : {}
381
+ };
382
+ }
383
+ async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
384
+ const configApi = resolveMigrationConfigRuntime(ctx);
385
+ if (!configApi?.current || !configApi.mutateConfigFile) return "unavailable";
386
+ try {
387
+ await configApi.mutateConfigFile({
388
+ base: "runtime",
389
+ afterWrite: { mode: "auto" },
390
+ mutate(draft) {
391
+ const current = draft;
392
+ if (hasAuthProfileConfigConflict(current, profile, Boolean(ctx.overwrite))) throw new CodexAuthConfigConflict();
393
+ replaceConfigDraft(draft, applyConfig(current));
394
+ }
395
+ });
396
+ return "configured";
397
+ } catch (error) {
398
+ return error instanceof CodexAuthConfigConflict ? "conflict" : "unavailable";
399
+ }
400
+ }
401
+ async function applyCodexAuthConfig(ctx, credential, profileId) {
402
+ const profile = authProfileConfigForCredential(credential, profileId);
403
+ if (!profile) return "unavailable";
404
+ return applyCodexAuthProfileConfig(ctx, profile, (config) => applyCredentialConfig(config, credential, profileId));
405
+ }
406
+ function applyCredentialConfig(config, credential, profileId) {
407
+ return credential.kind === "oauth" ? applyOAuthConfigToConfig(config, credential, profileId) : applyApiKeyConfigToConfig(config, credential, profileId);
408
+ }
409
+ async function buildCodexAuthItems(params) {
410
+ const configPatchMode = resolveCodexConfigPatchMode(params.ctx);
411
+ const credentials = await readCodexAuthCredentials(params.source, {
412
+ credentialKind: resolveRequestedCredentialKind(params.ctx),
413
+ includeConfigPatch: configPatchMode !== "none",
414
+ allowKeychainPrompt: allowCodexKeychainPrompt(params.ctx),
415
+ signal: params.ctx.signal
416
+ });
417
+ if (credentials.length === 0) {
418
+ const requestedKind = resolveRequestedCredentialKind(params.ctx);
419
+ if (!requestedKind) return [];
420
+ const credentialKind = requestedKind;
421
+ return [createMigrationItem({
422
+ id: credentialKind === "oauth" ? OPENAI_OAUTH_ITEM_ID : OPENAI_API_KEY_ITEM_ID,
423
+ kind: "auth",
424
+ action: "skip",
425
+ source: params.source.codexHome,
426
+ status: "skipped",
427
+ sensitive: true,
428
+ reason: CODEX_REASON_AUTH_STORAGE_NOT_IMPORTABLE,
429
+ message: "No supported Codex credential could be imported. Continue with sign-in to connect OpenClaw.",
430
+ details: {
431
+ provider: OPENAI_PROVIDER_ID,
432
+ credentialKind,
433
+ credentialImportUnavailable: true
434
+ }
435
+ })];
436
+ }
437
+ const store = loadAuthProfileStoreWithoutExternalProfiles(params.targets.agentDir);
438
+ const skipped = !params.ctx.includeSecrets;
439
+ return credentials.map((credential) => {
440
+ const { profileId, matchedExisting } = itemProfileTarget(credential, store);
441
+ const existing = store.profiles[profileId];
442
+ const configProfile = authProfileConfigForCredential(credential, profileId);
443
+ const configConflict = configProfile ? hasAuthProfileConfigConflict(params.ctx.config, configProfile, Boolean(params.ctx.overwrite)) : false;
444
+ const conflict = (existing && !matchedExisting && !params.ctx.overwrite || configConflict) && !skipped;
445
+ const unavailable = !skipped && !conflict && !params.ctx.overwrite && existing?.type === "oauth" && !hasUsableOAuthCredential(existing);
446
+ return createMigrationItem({
447
+ id: authItemId(credential),
448
+ kind: "auth",
449
+ action: skipped || unavailable ? "skip" : "create",
450
+ source: params.source.codexHome,
451
+ target: `${params.targets.agentDir}/openclaw-agent.sqlite#auth_profile_store:${profileId}`,
452
+ status: skipped || unavailable ? "skipped" : conflict ? "conflict" : "planned",
453
+ sensitive: true,
454
+ reason: skipped ? CODEX_REASON_AUTH_NOT_SELECTED : conflict ? CODEX_REASON_AUTH_PROFILE_EXISTS : unavailable ? CODEX_REASON_AUTH_PROFILE_UNUSABLE : void 0,
455
+ message: unavailable ? "The existing OpenAI sign-in needs to be renewed. Continue with sign-in." : credential.kind === "oauth" ? configPatchMode === "none" ? "Import Codex OAuth credentials." : "Import Codex OAuth credentials and configure OpenAI Codex models." : "Import Codex OpenAI API key.",
456
+ details: {
457
+ provider: credential.provider,
458
+ profileId,
459
+ sourceProfileId: credential.profileId,
460
+ sourceCredentialFingerprint: sourceCredentialFingerprint(credential),
461
+ sourceKind: "codex-native-selected-storage",
462
+ credentialKind: credential.kind,
463
+ credentialImportUnavailable: unavailable
464
+ }
465
+ });
466
+ });
467
+ }
468
+ async function applyCodexAuthItems(params) {
469
+ const { ctx, item, source, targets } = params;
470
+ if (item.status !== "planned") return [item];
471
+ const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
472
+ const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
473
+ const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
474
+ const credentialKind = item.details?.credentialKind;
475
+ if (!profileId || !provider || credentialKind !== "oauth" && credentialKind !== "api_key") return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
476
+ const configPatchMode = resolveCodexConfigPatchMode(ctx);
477
+ const credential = (await readCodexAuthCredentials(source, {
478
+ credentialKind,
479
+ includeConfigPatch: configPatchMode !== "none",
480
+ allowKeychainPrompt: allowCodexKeychainPrompt(ctx),
481
+ signal: ctx.signal
482
+ })).find((candidate) => candidate.provider === provider && candidate.kind === credentialKind && (!sourceProfileId || candidate.profileId === sourceProfileId));
483
+ if (!credential) return [markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT)];
484
+ if (item.details?.sourceCredentialFingerprint !== sourceCredentialFingerprint(credential)) return [markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT)];
485
+ ctx.signal?.throwIfAborted();
486
+ const oauthProfile = credential.kind === "oauth" ? credential.result.profiles[0] : void 0;
487
+ const oauthCredential = oauthProfile?.credential.type === "oauth" ? oauthProfile.credential : void 0;
488
+ if (credential.kind === "oauth" && !oauthCredential) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
489
+ const configProfile = authProfileConfigForCredential(credential, profileId);
490
+ if (!configProfile) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
491
+ if (hasCurrentAuthProfileConfigConflict(ctx, configProfile)) return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
492
+ let conflicted = false;
493
+ let unusable = false;
494
+ let wrote = false;
495
+ const store = await updateAuthProfileStoreWithLock({
496
+ agentDir: targets.agentDir,
497
+ stateDir: ctx.stateDir,
498
+ updater: (freshStore) => {
499
+ ctx.signal?.throwIfAborted();
500
+ const existing = freshStore.profiles[profileId];
501
+ if (!ctx.overwrite && existing) {
502
+ if ((credential.kind === "oauth" ? findMatchingOAuthProfile(freshStore, oauthCredential) : findMatchingApiKeyProfile(freshStore, credential.provider, credential.key)) === profileId) {
503
+ unusable = existing.type === "oauth" && !hasUsableOAuthCredential(existing);
504
+ return false;
505
+ }
506
+ conflicted = true;
507
+ return false;
508
+ }
509
+ freshStore.profiles[profileId] = credential.kind === "oauth" ? {
510
+ ...oauthCredential,
511
+ displayName: CODEX_IMPORT_DISPLAY_NAME
512
+ } : {
513
+ ...buildApiKeyCredential(credential.provider, credential.key),
514
+ displayName: CODEX_IMPORT_DISPLAY_NAME
515
+ };
516
+ wrote = true;
517
+ return true;
518
+ }
519
+ });
520
+ if (conflicted) return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
521
+ if (unusable) return [markMigrationItemSkipped(item, CODEX_REASON_AUTH_PROFILE_UNUSABLE)];
522
+ if (!store?.profiles[profileId]) return [markMigrationItemError(item, CODEX_REASON_AUTH_PROFILE_WRITE_FAILED)];
523
+ const configResult = configPatchMode !== "apply" ? "unavailable" : await applyCodexAuthConfig(ctx, credential, profileId);
524
+ if (configResult === "conflict") return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
525
+ const migratedItem = {
526
+ ...item,
527
+ status: "migrated",
528
+ details: {
529
+ ...item.details,
530
+ wroteAuthProfile: wrote,
531
+ configUpdated: configResult === "configured",
532
+ ...configPatchMode === "return" ? { configPatchReturned: true } : {}
533
+ }
534
+ };
535
+ return [migratedItem, ...configPatchMode === "return" ? buildCodexAuthConfigPatchItems(ctx, migratedItem, credential, profileId) : []];
536
+ }
537
+ function buildCodexAuthConfigPatchItems(ctx, item, credential, profileId) {
538
+ const next = applyCredentialConfig(ctx.config, credential, profileId);
539
+ const items = [];
540
+ if (next.auth) items.push(createMigrationItem({
541
+ id: `${item.id}:config:auth`,
542
+ kind: "config",
543
+ action: "merge",
544
+ status: "migrated",
545
+ target: "auth",
546
+ message: "Configure imported Codex auth profile.",
547
+ details: {
548
+ path: ["auth"],
549
+ value: next.auth
550
+ }
551
+ }));
552
+ if (next.agents?.defaults) items.push(createMigrationItem({
553
+ id: `${item.id}:config:agents-defaults`,
554
+ kind: "config",
555
+ action: "merge",
556
+ status: "migrated",
557
+ target: "agents.defaults",
558
+ message: "Configure imported Codex models.",
559
+ details: {
560
+ path: ["agents", "defaults"],
561
+ value: next.agents.defaults
562
+ }
563
+ }));
564
+ return items;
565
+ }
566
+ //#endregion
567
+ //#region extensions/codex/src/migration/targets.ts
568
+ function resolveCodexMigrationTargets(ctx) {
569
+ const cfg = ctx.config;
570
+ const agentId = ctx.targetAgentId ?? resolveDefaultAgentId(cfg);
571
+ const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
572
+ const configuredAgentDir = resolveAgentConfig(cfg, agentId)?.agentDir?.trim();
573
+ return {
574
+ workspaceDir,
575
+ agentDir: ctx.runtime?.agent?.resolveAgentDir(cfg, agentId) ?? (configuredAgentDir ? resolveHomePath(configuredAgentDir) : void 0) ?? path.join(ctx.stateDir, "agents", agentId, "agent")
576
+ };
577
+ }
578
+ //#endregion
579
+ //#region extensions/codex/src/migration/plan.ts
580
+ const CODEX_PLUGIN_CONFIG_ITEM_ID = "config:codex-plugins";
581
+ const CODEX_PLUGIN_CONFIG_PATH = [
582
+ "plugins",
583
+ "entries",
584
+ "codex"
585
+ ];
586
+ const CODEX_PLUGIN_ENABLED_PATH = [
587
+ "plugins",
588
+ "entries",
589
+ "codex",
590
+ "enabled"
591
+ ];
592
+ const CODEX_PLUGIN_NATIVE_CONFIG_PATH = [
593
+ "plugins",
594
+ "entries",
595
+ "codex",
596
+ "config",
597
+ "codexPlugins"
598
+ ];
599
+ const MIGRATION_REASON_PLUGIN_EXISTS = "plugin exists";
600
+ const CODEX_PLUGIN_SOURCE_APP_VERIFICATION_UNVERIFIED = "not_run";
601
+ const MIGRATION_REASON_TARGET_NOT_REGULAR = "target is not a regular file";
602
+ async function lstatIfExists(filePath) {
603
+ try {
604
+ return await fs.lstat(filePath);
605
+ } catch (error) {
606
+ const code = extractErrorCode(error);
607
+ if (code === "ENOENT" || code === "ENOTDIR") return;
608
+ throw error;
609
+ }
610
+ }
611
+ async function buildCodexMemoryItems(params) {
612
+ const items = [];
613
+ for (const memory of params.memoryFiles) {
614
+ const target = path.join(params.workspaceDir, "memory", "imports", "codex", path.basename(memory.path));
615
+ const targetStat = await lstatIfExists(target);
616
+ const targetNotRegular = targetStat !== void 0 && !targetStat.isFile();
617
+ if (!targetNotRegular) {
618
+ const [source, workspace, destination] = await Promise.all([
619
+ fs.realpath(path.dirname(memory.path)),
620
+ canonicalPathFromExistingAncestor(params.workspaceDir),
621
+ canonicalPathFromExistingAncestor(target)
622
+ ]);
623
+ if (!isPathInside(workspace, destination)) throw new Error("Codex memory import destination must stay in the selected workspace.");
624
+ if (isPathInside(source, destination) || isPathInside(destination, source)) throw new Error("Codex memory source and OpenClaw import destination must be separate paths.");
625
+ }
626
+ const targetConflict = targetStat !== void 0 && !params.overwrite;
627
+ items.push(createMigrationItem({
628
+ id: memory.id,
629
+ kind: "memory",
630
+ action: "copy",
631
+ source: memory.path,
632
+ target,
633
+ status: targetNotRegular || targetConflict ? "conflict" : "planned",
634
+ reason: targetNotRegular ? MIGRATION_REASON_TARGET_NOT_REGULAR : targetConflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
635
+ message: "Copy consolidated Codex memory into the OpenClaw memory index.",
636
+ details: {
637
+ sourceType: "codex-memory",
638
+ sourceLabel: memory.label,
639
+ collectionId: "codex",
640
+ collectionLabel: "Codex",
641
+ relativePath: path.basename(memory.path)
642
+ }
643
+ }));
644
+ }
645
+ return items;
646
+ }
647
+ function uniqueSkillName(skill, counts) {
648
+ const base = sanitizeName(skill.name) || "codex-skill";
649
+ if ((counts.get(base) ?? 0) <= 1) return base;
650
+ const parent = sanitizeName(path.basename(path.dirname(skill.source)));
651
+ return sanitizeName([
652
+ "codex",
653
+ parent,
654
+ base
655
+ ].filter(Boolean).join("-")) || base;
656
+ }
657
+ async function buildCodexSkillItems(params) {
658
+ const counts = /* @__PURE__ */ new Map();
659
+ for (const skill of params.skills) {
660
+ const base = sanitizeName(skill.name) || "codex-skill";
661
+ counts.set(base, (counts.get(base) ?? 0) + 1);
662
+ }
663
+ const planned = params.skills.map((skill) => {
664
+ const name = uniqueSkillName(skill, counts);
665
+ return {
666
+ skill,
667
+ name,
668
+ target: path.join(params.workspaceDir, "skills", name)
669
+ };
670
+ });
671
+ const resolvedCounts = planned.reduce((resolved, item) => {
672
+ resolved.set(item.name, (resolved.get(item.name) ?? 0) + 1);
673
+ return resolved;
674
+ }, /* @__PURE__ */ new Map());
675
+ return await Promise.all(planned.map(async (item) => {
676
+ const collision = (resolvedCounts.get(item.name) ?? 0) > 1;
677
+ const targetExists = await exists(item.target);
678
+ const conflict = collision || targetExists && !params.overwrite;
679
+ return createMigrationItem({
680
+ id: `skill:${item.name}`,
681
+ kind: "skill",
682
+ action: "copy",
683
+ source: item.skill.source,
684
+ target: item.target,
685
+ status: conflict ? "conflict" : "planned",
686
+ reason: collision ? `multiple Codex skills normalize to "${item.name}"` : conflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
687
+ message: `Copy ${item.skill.sourceLabel} into this OpenClaw agent workspace.`,
688
+ details: {
689
+ skillName: item.name,
690
+ sourceLabel: item.skill.sourceLabel
691
+ }
692
+ });
693
+ }));
694
+ }
695
+ function readExistingCodexPluginEntries(config) {
696
+ const entries = readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "plugins"]);
697
+ return isRecord(entries) ? entries : {};
698
+ }
699
+ function hasExistingCodexPluginEntry(existingEntries, configKey, pluginName, nextEntry) {
700
+ const existingEntry = existingEntries[configKey];
701
+ if (existingEntry !== void 0) return !isLegacyDestructivePolicyRepair(existingEntry, nextEntry);
702
+ return Object.values(existingEntries).some((entry) => {
703
+ if (!isRecord(entry)) return false;
704
+ return entry.pluginName === pluginName;
705
+ });
706
+ }
707
+ function isLegacyDestructivePolicyRepair(existing, nextEntry) {
708
+ const existingEntry = isRecord(existing) ? existing : void 0;
709
+ if (existingEntry?.allow_destructive_actions !== "on-request" || nextEntry.allow_destructive_actions !== "auto") return false;
710
+ const normalizedExisting = {
711
+ ...existingEntry,
712
+ allow_destructive_actions: "auto"
713
+ };
714
+ const normalizedEntries = Object.entries(normalizedExisting);
715
+ return normalizedEntries.length === Object.keys(nextEntry).length && normalizedEntries.every(([key, value]) => nextEntry[key] === value);
716
+ }
717
+ function readExistingPluginAllowDestructiveActions(existing, pluginName) {
718
+ const existingEntry = isRecord(existing) ? existing : void 0;
719
+ if (existingEntry?.pluginName !== pluginName) return;
720
+ const normalized = normalizeExistingAllowDestructiveActions(existingEntry.allow_destructive_actions);
721
+ return normalized === "auto" || normalized === "ask" ? normalized : void 0;
722
+ }
723
+ function buildPluginItems(ctx, plugins) {
724
+ const existingPluginEntries = readExistingCodexPluginEntries(ctx.config);
725
+ let manualIndex = 0;
726
+ const items = [];
727
+ for (const plugin of plugins) {
728
+ if (plugin.migratable && plugin.marketplaceName === "openai-curated" && plugin.pluginName) {
729
+ const configKey = plugin.pluginName;
730
+ const plannedEntry = {
731
+ enabled: true,
732
+ marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
733
+ pluginName: plugin.pluginName,
734
+ ...(() => {
735
+ const allowDestructiveActions = readExistingPluginAllowDestructiveActions(existingPluginEntries[configKey], plugin.pluginName);
736
+ return allowDestructiveActions ? { allow_destructive_actions: allowDestructiveActions } : {};
737
+ })()
738
+ };
739
+ const conflict = !ctx.overwrite && hasExistingCodexPluginEntry(existingPluginEntries, configKey, plugin.pluginName, plannedEntry);
740
+ items.push(createMigrationItem({
741
+ id: `plugin:${configKey}`,
742
+ kind: "plugin",
743
+ action: "install",
744
+ status: conflict ? "conflict" : "planned",
745
+ reason: conflict ? MIGRATION_REASON_PLUGIN_EXISTS : void 0,
746
+ applyPhase: "after-promotion",
747
+ source: plugin.source,
748
+ target: `plugins.entries.codex.config.codexPlugins.plugins.${configKey}`,
749
+ message: `Install Codex plugin "${plugin.pluginName}" in the OpenClaw-managed Codex app-server runtime.`,
750
+ details: {
751
+ configKey,
752
+ marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
753
+ pluginName: plugin.pluginName,
754
+ sourceInstalled: plugin.installed === true,
755
+ sourceEnabled: plugin.enabled === true,
756
+ ...plannedEntry.allow_destructive_actions === "auto" || plannedEntry.allow_destructive_actions === "ask" ? { allowDestructiveActions: plannedEntry.allow_destructive_actions } : {},
757
+ ...plugin.apps && plugin.apps.length > 0 && !shouldVerifyPluginApps(ctx) ? { sourceAppVerification: CODEX_PLUGIN_SOURCE_APP_VERIFICATION_UNVERIFIED } : {}
758
+ }
759
+ }));
760
+ continue;
761
+ }
762
+ manualIndex += 1;
763
+ if (plugin.migrationBlock && plugin.pluginName) {
764
+ items.push(createMigrationItem({
765
+ id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
766
+ kind: "manual",
767
+ action: "manual",
768
+ source: plugin.source,
769
+ status: "skipped",
770
+ reason: plugin.migrationBlock.code,
771
+ message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
772
+ details: {
773
+ pluginName: plugin.pluginName,
774
+ marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
775
+ ...plugin.migrationBlock.apps ? { apps: plugin.migrationBlock.apps } : {},
776
+ ...plugin.migrationBlock.error ? { error: plugin.migrationBlock.error } : {}
777
+ }
778
+ }));
779
+ continue;
780
+ }
781
+ items.push(createMigrationManualItem({
782
+ id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
783
+ source: plugin.source,
784
+ message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
785
+ recommendation: "Review the plugin bundle first, then install trusted compatible plugins with openclaw plugins install <path> --force."
786
+ }));
787
+ }
788
+ return items;
789
+ }
790
+ function shouldVerifyPluginApps(ctx) {
791
+ return ctx.providerOptions?.verifyPluginApps === true;
792
+ }
793
+ function readCodexPluginMigrationConfigEntry(item, enabled) {
794
+ const configKey = item.details?.configKey;
795
+ const marketplaceName = item.details?.marketplaceName;
796
+ const pluginName = item.details?.pluginName;
797
+ if (item.kind !== "plugin" || item.action !== "install" || typeof configKey !== "string" || marketplaceName !== "openai-curated" || typeof pluginName !== "string") return;
798
+ const allowDestructiveActions = item.details?.allowDestructiveActions;
799
+ return {
800
+ configKey,
801
+ pluginName,
802
+ enabled,
803
+ ...allowDestructiveActions === "auto" || allowDestructiveActions === "ask" ? { allowDestructiveActions } : {}
804
+ };
805
+ }
806
+ function readExistingAllowDestructiveActions(config) {
807
+ return normalizeExistingAllowDestructiveActions(readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "allow_destructive_actions"]));
808
+ }
809
+ function normalizeExistingAllowDestructiveActions(value) {
810
+ if (value === "auto" || value === "on-request") return "auto";
811
+ if (value === "ask") return "ask";
812
+ return asBoolean(value);
813
+ }
814
+ function readExistingPluginPolicyRepairs(config) {
815
+ return Object.fromEntries(Object.entries(readExistingCodexPluginEntries(config)).flatMap(([configKey, entry]) => {
816
+ const pluginEntry = isRecord(entry) ? entry : void 0;
817
+ if (pluginEntry?.allow_destructive_actions !== "on-request") return [];
818
+ return [[configKey, {
819
+ ...pluginEntry,
820
+ allow_destructive_actions: "auto"
821
+ }]];
822
+ }));
823
+ }
824
+ function buildCodexPluginsConfigValue(entries, config) {
825
+ const plugins = {
826
+ ...readExistingPluginPolicyRepairs(config),
827
+ ...Object.fromEntries(entries.toSorted((a, b) => a.configKey.localeCompare(b.configKey)).map((entry) => [entry.configKey, {
828
+ enabled: entry.enabled,
829
+ marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
830
+ pluginName: entry.pluginName,
831
+ ...entry.allowDestructiveActions ? { allow_destructive_actions: entry.allowDestructiveActions } : {}
832
+ }]))
833
+ };
834
+ return {
835
+ enabled: true,
836
+ config: { codexPlugins: {
837
+ enabled: true,
838
+ allow_destructive_actions: readExistingAllowDestructiveActions(config) ?? true,
839
+ plugins
840
+ } }
841
+ };
842
+ }
843
+ function hasCodexPluginConfigConflict(config, value) {
844
+ const enabled = readMigrationConfigPath(config, CODEX_PLUGIN_ENABLED_PATH);
845
+ if (enabled !== void 0 && enabled !== true) return true;
846
+ const nativeConfig = value.config?.codexPlugins;
847
+ if (!isRecord(nativeConfig)) return hasMigrationConfigPatchConflict(config, CODEX_PLUGIN_NATIVE_CONFIG_PATH, nativeConfig);
848
+ const existingNativeConfig = readMigrationConfigPath(config, CODEX_PLUGIN_NATIVE_CONFIG_PATH);
849
+ if (existingNativeConfig === void 0) return false;
850
+ if (!isRecord(existingNativeConfig)) return true;
851
+ if (existingNativeConfig.enabled !== void 0 && existingNativeConfig.enabled !== true) return true;
852
+ const allowDestructiveActions = nativeConfig.allow_destructive_actions;
853
+ const existingAllowDestructiveActions = normalizeExistingAllowDestructiveActions(existingNativeConfig.allow_destructive_actions);
854
+ if (existingNativeConfig.allow_destructive_actions !== void 0 && existingAllowDestructiveActions !== allowDestructiveActions) return true;
855
+ const plugins = nativeConfig.plugins;
856
+ if (!isRecord(plugins)) return false;
857
+ return Object.entries(plugins).some(([configKey, plugin]) => {
858
+ if (!isRecord(plugin)) return existingNativeConfig[configKey] !== void 0;
859
+ return hasExistingCodexPluginEntry(readExistingCodexPluginEntries(config), configKey, typeof plugin.pluginName === "string" ? plugin.pluginName : configKey, plugin);
860
+ });
861
+ }
862
+ function buildPluginConfigItem(ctx, pluginItems) {
863
+ const entries = pluginItems.filter((item) => item.status === "planned").map((item) => readCodexPluginMigrationConfigEntry(item, true)).filter((entry) => entry !== void 0);
864
+ if (entries.length === 0) return;
865
+ const value = buildCodexPluginsConfigValue(entries, ctx.config);
866
+ const conflict = !ctx.overwrite && hasCodexPluginConfigConflict(ctx.config, value);
867
+ return createMigrationItem({
868
+ id: CODEX_PLUGIN_CONFIG_ITEM_ID,
869
+ kind: "config",
870
+ action: "merge",
871
+ target: "plugins.entries.codex.config.codexPlugins",
872
+ status: conflict ? "conflict" : "planned",
873
+ reason: conflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
874
+ applyPhase: "after-promotion",
875
+ message: "Enable OpenClaw's Codex plugin integration and record migrated source-installed curated plugins.",
876
+ details: {
877
+ path: [...CODEX_PLUGIN_CONFIG_PATH],
878
+ value
879
+ }
880
+ });
881
+ }
882
+ async function buildCodexMigrationPlan(ctx) {
883
+ const targets = resolveCodexMigrationTargets(ctx);
884
+ const memoryOnly = ctx.itemKinds !== void 0 && ctx.itemKinds.length > 0 && ctx.itemKinds.every((kind) => kind === "memory");
885
+ const authOnly = ctx.itemKinds !== void 0 && ctx.itemKinds.length > 0 && ctx.itemKinds.every((kind) => kind === "auth");
886
+ const source = await discoverCodexSource({
887
+ input: ctx.source,
888
+ memoryOnly,
889
+ authOnly,
890
+ evaluatePluginMigrationEligibility: !memoryOnly && !authOnly,
891
+ verifyPluginApps: shouldVerifyPluginApps(ctx)
892
+ });
893
+ if (!hasCodexSource(source) && !authOnly) throw new Error(`Codex state was not found at ${source.root}. Pass --from <path> if it lives elsewhere.`);
894
+ const items = [];
895
+ if (!authOnly) items.push(...await buildCodexMemoryItems({
896
+ memoryFiles: source.memoryFiles,
897
+ workspaceDir: targets.workspaceDir,
898
+ overwrite: ctx.overwrite
899
+ }));
900
+ if (!memoryOnly) items.push(...await buildCodexAuthItems({
901
+ ctx,
902
+ source,
903
+ targets
904
+ }));
905
+ if (!memoryOnly && !authOnly) {
906
+ items.push(...await buildCodexSkillItems({
907
+ skills: source.skills,
908
+ workspaceDir: targets.workspaceDir,
909
+ overwrite: ctx.overwrite
910
+ }));
911
+ const pluginItems = buildPluginItems(ctx, source.plugins);
912
+ items.push(...pluginItems);
913
+ const pluginConfigItem = buildPluginConfigItem(ctx, pluginItems);
914
+ if (pluginConfigItem) items.push(pluginConfigItem);
915
+ for (const archivePath of source.archivePaths) items.push(createMigrationItem({
916
+ id: archivePath.id,
917
+ kind: "archive",
918
+ action: "archive",
919
+ source: archivePath.path,
920
+ message: archivePath.message ?? "Archived in the migration report for manual review; not imported into live config.",
921
+ details: { archiveRelativePath: archivePath.relativePath }
922
+ }));
923
+ }
924
+ const warnings = [
925
+ ...!ctx.includeSecrets && items.some((item) => item.kind === "auth") ? ["Auth credentials were detected but skipped. Re-run interactively or pass --include-secrets to import supported credentials."] : [],
926
+ ...items.some((item) => item.status === "conflict") ? ["Conflicts were found. Re-run with --overwrite to replace conflicting migration targets after item-level backups."] : [],
927
+ ...source.pluginDiscoveryError ? [`Codex app-server plugin inventory discovery failed: ${source.pluginDiscoveryError}. Cached plugin bundles, if any, are advisory only.`] : [],
928
+ ...source.plugins.some((plugin) => plugin.migrationBlock?.code === "codex_subscription_required") ? [codexPluginMigrationSubscriptionWarning()] : []
929
+ ];
930
+ return {
931
+ providerId: "codex",
932
+ source: source.root,
933
+ target: targets.workspaceDir,
934
+ summary: summarizeMigrationItems(items),
935
+ items,
936
+ warnings,
937
+ nextSteps: memoryOnly ? [] : ["Run openclaw doctor after applying the migration.", "Review skipped or auth-required Codex plugin/config/hook items before exposing them in OpenClaw sessions."],
938
+ metadata: {
939
+ agentDir: targets.agentDir,
940
+ codexHome: source.codexHome,
941
+ codexSkillsDir: source.codexSkillsDir,
942
+ personalAgentsSkillsDir: source.personalAgentsSkillsDir
943
+ }
944
+ };
945
+ }
946
+ //#endregion
947
+ export { CODEX_PLUGIN_CONFIG_ITEM_ID, CODEX_PLUGIN_CONFIG_PATH, buildCodexMigrationPlan, buildCodexPluginsConfigValue, hasCodexPluginConfigConflict, applyCodexAuthItems as n, resolveCodexConfigPatchMode as r, readCodexPluginMigrationConfigEntry, resolveCodexMigrationTargets as t };