From e9421bbdecb863462a56070bc00ea5c4de8e9ce2 Mon Sep 17 00:00:00 2001 From: Sarah Fortune Date: Mon, 11 May 2026 17:26:40 -0700 Subject: [PATCH] fix(onboarding): treat already-installed codex as success, update in place (#80816) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When `ensureCodexRuntimePluginForModelSelection` ran for a host that already had `@openclaw/codex` installed under the managed npm root, the default `mode: "install"` path in `installPluginFromNpmSpec` returned "plugin already exists" from `ensureInstallTargetAvailable` and the wizard marked the step as failed. Now, when the install record points at a real package on disk, route through the existing `repairCodexRuntimePluginInstallForModelSelection` flow (which runs `repairMissingPluginInstallsForIds` → `updateNpmInstalledPlugins`), forward any changes/warnings to the runtime log, enable the plugin in cfg, and return `installed`. A fresh install still flows through `ensureOnboardingPluginInstalled` so the wizard progress UI is unchanged. --- src/commands/codex-runtime-plugin-install.ts | 38 ++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/commands/codex-runtime-plugin-install.ts b/src/commands/codex-runtime-plugin-install.ts index 6663791c3540..4341a97f6a88 100644 --- a/src/commands/codex-runtime-plugin-install.ts +++ b/src/commands/codex-runtime-plugin-install.ts @@ -1,12 +1,29 @@ +import { existsSync } from "node:fs"; +import path from "node:path"; import { modelSelectionShouldEnsureCodexPlugin } from "../agents/openai-codex-routing.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; +import type { PluginInstallRecord } from "../config/types.plugins.js"; +import { enablePluginInConfig } from "../plugins/enable.js"; +import { loadInstalledPluginIndexInstallRecords } from "../plugins/installed-plugin-index-records.js"; import type { RuntimeEnv } from "../runtime.js"; +import { resolveUserPath } from "../utils.js"; import type { WizardPrompter } from "../wizard/prompts.js"; const CODEX_RUNTIME_PLUGIN_ID = "codex"; const CODEX_RUNTIME_PLUGIN_LABEL = "Codex"; const CODEX_RUNTIME_PLUGIN_NPM_SPEC = "@openclaw/codex"; +function isInstalledRecordPresentOnDisk( + record: PluginInstallRecord | undefined, + env: NodeJS.ProcessEnv, +): boolean { + const installPath = record?.installPath?.trim(); + if (!installPath) { + return false; + } + return existsSync(path.join(resolveUserPath(installPath, env), "package.json")); +} + export type CodexRuntimePluginInstallResult = { cfg: OpenClawConfig; required: boolean; @@ -34,6 +51,27 @@ export async function ensureCodexRuntimePluginForModelSelection(params: { if (!selectedModelShouldEnsureCodexRuntimePlugin({ cfg: params.cfg, model: params.model })) { return { cfg: params.cfg, required: false, installed: false }; } + const existingRecords = await loadInstalledPluginIndexInstallRecords({ env: process.env }); + if (isInstalledRecordPresentOnDisk(existingRecords[CODEX_RUNTIME_PLUGIN_ID], process.env)) { + const repair = await repairCodexRuntimePluginInstallForModelSelection({ + cfg: params.cfg, + model: params.model, + env: process.env, + }); + for (const change of repair.changes) { + params.runtime.log?.(change); + } + for (const warning of repair.warnings) { + params.runtime.log?.(`Codex update warning: ${warning}`); + } + const enableResult = enablePluginInConfig(params.cfg, CODEX_RUNTIME_PLUGIN_ID); + return { + cfg: enableResult.enabled ? enableResult.config : params.cfg, + required: true, + installed: true, + status: "installed", + }; + } const { ensureOnboardingPluginInstalled } = await import("./onboarding-plugin-install.js"); const result = await ensureOnboardingPluginInstalled({ cfg: params.cfg,