@openclaw/codex 2026.9.1 → 2026.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/api.js +18 -23
- package/dist/apply-B6el6hdO.js +357 -0
- package/dist/{attempt-client-cleanup-DYHcRQAa.js → attempt-client-cleanup-D6cR54TV.js} +113 -49
- package/dist/{attempt-context-DmQUNWwT.js → attempt-context-AO4DSjE0.js} +11 -9
- package/dist/{event-projector-usage-C5mcrGjp.js → attempt-notifications--H8zXR5-.js} +3 -89
- package/dist/auth-cache-key-Kq6oerr6.js +498 -0
- package/dist/auth-profile-CwzO4_RG.js +43 -0
- package/dist/auth-profile-selection-DFu9e167.js +45 -0
- package/dist/{binding-connection-E6uDsPlx.js → binding-connection-QqgcPCgk.js} +18 -3
- package/dist/{bounded-turn-wzJrwKKe.js → bounded-turn-lRpmZHFv.js} +54 -150
- package/dist/build-state-6RL6qohz.js +19 -0
- package/dist/{capabilities-dIjNVCYH.js → capabilities-BeqLPbtM.js} +2 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/client-BEKu4yDx.js +3983 -0
- package/dist/command-dispatch-CisT9Jqq.js +21 -0
- package/dist/{command-formatters-CLHhEB1Q.js → command-formatters-DeTfrapr.js} +2 -2
- package/dist/{command-handlers-CTGb2csu.js → command-handlers-BfIsJEuA.js} +158 -76
- package/dist/command-rpc-BMqRQOFX.js +226 -0
- package/dist/{compact-_t0HQrUS.js → compact-CGJZ2rlb.js} +89 -49
- package/dist/config-JDfTI4VI.js +106 -0
- package/dist/{config-BhmfC1s6.js → config-options-jnIFvwxD.js} +208 -654
- package/dist/config-runtime-BWf6JLWG.js +11 -0
- package/dist/config-utils-C57JiJrU.js +69 -0
- package/dist/conversation-binding-CxOlHiix.js +889 -0
- package/dist/{plugin-marketplace-discovery-CfFlKqDl.js → conversation-control-CUCrSIQI.js} +36 -155
- package/dist/doctor-contract-api.js +8 -9
- package/dist/{dynamic-tools-BU9tmlYm.js → dynamic-tools-D-JBoaLt.js} +734 -698
- package/dist/{effective-mcp-catalog-DzYBW4h1.js → effective-mcp-catalog-J_5Ep5Um.js} +4 -3
- package/dist/{elicitation-input-DMkeki8d.js → elicitation-input-DPoK8F7w.js} +8 -22
- package/dist/ephemeral-turn-DSwzP8YY.js +101 -0
- package/dist/{event-projector-assistant-message-DHRFXKbS.js → event-projector-assistant-message-BmBM4qBV.js} +23 -27
- package/dist/event-projector-usage-CIyrYYid.js +102 -0
- package/dist/{harness-DXVZ5II5.js → harness-FR6eQgyO.js} +47 -17
- package/dist/harness.js +1 -1
- package/dist/history-rejection-B4nOXNXF.js +13 -0
- package/dist/{image-payload-sanitizer-CnfoUPCE.js → image-payload-sanitizer-tupAr-2o.js} +29 -15
- package/dist/incognito-session-uhrBF6wJ.js +15 -0
- package/dist/index.js +413 -2893
- package/dist/{isolated-completion-kg9kv4iH.js → isolated-completion-DHBefrzg.js} +12 -21
- package/dist/{managed-binary-4-Vjz5GH.js → managed-binary-DrjjRRUD.js} +17 -84
- package/dist/managed-thread-store-D1t5jh3G.js +66 -0
- package/dist/media-understanding-provider.js +4 -1
- package/dist/{model-catalog-CUM68Hkl.js → model-catalog-B_6M04Wo.js} +6 -5
- package/dist/{models-sYnEc0gd.js → models-P9k2qAof.js} +4 -4
- package/dist/native-execution-policy-umd0tgh4.js +128 -0
- package/dist/{native-subagent-monitor-CnH147jH.js → native-subagent-monitor-BbCUKRwC.js} +76 -48
- package/dist/node-cli-sessions-rj0ysJwM.js +527 -0
- package/dist/{node-exec-server.runtime-CstNHCmC.js → node-exec-server.runtime-BY_ctYnT.js} +54 -23
- package/dist/plan-BMJdMOEF.js +711 -0
- package/dist/plugin-activation-BQK-f5sV.js +133 -0
- package/dist/{thread-resume-C7pyVKeW.js → plugin-app-cache-key-CdSQzWyX.js} +5 -53
- package/dist/{plugin-approval-roundtrip-CNe-Xrjx.js → plugin-approval-roundtrip-DeW95wB5.js} +1 -1
- package/dist/plugin-inventory-CoRCzWW4.js +325 -0
- package/dist/plugin-marketplace-discovery-3akBizzF.js +134 -0
- package/dist/{plugin-metadata-cache-BFrPhb2c.js → plugin-metadata-cache-DgNfRAGA.js} +13 -9
- package/dist/protocol-CAAcVIKF.js +275 -0
- package/dist/{provider-capabilities-DW5p4k9w.js → provider-capabilities-W4tfK5Y5.js} +2 -2
- package/dist/{rate-limits-CawQJfUS.js → rate-limits-Bqnx1N4z.js} +1 -1
- package/dist/request-CGvGOpeR.js +198 -0
- package/dist/{run-attempt-D1hJO4fX.js → run-attempt-DwKds3DN.js} +1498 -1028
- package/dist/{runtime-artifact-C3jVimFn.js → runtime-artifact-T-NwqeSH.js} +28 -28
- package/dist/sandbox-guard-BcT66fWy.js +165 -0
- package/dist/{session-binding-BHuTCRZQ.js → session-binding-CKSepwIC.js} +118 -324
- package/dist/session-binding-record-CaZjYwNS.js +256 -0
- package/dist/session-catalog-HaDcp7Sv.js +1533 -0
- package/dist/session-catalog-adoption-BKLtSrwi.js +251 -0
- package/dist/session-catalog-archive-D0i2pGj2.js +56 -0
- package/dist/session-catalog-home-id-D7-aLiNt.js +190 -0
- package/dist/session-catalog-node-adoption-BUuDYY8R.js +182 -0
- package/dist/session-catalog-node-continue-CBtc4wkI.js +238 -0
- package/dist/session-catalog-parsing-CdD3JhdM.js +280 -0
- package/dist/session-catalog-provenance-DkJHo735.js +124 -0
- package/dist/{session-cli-CmFEXMAM.js → session-cli-CbgaHh0f.js} +3 -2
- package/dist/session-history-Cz-tOOYj.js +96 -0
- package/dist/{session-upstream-marker-C351-5Qw.js → session-history-import-DFN7sWRU.js} +5 -49
- package/dist/session-history-read-cxPUxyVV.js +50 -0
- package/dist/session-history-worker-runtime.js +60 -0
- package/dist/{settled-turn-context-D3SNWtUn.js → session-history.worker-CNEvGaU5.js} +75 -69
- package/dist/session-history.worker.js +2 -0
- package/dist/{session-initialization-BeWzjGiR.js → session-initialization-Jo86C60I.js} +2 -1
- package/dist/{session-permission-policy-DeWVTCMj.js → session-permission-policy-B22InTp_.js} +4 -7
- package/dist/{session-retirement-BujDjXVF.js → session-retirement-BSNG_mhf.js} +8 -7
- package/dist/session-upstream-marker-D15C9NHp.js +46 -0
- package/dist/settled-turn-context-Du6gdUDd.js +45 -0
- package/dist/{settled-turn-finalizer-BVJx4yoc.js → settled-turn-finalizer-BuLUrZl2.js} +54 -42
- package/dist/shared-client-CVhez2Uo.js +2911 -0
- package/dist/{side-question-BTZAII4l.js → side-question-DLbr2vUU.js} +135 -191
- package/dist/source-Da-h6f7e.js +518 -0
- package/dist/thread-archive-guard-D1bP04Tf.js +53 -0
- package/dist/{thread-lifecycle-DAcEJ_Om.js → thread-lifecycle-D1pzRwHB.js} +467 -977
- package/dist/{thread-ownership-zGokd8i1.js → thread-ownership-D6oASVRu.js} +15 -17
- package/dist/{thread-policy-g4KbJPzp.js → thread-policy-D8kI7yAO.js} +2 -2
- package/dist/thread-resume-T5cveyxA.js +52 -0
- package/dist/timeout-BVw8aaM1.js +33 -0
- package/dist/transcript-mirror-attestation-DibE0krH.js +89 -0
- package/dist/{transcript-mirror-DTUxFWa_.js → transcript-mirror-t5TOccct.js} +111 -379
- package/dist/{transport-stdio-CyPldczm.js → transport-stdio-BGjHoYLr.js} +80 -48
- package/dist/{upstream-session-fork-CYlBIGVt.js → upstream-session-fork-De-sLJ2r.js} +94 -166
- package/dist/{usage-Bj-rrzkR.js → usage-CPPTmBvZ.js} +4 -3
- package/dist/version-omm6IPI3.js +12 -0
- package/dist/{web-search-provider.runtime-D2FWSK4w.js → web-search-provider.runtime-CSVgQL0w.js} +3 -3
- package/openclaw.plugin.json +22 -1
- package/package.json +6 -6
- package/dist/request-DYAI5FQE.js +0 -475
- package/dist/session-catalog-yyvTWFOw.js +0 -3120
- package/dist/session-history-C5J1AKzG.js +0 -87
- package/dist/shared-client-DxcfF9KX.js +0 -7494
- package/dist/{auth-binding-Ds_QVm-X.js → auth-binding-Co2Vfcf_.js} +1 -1
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
import { m as CODEX_PLUGINS_MARKETPLACE_NAME } from "./config-utils-C57JiJrU.js";
|
|
2
|
+
import "./config-JDfTI4VI.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-Da-h6f7e.js";
|
|
4
|
+
import { asBoolean, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import { canonicalPathFromExistingAncestor, isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
|
|
8
|
+
import { loadAuthProfileStoreWithoutExternalProfiles, resolveAgentConfig, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
|
|
9
|
+
import { extractErrorCode } from "openclaw/plugin-sdk/security-runtime";
|
|
10
|
+
import { applyAuthProfileConfig, buildApiKeyCredential, buildOauthProviderAuthResult, buildOpenAICodexCredentialExtra, readCodexCliCredentialsCached, resolveOpenAICodexAuthIdentity, resolveOpenAICodexImportProfileName, updateAuthProfileStoreWithLock } from "openclaw/plugin-sdk/provider-auth";
|
|
11
|
+
import { MIGRATION_REASON_TARGET_EXISTS, createMigrationItem, createMigrationManualItem, hasMigrationConfigPatchConflict, markMigrationItemConflict, markMigrationItemError, markMigrationItemSkipped, mergeMigrationConfigValue, readMigrationConfigPath, resolveMigrationConfigRuntime, summarizeMigrationItems } from "openclaw/plugin-sdk/migration";
|
|
12
|
+
//#region extensions/codex/src/migration/auth.ts
|
|
13
|
+
const OPENAI_PROVIDER_ID = "openai";
|
|
14
|
+
const OPENAI_CODEX_DEFAULT_MODEL = "openai/gpt-5.6-sol";
|
|
15
|
+
const CODEX_IMPORT_DISPLAY_NAME = "Codex import";
|
|
16
|
+
const CODEX_REASON_AUTH_NOT_SELECTED = "auth credential migration not selected";
|
|
17
|
+
const CODEX_REASON_AUTH_PROFILE_EXISTS = "auth profile exists";
|
|
18
|
+
const CODEX_REASON_AUTH_PROFILE_WRITE_FAILED = "failed to write auth profile";
|
|
19
|
+
const CODEX_REASON_AUTH_NO_LONGER_PRESENT = "auth credential no longer present";
|
|
20
|
+
const CODEX_REASON_MISSING_AUTH_METADATA = "missing auth metadata";
|
|
21
|
+
const CODEX_CONFIG_PATCH_MODE_RETURN = "return";
|
|
22
|
+
var CodexAuthConfigConflict = class extends Error {};
|
|
23
|
+
async function readModelRefs(source) {
|
|
24
|
+
const cache = await readJsonObject(source.modelsCachePath);
|
|
25
|
+
const models = Array.isArray(cache.models) ? cache.models : [];
|
|
26
|
+
const refs = /* @__PURE__ */ new Set();
|
|
27
|
+
for (const model of models) {
|
|
28
|
+
const slug = typeof model === "string" ? model.trim() : isRecord(model) ? normalizeOptionalString(model.slug) ?? normalizeOptionalString(model.id) ?? normalizeOptionalString(model.name) : void 0;
|
|
29
|
+
if (!slug) continue;
|
|
30
|
+
refs.add(`${OPENAI_PROVIDER_ID}/${slug}`);
|
|
31
|
+
}
|
|
32
|
+
refs.add(OPENAI_CODEX_DEFAULT_MODEL);
|
|
33
|
+
return [...refs].toSorted();
|
|
34
|
+
}
|
|
35
|
+
async function buildCodexOAuthCredential(source) {
|
|
36
|
+
const credential = readCodexCliCredentialsCached({
|
|
37
|
+
codexHome: source.codexHome,
|
|
38
|
+
allowKeychainPrompt: false,
|
|
39
|
+
ttlMs: 0
|
|
40
|
+
});
|
|
41
|
+
if (!credential) return null;
|
|
42
|
+
const identity = resolveOpenAICodexAuthIdentity({
|
|
43
|
+
access: credential.access,
|
|
44
|
+
accountId: credential.accountId
|
|
45
|
+
});
|
|
46
|
+
const modelRefs = await readModelRefs(source);
|
|
47
|
+
const configPatch = { agents: { defaults: { models: Object.fromEntries(modelRefs.map((modelRef) => [modelRef, {}])) } } };
|
|
48
|
+
const result = buildOauthProviderAuthResult({
|
|
49
|
+
providerId: OPENAI_PROVIDER_ID,
|
|
50
|
+
defaultModel: OPENAI_CODEX_DEFAULT_MODEL,
|
|
51
|
+
access: credential.access,
|
|
52
|
+
refresh: credential.refresh,
|
|
53
|
+
expires: credential.expires,
|
|
54
|
+
email: identity.email,
|
|
55
|
+
profileName: resolveOpenAICodexImportProfileName(identity, "codex-import"),
|
|
56
|
+
displayName: CODEX_IMPORT_DISPLAY_NAME,
|
|
57
|
+
credentialExtra: buildOpenAICodexCredentialExtra({
|
|
58
|
+
accountId: identity.accountId,
|
|
59
|
+
chatgptPlanType: identity.chatgptPlanType,
|
|
60
|
+
idToken: credential.idToken
|
|
61
|
+
}),
|
|
62
|
+
configPatch
|
|
63
|
+
});
|
|
64
|
+
const profile = result.profiles[0];
|
|
65
|
+
return profile ? {
|
|
66
|
+
kind: "oauth",
|
|
67
|
+
provider: OPENAI_PROVIDER_ID,
|
|
68
|
+
profileId: profile.profileId,
|
|
69
|
+
result
|
|
70
|
+
} : null;
|
|
71
|
+
}
|
|
72
|
+
async function buildCodexApiKeyCredential(source) {
|
|
73
|
+
const raw = await readJsonObject(source.authPath);
|
|
74
|
+
const key = normalizeOptionalString(raw.OPENAI_API_KEY);
|
|
75
|
+
if (!key) return null;
|
|
76
|
+
return {
|
|
77
|
+
kind: "api_key",
|
|
78
|
+
provider: OPENAI_PROVIDER_ID,
|
|
79
|
+
profileId: "openai:codex-import",
|
|
80
|
+
key
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async function readCodexAuthCredentials(source) {
|
|
84
|
+
return [await buildCodexOAuthCredential(source), await buildCodexApiKeyCredential(source)].filter((entry) => entry !== null);
|
|
85
|
+
}
|
|
86
|
+
function findMatchingOAuthProfile(store, credential) {
|
|
87
|
+
for (const [profileId, existing] of Object.entries(store.profiles)) {
|
|
88
|
+
if (existing.type !== "oauth" || existing.provider !== credential.provider) continue;
|
|
89
|
+
if (credential.accountId && existing.accountId === credential.accountId) return profileId;
|
|
90
|
+
if ((!credential.accountId || !existing.accountId) && credential.email && existing.email === credential.email) return profileId;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function findMatchingApiKeyProfile(store, provider, key) {
|
|
94
|
+
for (const [profileId, existing] of Object.entries(store.profiles)) if (existing.type === "api_key" && existing.provider === provider && existing.key === key) return profileId;
|
|
95
|
+
}
|
|
96
|
+
function itemProfileTarget(credential, store) {
|
|
97
|
+
if (credential.kind === "oauth") {
|
|
98
|
+
const profile = credential.result.profiles[0];
|
|
99
|
+
const matched = profile?.credential.type === "oauth" ? findMatchingOAuthProfile(store, profile.credential) : void 0;
|
|
100
|
+
return {
|
|
101
|
+
profileId: matched ?? credential.profileId,
|
|
102
|
+
matchedExisting: Boolean(matched)
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const matched = findMatchingApiKeyProfile(store, credential.provider, credential.key);
|
|
106
|
+
return {
|
|
107
|
+
profileId: matched ?? credential.profileId,
|
|
108
|
+
matchedExisting: Boolean(matched)
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function replaceConfigDraft(draft, next) {
|
|
112
|
+
for (const key of Object.keys(draft)) delete draft[key];
|
|
113
|
+
Object.assign(draft, next);
|
|
114
|
+
}
|
|
115
|
+
function existingAuthProfileConfigIsCompatible(existing, profile) {
|
|
116
|
+
if (existing.provider !== profile.provider || existing.mode !== profile.mode) return false;
|
|
117
|
+
if (existing.email && profile.email && existing.email !== profile.email) return false;
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
function hasAuthProfileConfigConflict(config, profile, overwrite) {
|
|
121
|
+
if (overwrite) return false;
|
|
122
|
+
const existing = config.auth?.profiles?.[profile.profileId];
|
|
123
|
+
return Boolean(existing && !existingAuthProfileConfigIsCompatible(existing, profile));
|
|
124
|
+
}
|
|
125
|
+
function hasCurrentAuthProfileConfigConflict(ctx, profile) {
|
|
126
|
+
let config = ctx.config;
|
|
127
|
+
try {
|
|
128
|
+
config = resolveMigrationConfigRuntime(ctx)?.current?.() ?? config;
|
|
129
|
+
} catch {}
|
|
130
|
+
return hasAuthProfileConfigConflict(config, profile, Boolean(ctx.overwrite));
|
|
131
|
+
}
|
|
132
|
+
function applyDefaultModelIfMissing(cfg) {
|
|
133
|
+
const currentModel = cfg.agents?.defaults?.model;
|
|
134
|
+
if (typeof currentModel === "string" ? currentModel : isRecord(currentModel) ? normalizeOptionalString(currentModel.primary) : void 0) return cfg;
|
|
135
|
+
return {
|
|
136
|
+
...cfg,
|
|
137
|
+
agents: {
|
|
138
|
+
...cfg.agents,
|
|
139
|
+
defaults: {
|
|
140
|
+
...cfg.agents?.defaults,
|
|
141
|
+
model: {
|
|
142
|
+
...isRecord(currentModel) ? currentModel : {},
|
|
143
|
+
primary: OPENAI_CODEX_DEFAULT_MODEL
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function applyOAuthConfigToConfig(cfg, credential, profileId) {
|
|
150
|
+
let next = mergeMigrationConfigValue(cfg, credential.result.configPatch);
|
|
151
|
+
const profile = credential.result.profiles[0];
|
|
152
|
+
if (profile) next = applyAuthProfileConfig(next, {
|
|
153
|
+
profileId,
|
|
154
|
+
provider: profile.credential.provider,
|
|
155
|
+
mode: "oauth",
|
|
156
|
+
..."email" in profile.credential && profile.credential.email ? { email: profile.credential.email } : {},
|
|
157
|
+
..."displayName" in profile.credential && profile.credential.displayName ? { displayName: profile.credential.displayName } : {},
|
|
158
|
+
preferProfileFirst: false
|
|
159
|
+
});
|
|
160
|
+
return applyDefaultModelIfMissing(next);
|
|
161
|
+
}
|
|
162
|
+
function applyApiKeyConfigToConfig(cfg, credential, profileId) {
|
|
163
|
+
return applyAuthProfileConfig(cfg, {
|
|
164
|
+
profileId,
|
|
165
|
+
provider: credential.provider,
|
|
166
|
+
mode: "api_key",
|
|
167
|
+
displayName: CODEX_IMPORT_DISPLAY_NAME,
|
|
168
|
+
preferProfileFirst: false
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function shouldReturnAuthConfigPatch(ctx) {
|
|
172
|
+
return ctx.providerOptions?.configPatchMode === CODEX_CONFIG_PATCH_MODE_RETURN;
|
|
173
|
+
}
|
|
174
|
+
function authProfileConfigForCredential(credential, profileId) {
|
|
175
|
+
if (credential.kind === "api_key") return {
|
|
176
|
+
profileId,
|
|
177
|
+
provider: credential.provider,
|
|
178
|
+
mode: "api_key",
|
|
179
|
+
displayName: CODEX_IMPORT_DISPLAY_NAME
|
|
180
|
+
};
|
|
181
|
+
const profile = credential.result.profiles[0];
|
|
182
|
+
if (!profile || profile.credential.type !== "oauth") return null;
|
|
183
|
+
return {
|
|
184
|
+
profileId,
|
|
185
|
+
provider: profile.credential.provider,
|
|
186
|
+
mode: "oauth",
|
|
187
|
+
...profile.credential.email ? { email: profile.credential.email } : {},
|
|
188
|
+
...profile.credential.displayName ? { displayName: profile.credential.displayName } : {}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
async function applyCodexAuthProfileConfig(ctx, profile, applyConfig) {
|
|
192
|
+
const configApi = resolveMigrationConfigRuntime(ctx);
|
|
193
|
+
if (!configApi?.current || !configApi.mutateConfigFile) return "unavailable";
|
|
194
|
+
try {
|
|
195
|
+
await configApi.mutateConfigFile({
|
|
196
|
+
base: "runtime",
|
|
197
|
+
afterWrite: { mode: "auto" },
|
|
198
|
+
mutate(draft) {
|
|
199
|
+
const current = draft;
|
|
200
|
+
if (hasAuthProfileConfigConflict(current, profile, Boolean(ctx.overwrite))) throw new CodexAuthConfigConflict();
|
|
201
|
+
replaceConfigDraft(draft, applyConfig(current));
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
return "configured";
|
|
205
|
+
} catch (error) {
|
|
206
|
+
return error instanceof CodexAuthConfigConflict ? "conflict" : "unavailable";
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function applyCodexAuthConfig(ctx, credential, profileId) {
|
|
210
|
+
const profile = authProfileConfigForCredential(credential, profileId);
|
|
211
|
+
if (!profile) return "unavailable";
|
|
212
|
+
return applyCodexAuthProfileConfig(ctx, profile, (config) => applyCredentialConfig(config, credential, profileId));
|
|
213
|
+
}
|
|
214
|
+
function applyCredentialConfig(config, credential, profileId) {
|
|
215
|
+
return credential.kind === "oauth" ? applyOAuthConfigToConfig(config, credential, profileId) : applyApiKeyConfigToConfig(config, credential, profileId);
|
|
216
|
+
}
|
|
217
|
+
async function buildCodexAuthItems(params) {
|
|
218
|
+
const credentials = await readCodexAuthCredentials(params.source);
|
|
219
|
+
if (credentials.length === 0) return [];
|
|
220
|
+
const store = loadAuthProfileStoreWithoutExternalProfiles(params.targets.agentDir);
|
|
221
|
+
const skipped = !params.ctx.includeSecrets;
|
|
222
|
+
return credentials.map((credential) => {
|
|
223
|
+
const { profileId, matchedExisting } = itemProfileTarget(credential, store);
|
|
224
|
+
const targetExists = Boolean(store.profiles[profileId]);
|
|
225
|
+
const configProfile = authProfileConfigForCredential(credential, profileId);
|
|
226
|
+
const configConflict = configProfile ? hasAuthProfileConfigConflict(params.ctx.config, configProfile, Boolean(params.ctx.overwrite)) : false;
|
|
227
|
+
const conflict = (targetExists && !matchedExisting && !params.ctx.overwrite || configConflict) && !skipped;
|
|
228
|
+
return createMigrationItem({
|
|
229
|
+
id: `auth:${credential.provider}`,
|
|
230
|
+
kind: "auth",
|
|
231
|
+
action: skipped ? "skip" : "create",
|
|
232
|
+
source: params.source.authPath,
|
|
233
|
+
target: `${params.targets.agentDir}/openclaw-agent.sqlite#auth_profile_store:${profileId}`,
|
|
234
|
+
status: skipped ? "skipped" : conflict ? "conflict" : "planned",
|
|
235
|
+
sensitive: true,
|
|
236
|
+
reason: skipped ? CODEX_REASON_AUTH_NOT_SELECTED : conflict ? CODEX_REASON_AUTH_PROFILE_EXISTS : void 0,
|
|
237
|
+
message: credential.kind === "oauth" ? "Import Codex OAuth credentials and configure OpenAI Codex models." : "Import Codex OpenAI API key.",
|
|
238
|
+
details: {
|
|
239
|
+
provider: credential.provider,
|
|
240
|
+
profileId,
|
|
241
|
+
sourceProfileId: credential.profileId,
|
|
242
|
+
sourceKind: "codex-auth-json",
|
|
243
|
+
credentialKind: credential.kind
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
async function applyCodexAuthItems(params) {
|
|
249
|
+
const { ctx, item, source, targets } = params;
|
|
250
|
+
if (item.status !== "planned") return [item];
|
|
251
|
+
const profileId = typeof item.details?.profileId === "string" ? item.details.profileId : "";
|
|
252
|
+
const provider = typeof item.details?.provider === "string" ? item.details.provider : "";
|
|
253
|
+
const sourceProfileId = typeof item.details?.sourceProfileId === "string" ? item.details.sourceProfileId : void 0;
|
|
254
|
+
if (!profileId || !provider) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
|
|
255
|
+
const credential = (await readCodexAuthCredentials(source)).find((candidate) => candidate.provider === provider);
|
|
256
|
+
if (!credential) return [markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT)];
|
|
257
|
+
if (credential.kind === "oauth" && sourceProfileId && credential.profileId !== sourceProfileId) return [markMigrationItemSkipped(item, CODEX_REASON_AUTH_NO_LONGER_PRESENT)];
|
|
258
|
+
const oauthProfile = credential.kind === "oauth" ? credential.result.profiles[0] : void 0;
|
|
259
|
+
const oauthCredential = oauthProfile?.credential.type === "oauth" ? oauthProfile.credential : void 0;
|
|
260
|
+
if (credential.kind === "oauth" && !oauthCredential) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
|
|
261
|
+
const configProfile = authProfileConfigForCredential(credential, profileId);
|
|
262
|
+
if (!configProfile) return [markMigrationItemError(item, CODEX_REASON_MISSING_AUTH_METADATA)];
|
|
263
|
+
if (hasCurrentAuthProfileConfigConflict(ctx, configProfile)) return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
|
|
264
|
+
let conflicted = false;
|
|
265
|
+
let wrote = false;
|
|
266
|
+
const store = await updateAuthProfileStoreWithLock({
|
|
267
|
+
agentDir: targets.agentDir,
|
|
268
|
+
stateDir: ctx.stateDir,
|
|
269
|
+
updater: (freshStore) => {
|
|
270
|
+
const existing = freshStore.profiles[profileId];
|
|
271
|
+
if (!ctx.overwrite && existing) {
|
|
272
|
+
if ((credential.kind === "oauth" ? findMatchingOAuthProfile(freshStore, oauthCredential) : findMatchingApiKeyProfile(freshStore, credential.provider, credential.key)) === profileId) return false;
|
|
273
|
+
conflicted = true;
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
freshStore.profiles[profileId] = credential.kind === "oauth" ? {
|
|
277
|
+
...oauthCredential,
|
|
278
|
+
displayName: CODEX_IMPORT_DISPLAY_NAME
|
|
279
|
+
} : {
|
|
280
|
+
...buildApiKeyCredential(credential.provider, credential.key),
|
|
281
|
+
displayName: CODEX_IMPORT_DISPLAY_NAME
|
|
282
|
+
};
|
|
283
|
+
wrote = true;
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if (conflicted) return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
|
|
288
|
+
if (!store?.profiles[profileId]) return [markMigrationItemError(item, CODEX_REASON_AUTH_PROFILE_WRITE_FAILED)];
|
|
289
|
+
const configResult = shouldReturnAuthConfigPatch(ctx) ? "unavailable" : await applyCodexAuthConfig(ctx, credential, profileId);
|
|
290
|
+
if (configResult === "conflict") return [markMigrationItemConflict(item, CODEX_REASON_AUTH_PROFILE_EXISTS)];
|
|
291
|
+
const migratedItem = {
|
|
292
|
+
...item,
|
|
293
|
+
status: "migrated",
|
|
294
|
+
details: {
|
|
295
|
+
...item.details,
|
|
296
|
+
wroteAuthProfile: wrote,
|
|
297
|
+
configUpdated: configResult === "configured",
|
|
298
|
+
...shouldReturnAuthConfigPatch(ctx) ? { configPatchReturned: true } : {}
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
return [migratedItem, ...shouldReturnAuthConfigPatch(ctx) ? buildCodexAuthConfigPatchItems(ctx, migratedItem, credential, profileId) : []];
|
|
302
|
+
}
|
|
303
|
+
function buildCodexAuthConfigPatchItems(ctx, item, credential, profileId) {
|
|
304
|
+
const next = applyCredentialConfig(ctx.config, credential, profileId);
|
|
305
|
+
const items = [];
|
|
306
|
+
if (next.auth) items.push(createMigrationItem({
|
|
307
|
+
id: `${item.id}:config:auth`,
|
|
308
|
+
kind: "config",
|
|
309
|
+
action: "merge",
|
|
310
|
+
status: "migrated",
|
|
311
|
+
target: "auth",
|
|
312
|
+
message: "Configure imported Codex auth profile.",
|
|
313
|
+
details: {
|
|
314
|
+
path: ["auth"],
|
|
315
|
+
value: next.auth
|
|
316
|
+
}
|
|
317
|
+
}));
|
|
318
|
+
if (next.agents?.defaults) items.push(createMigrationItem({
|
|
319
|
+
id: `${item.id}:config:agents-defaults`,
|
|
320
|
+
kind: "config",
|
|
321
|
+
action: "merge",
|
|
322
|
+
status: "migrated",
|
|
323
|
+
target: "agents.defaults",
|
|
324
|
+
message: "Configure imported Codex models.",
|
|
325
|
+
details: {
|
|
326
|
+
path: ["agents", "defaults"],
|
|
327
|
+
value: next.agents.defaults
|
|
328
|
+
}
|
|
329
|
+
}));
|
|
330
|
+
return items;
|
|
331
|
+
}
|
|
332
|
+
//#endregion
|
|
333
|
+
//#region extensions/codex/src/migration/targets.ts
|
|
334
|
+
function resolveCodexMigrationTargets(ctx) {
|
|
335
|
+
const cfg = ctx.config;
|
|
336
|
+
const agentId = ctx.targetAgentId ?? resolveDefaultAgentId(cfg);
|
|
337
|
+
const workspaceDir = resolveAgentWorkspaceDir(cfg, agentId);
|
|
338
|
+
const configuredAgentDir = resolveAgentConfig(cfg, agentId)?.agentDir?.trim();
|
|
339
|
+
return {
|
|
340
|
+
workspaceDir,
|
|
341
|
+
agentDir: ctx.runtime?.agent?.resolveAgentDir(cfg, agentId) ?? (configuredAgentDir ? resolveHomePath(configuredAgentDir) : void 0) ?? path.join(ctx.stateDir, "agents", agentId, "agent")
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region extensions/codex/src/migration/plan.ts
|
|
346
|
+
const CODEX_PLUGIN_CONFIG_ITEM_ID = "config:codex-plugins";
|
|
347
|
+
const CODEX_PLUGIN_CONFIG_PATH = [
|
|
348
|
+
"plugins",
|
|
349
|
+
"entries",
|
|
350
|
+
"codex"
|
|
351
|
+
];
|
|
352
|
+
const CODEX_PLUGIN_ENABLED_PATH = [
|
|
353
|
+
"plugins",
|
|
354
|
+
"entries",
|
|
355
|
+
"codex",
|
|
356
|
+
"enabled"
|
|
357
|
+
];
|
|
358
|
+
const CODEX_PLUGIN_NATIVE_CONFIG_PATH = [
|
|
359
|
+
"plugins",
|
|
360
|
+
"entries",
|
|
361
|
+
"codex",
|
|
362
|
+
"config",
|
|
363
|
+
"codexPlugins"
|
|
364
|
+
];
|
|
365
|
+
const MIGRATION_REASON_PLUGIN_EXISTS = "plugin exists";
|
|
366
|
+
const CODEX_PLUGIN_SOURCE_APP_VERIFICATION_UNVERIFIED = "not_run";
|
|
367
|
+
const MIGRATION_REASON_TARGET_NOT_REGULAR = "target is not a regular file";
|
|
368
|
+
async function lstatIfExists(filePath) {
|
|
369
|
+
try {
|
|
370
|
+
return await fs.lstat(filePath);
|
|
371
|
+
} catch (error) {
|
|
372
|
+
const code = extractErrorCode(error);
|
|
373
|
+
if (code === "ENOENT" || code === "ENOTDIR") return;
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async function buildCodexMemoryItems(params) {
|
|
378
|
+
const items = [];
|
|
379
|
+
for (const memory of params.memoryFiles) {
|
|
380
|
+
const target = path.join(params.workspaceDir, "memory", "imports", "codex", path.basename(memory.path));
|
|
381
|
+
const targetStat = await lstatIfExists(target);
|
|
382
|
+
const targetNotRegular = targetStat !== void 0 && !targetStat.isFile();
|
|
383
|
+
if (!targetNotRegular) {
|
|
384
|
+
const [source, workspace, destination] = await Promise.all([
|
|
385
|
+
fs.realpath(path.dirname(memory.path)),
|
|
386
|
+
canonicalPathFromExistingAncestor(params.workspaceDir),
|
|
387
|
+
canonicalPathFromExistingAncestor(target)
|
|
388
|
+
]);
|
|
389
|
+
if (!isPathInside(workspace, destination)) throw new Error("Codex memory import destination must stay in the selected workspace.");
|
|
390
|
+
if (isPathInside(source, destination) || isPathInside(destination, source)) throw new Error("Codex memory source and OpenClaw import destination must be separate paths.");
|
|
391
|
+
}
|
|
392
|
+
const targetConflict = targetStat !== void 0 && !params.overwrite;
|
|
393
|
+
items.push(createMigrationItem({
|
|
394
|
+
id: memory.id,
|
|
395
|
+
kind: "memory",
|
|
396
|
+
action: "copy",
|
|
397
|
+
source: memory.path,
|
|
398
|
+
target,
|
|
399
|
+
status: targetNotRegular || targetConflict ? "conflict" : "planned",
|
|
400
|
+
reason: targetNotRegular ? MIGRATION_REASON_TARGET_NOT_REGULAR : targetConflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
|
|
401
|
+
message: "Copy consolidated Codex memory into the OpenClaw memory index.",
|
|
402
|
+
details: {
|
|
403
|
+
sourceType: "codex-memory",
|
|
404
|
+
sourceLabel: memory.label,
|
|
405
|
+
collectionId: "codex",
|
|
406
|
+
collectionLabel: "Codex",
|
|
407
|
+
relativePath: path.basename(memory.path)
|
|
408
|
+
}
|
|
409
|
+
}));
|
|
410
|
+
}
|
|
411
|
+
return items;
|
|
412
|
+
}
|
|
413
|
+
function uniqueSkillName(skill, counts) {
|
|
414
|
+
const base = sanitizeName(skill.name) || "codex-skill";
|
|
415
|
+
if ((counts.get(base) ?? 0) <= 1) return base;
|
|
416
|
+
const parent = sanitizeName(path.basename(path.dirname(skill.source)));
|
|
417
|
+
return sanitizeName([
|
|
418
|
+
"codex",
|
|
419
|
+
parent,
|
|
420
|
+
base
|
|
421
|
+
].filter(Boolean).join("-")) || base;
|
|
422
|
+
}
|
|
423
|
+
async function buildCodexSkillItems(params) {
|
|
424
|
+
const counts = /* @__PURE__ */ new Map();
|
|
425
|
+
for (const skill of params.skills) {
|
|
426
|
+
const base = sanitizeName(skill.name) || "codex-skill";
|
|
427
|
+
counts.set(base, (counts.get(base) ?? 0) + 1);
|
|
428
|
+
}
|
|
429
|
+
const planned = params.skills.map((skill) => {
|
|
430
|
+
const name = uniqueSkillName(skill, counts);
|
|
431
|
+
return {
|
|
432
|
+
skill,
|
|
433
|
+
name,
|
|
434
|
+
target: path.join(params.workspaceDir, "skills", name)
|
|
435
|
+
};
|
|
436
|
+
});
|
|
437
|
+
const resolvedCounts = planned.reduce((resolved, item) => {
|
|
438
|
+
resolved.set(item.name, (resolved.get(item.name) ?? 0) + 1);
|
|
439
|
+
return resolved;
|
|
440
|
+
}, /* @__PURE__ */ new Map());
|
|
441
|
+
return await Promise.all(planned.map(async (item) => {
|
|
442
|
+
const collision = (resolvedCounts.get(item.name) ?? 0) > 1;
|
|
443
|
+
const targetExists = await exists(item.target);
|
|
444
|
+
const conflict = collision || targetExists && !params.overwrite;
|
|
445
|
+
return createMigrationItem({
|
|
446
|
+
id: `skill:${item.name}`,
|
|
447
|
+
kind: "skill",
|
|
448
|
+
action: "copy",
|
|
449
|
+
source: item.skill.source,
|
|
450
|
+
target: item.target,
|
|
451
|
+
status: conflict ? "conflict" : "planned",
|
|
452
|
+
reason: collision ? `multiple Codex skills normalize to "${item.name}"` : conflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
|
|
453
|
+
message: `Copy ${item.skill.sourceLabel} into this OpenClaw agent workspace.`,
|
|
454
|
+
details: {
|
|
455
|
+
skillName: item.name,
|
|
456
|
+
sourceLabel: item.skill.sourceLabel
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}));
|
|
460
|
+
}
|
|
461
|
+
function readExistingCodexPluginEntries(config) {
|
|
462
|
+
const entries = readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "plugins"]);
|
|
463
|
+
return isRecord(entries) ? entries : {};
|
|
464
|
+
}
|
|
465
|
+
function hasExistingCodexPluginEntry(existingEntries, configKey, pluginName, nextEntry) {
|
|
466
|
+
const existingEntry = existingEntries[configKey];
|
|
467
|
+
if (existingEntry !== void 0) return !isLegacyDestructivePolicyRepair(existingEntry, nextEntry);
|
|
468
|
+
return Object.values(existingEntries).some((entry) => {
|
|
469
|
+
if (!isRecord(entry)) return false;
|
|
470
|
+
return entry.pluginName === pluginName;
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
function isLegacyDestructivePolicyRepair(existing, nextEntry) {
|
|
474
|
+
const existingEntry = isRecord(existing) ? existing : void 0;
|
|
475
|
+
if (existingEntry?.allow_destructive_actions !== "on-request" || nextEntry.allow_destructive_actions !== "auto") return false;
|
|
476
|
+
const normalizedExisting = {
|
|
477
|
+
...existingEntry,
|
|
478
|
+
allow_destructive_actions: "auto"
|
|
479
|
+
};
|
|
480
|
+
const normalizedEntries = Object.entries(normalizedExisting);
|
|
481
|
+
return normalizedEntries.length === Object.keys(nextEntry).length && normalizedEntries.every(([key, value]) => nextEntry[key] === value);
|
|
482
|
+
}
|
|
483
|
+
function readExistingPluginAllowDestructiveActions(existing, pluginName) {
|
|
484
|
+
const existingEntry = isRecord(existing) ? existing : void 0;
|
|
485
|
+
if (existingEntry?.pluginName !== pluginName) return;
|
|
486
|
+
const normalized = normalizeExistingAllowDestructiveActions(existingEntry.allow_destructive_actions);
|
|
487
|
+
return normalized === "auto" || normalized === "ask" ? normalized : void 0;
|
|
488
|
+
}
|
|
489
|
+
function buildPluginItems(ctx, plugins) {
|
|
490
|
+
const existingPluginEntries = readExistingCodexPluginEntries(ctx.config);
|
|
491
|
+
let manualIndex = 0;
|
|
492
|
+
const items = [];
|
|
493
|
+
for (const plugin of plugins) {
|
|
494
|
+
if (plugin.migratable && plugin.marketplaceName === "openai-curated" && plugin.pluginName) {
|
|
495
|
+
const configKey = plugin.pluginName;
|
|
496
|
+
const plannedEntry = {
|
|
497
|
+
enabled: true,
|
|
498
|
+
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
499
|
+
pluginName: plugin.pluginName,
|
|
500
|
+
...(() => {
|
|
501
|
+
const allowDestructiveActions = readExistingPluginAllowDestructiveActions(existingPluginEntries[configKey], plugin.pluginName);
|
|
502
|
+
return allowDestructiveActions ? { allow_destructive_actions: allowDestructiveActions } : {};
|
|
503
|
+
})()
|
|
504
|
+
};
|
|
505
|
+
const conflict = !ctx.overwrite && hasExistingCodexPluginEntry(existingPluginEntries, configKey, plugin.pluginName, plannedEntry);
|
|
506
|
+
items.push(createMigrationItem({
|
|
507
|
+
id: `plugin:${configKey}`,
|
|
508
|
+
kind: "plugin",
|
|
509
|
+
action: "install",
|
|
510
|
+
status: conflict ? "conflict" : "planned",
|
|
511
|
+
reason: conflict ? MIGRATION_REASON_PLUGIN_EXISTS : void 0,
|
|
512
|
+
applyPhase: "after-promotion",
|
|
513
|
+
source: plugin.source,
|
|
514
|
+
target: `plugins.entries.codex.config.codexPlugins.plugins.${configKey}`,
|
|
515
|
+
message: `Install Codex plugin "${plugin.pluginName}" in the OpenClaw-managed Codex app-server runtime.`,
|
|
516
|
+
details: {
|
|
517
|
+
configKey,
|
|
518
|
+
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
519
|
+
pluginName: plugin.pluginName,
|
|
520
|
+
sourceInstalled: plugin.installed === true,
|
|
521
|
+
sourceEnabled: plugin.enabled === true,
|
|
522
|
+
...plannedEntry.allow_destructive_actions === "auto" || plannedEntry.allow_destructive_actions === "ask" ? { allowDestructiveActions: plannedEntry.allow_destructive_actions } : {},
|
|
523
|
+
...plugin.apps && plugin.apps.length > 0 && !shouldVerifyPluginApps(ctx) ? { sourceAppVerification: CODEX_PLUGIN_SOURCE_APP_VERIFICATION_UNVERIFIED } : {}
|
|
524
|
+
}
|
|
525
|
+
}));
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
manualIndex += 1;
|
|
529
|
+
if (plugin.migrationBlock && plugin.pluginName) {
|
|
530
|
+
items.push(createMigrationItem({
|
|
531
|
+
id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
|
|
532
|
+
kind: "manual",
|
|
533
|
+
action: "manual",
|
|
534
|
+
source: plugin.source,
|
|
535
|
+
status: "skipped",
|
|
536
|
+
reason: plugin.migrationBlock.code,
|
|
537
|
+
message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
|
|
538
|
+
details: {
|
|
539
|
+
pluginName: plugin.pluginName,
|
|
540
|
+
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
541
|
+
...plugin.migrationBlock.apps ? { apps: plugin.migrationBlock.apps } : {},
|
|
542
|
+
...plugin.migrationBlock.error ? { error: plugin.migrationBlock.error } : {}
|
|
543
|
+
}
|
|
544
|
+
}));
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
items.push(createMigrationManualItem({
|
|
548
|
+
id: `plugin:${sanitizeName(plugin.name) || sanitizeName(path.basename(plugin.source))}:${manualIndex}`,
|
|
549
|
+
source: plugin.source,
|
|
550
|
+
message: plugin.message ?? `Codex native plugin "${plugin.name}" was found but not activated automatically.`,
|
|
551
|
+
recommendation: "Review the plugin bundle first, then install trusted compatible plugins with openclaw plugins install <path> --force."
|
|
552
|
+
}));
|
|
553
|
+
}
|
|
554
|
+
return items;
|
|
555
|
+
}
|
|
556
|
+
function shouldVerifyPluginApps(ctx) {
|
|
557
|
+
return ctx.providerOptions?.verifyPluginApps === true;
|
|
558
|
+
}
|
|
559
|
+
function readCodexPluginMigrationConfigEntry(item, enabled) {
|
|
560
|
+
const configKey = item.details?.configKey;
|
|
561
|
+
const marketplaceName = item.details?.marketplaceName;
|
|
562
|
+
const pluginName = item.details?.pluginName;
|
|
563
|
+
if (item.kind !== "plugin" || item.action !== "install" || typeof configKey !== "string" || marketplaceName !== "openai-curated" || typeof pluginName !== "string") return;
|
|
564
|
+
const allowDestructiveActions = item.details?.allowDestructiveActions;
|
|
565
|
+
return {
|
|
566
|
+
configKey,
|
|
567
|
+
pluginName,
|
|
568
|
+
enabled,
|
|
569
|
+
...allowDestructiveActions === "auto" || allowDestructiveActions === "ask" ? { allowDestructiveActions } : {}
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function readExistingAllowDestructiveActions(config) {
|
|
573
|
+
return normalizeExistingAllowDestructiveActions(readMigrationConfigPath(config, [...CODEX_PLUGIN_NATIVE_CONFIG_PATH, "allow_destructive_actions"]));
|
|
574
|
+
}
|
|
575
|
+
function normalizeExistingAllowDestructiveActions(value) {
|
|
576
|
+
if (value === "auto" || value === "on-request") return "auto";
|
|
577
|
+
if (value === "ask") return "ask";
|
|
578
|
+
return asBoolean(value);
|
|
579
|
+
}
|
|
580
|
+
function readExistingPluginPolicyRepairs(config) {
|
|
581
|
+
return Object.fromEntries(Object.entries(readExistingCodexPluginEntries(config)).flatMap(([configKey, entry]) => {
|
|
582
|
+
const pluginEntry = isRecord(entry) ? entry : void 0;
|
|
583
|
+
if (pluginEntry?.allow_destructive_actions !== "on-request") return [];
|
|
584
|
+
return [[configKey, {
|
|
585
|
+
...pluginEntry,
|
|
586
|
+
allow_destructive_actions: "auto"
|
|
587
|
+
}]];
|
|
588
|
+
}));
|
|
589
|
+
}
|
|
590
|
+
function buildCodexPluginsConfigValue(entries, config) {
|
|
591
|
+
const plugins = {
|
|
592
|
+
...readExistingPluginPolicyRepairs(config),
|
|
593
|
+
...Object.fromEntries(entries.toSorted((a, b) => a.configKey.localeCompare(b.configKey)).map((entry) => [entry.configKey, {
|
|
594
|
+
enabled: entry.enabled,
|
|
595
|
+
marketplaceName: CODEX_PLUGINS_MARKETPLACE_NAME,
|
|
596
|
+
pluginName: entry.pluginName,
|
|
597
|
+
...entry.allowDestructiveActions ? { allow_destructive_actions: entry.allowDestructiveActions } : {}
|
|
598
|
+
}]))
|
|
599
|
+
};
|
|
600
|
+
return {
|
|
601
|
+
enabled: true,
|
|
602
|
+
config: { codexPlugins: {
|
|
603
|
+
enabled: true,
|
|
604
|
+
allow_destructive_actions: readExistingAllowDestructiveActions(config) ?? true,
|
|
605
|
+
plugins
|
|
606
|
+
} }
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
function hasCodexPluginConfigConflict(config, value) {
|
|
610
|
+
const enabled = readMigrationConfigPath(config, CODEX_PLUGIN_ENABLED_PATH);
|
|
611
|
+
if (enabled !== void 0 && enabled !== true) return true;
|
|
612
|
+
const nativeConfig = value.config?.codexPlugins;
|
|
613
|
+
if (!isRecord(nativeConfig)) return hasMigrationConfigPatchConflict(config, CODEX_PLUGIN_NATIVE_CONFIG_PATH, nativeConfig);
|
|
614
|
+
const existingNativeConfig = readMigrationConfigPath(config, CODEX_PLUGIN_NATIVE_CONFIG_PATH);
|
|
615
|
+
if (existingNativeConfig === void 0) return false;
|
|
616
|
+
if (!isRecord(existingNativeConfig)) return true;
|
|
617
|
+
if (existingNativeConfig.enabled !== void 0 && existingNativeConfig.enabled !== true) return true;
|
|
618
|
+
const allowDestructiveActions = nativeConfig.allow_destructive_actions;
|
|
619
|
+
const existingAllowDestructiveActions = normalizeExistingAllowDestructiveActions(existingNativeConfig.allow_destructive_actions);
|
|
620
|
+
if (existingNativeConfig.allow_destructive_actions !== void 0 && existingAllowDestructiveActions !== allowDestructiveActions) return true;
|
|
621
|
+
const plugins = nativeConfig.plugins;
|
|
622
|
+
if (!isRecord(plugins)) return false;
|
|
623
|
+
return Object.entries(plugins).some(([configKey, plugin]) => {
|
|
624
|
+
if (!isRecord(plugin)) return existingNativeConfig[configKey] !== void 0;
|
|
625
|
+
return hasExistingCodexPluginEntry(readExistingCodexPluginEntries(config), configKey, typeof plugin.pluginName === "string" ? plugin.pluginName : configKey, plugin);
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
function buildPluginConfigItem(ctx, pluginItems) {
|
|
629
|
+
const entries = pluginItems.filter((item) => item.status === "planned").map((item) => readCodexPluginMigrationConfigEntry(item, true)).filter((entry) => entry !== void 0);
|
|
630
|
+
if (entries.length === 0) return;
|
|
631
|
+
const value = buildCodexPluginsConfigValue(entries, ctx.config);
|
|
632
|
+
const conflict = !ctx.overwrite && hasCodexPluginConfigConflict(ctx.config, value);
|
|
633
|
+
return createMigrationItem({
|
|
634
|
+
id: CODEX_PLUGIN_CONFIG_ITEM_ID,
|
|
635
|
+
kind: "config",
|
|
636
|
+
action: "merge",
|
|
637
|
+
target: "plugins.entries.codex.config.codexPlugins",
|
|
638
|
+
status: conflict ? "conflict" : "planned",
|
|
639
|
+
reason: conflict ? MIGRATION_REASON_TARGET_EXISTS : void 0,
|
|
640
|
+
applyPhase: "after-promotion",
|
|
641
|
+
message: "Enable OpenClaw's Codex plugin integration and record migrated source-installed curated plugins.",
|
|
642
|
+
details: {
|
|
643
|
+
path: [...CODEX_PLUGIN_CONFIG_PATH],
|
|
644
|
+
value
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
async function buildCodexMigrationPlan(ctx) {
|
|
649
|
+
const targets = resolveCodexMigrationTargets(ctx);
|
|
650
|
+
const memoryOnly = ctx.itemKinds !== void 0 && ctx.itemKinds.length > 0 && ctx.itemKinds.every((kind) => kind === "memory");
|
|
651
|
+
const source = await discoverCodexSource({
|
|
652
|
+
input: ctx.source,
|
|
653
|
+
memoryOnly,
|
|
654
|
+
evaluatePluginMigrationEligibility: !memoryOnly,
|
|
655
|
+
verifyPluginApps: shouldVerifyPluginApps(ctx)
|
|
656
|
+
});
|
|
657
|
+
if (!hasCodexSource(source)) throw new Error(`Codex state was not found at ${source.root}. Pass --from <path> if it lives elsewhere.`);
|
|
658
|
+
const items = [];
|
|
659
|
+
items.push(...await buildCodexMemoryItems({
|
|
660
|
+
memoryFiles: source.memoryFiles,
|
|
661
|
+
workspaceDir: targets.workspaceDir,
|
|
662
|
+
overwrite: ctx.overwrite
|
|
663
|
+
}));
|
|
664
|
+
if (!memoryOnly) {
|
|
665
|
+
items.push(...await buildCodexAuthItems({
|
|
666
|
+
ctx,
|
|
667
|
+
source,
|
|
668
|
+
targets
|
|
669
|
+
}));
|
|
670
|
+
items.push(...await buildCodexSkillItems({
|
|
671
|
+
skills: source.skills,
|
|
672
|
+
workspaceDir: targets.workspaceDir,
|
|
673
|
+
overwrite: ctx.overwrite
|
|
674
|
+
}));
|
|
675
|
+
const pluginItems = buildPluginItems(ctx, source.plugins);
|
|
676
|
+
items.push(...pluginItems);
|
|
677
|
+
const pluginConfigItem = buildPluginConfigItem(ctx, pluginItems);
|
|
678
|
+
if (pluginConfigItem) items.push(pluginConfigItem);
|
|
679
|
+
for (const archivePath of source.archivePaths) items.push(createMigrationItem({
|
|
680
|
+
id: archivePath.id,
|
|
681
|
+
kind: "archive",
|
|
682
|
+
action: "archive",
|
|
683
|
+
source: archivePath.path,
|
|
684
|
+
message: archivePath.message ?? "Archived in the migration report for manual review; not imported into live config.",
|
|
685
|
+
details: { archiveRelativePath: archivePath.relativePath }
|
|
686
|
+
}));
|
|
687
|
+
}
|
|
688
|
+
const warnings = [
|
|
689
|
+
...!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."] : [],
|
|
690
|
+
...items.some((item) => item.status === "conflict") ? ["Conflicts were found. Re-run with --overwrite to replace conflicting migration targets after item-level backups."] : [],
|
|
691
|
+
...source.pluginDiscoveryError ? [`Codex app-server plugin inventory discovery failed: ${source.pluginDiscoveryError}. Cached plugin bundles, if any, are advisory only.`] : [],
|
|
692
|
+
...source.plugins.some((plugin) => plugin.migrationBlock?.code === "codex_subscription_required") ? [codexPluginMigrationSubscriptionWarning()] : []
|
|
693
|
+
];
|
|
694
|
+
return {
|
|
695
|
+
providerId: "codex",
|
|
696
|
+
source: source.root,
|
|
697
|
+
target: targets.workspaceDir,
|
|
698
|
+
summary: summarizeMigrationItems(items),
|
|
699
|
+
items,
|
|
700
|
+
warnings,
|
|
701
|
+
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."],
|
|
702
|
+
metadata: {
|
|
703
|
+
agentDir: targets.agentDir,
|
|
704
|
+
codexHome: source.codexHome,
|
|
705
|
+
codexSkillsDir: source.codexSkillsDir,
|
|
706
|
+
personalAgentsSkillsDir: source.personalAgentsSkillsDir
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
//#endregion
|
|
711
|
+
export { CODEX_PLUGIN_CONFIG_ITEM_ID, CODEX_PLUGIN_CONFIG_PATH, buildCodexMigrationPlan, buildCodexPluginsConfigValue, hasCodexPluginConfigConflict, applyCodexAuthItems as n, readCodexPluginMigrationConfigEntry, resolveCodexMigrationTargets as t };
|