fix(onboarding): treat already-installed codex as success, update in place (#80816)

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.
This commit is contained in:
Sarah Fortune
2026-05-11 17:26:40 -07:00
committed by GitHub
parent 5c4f5e0f3a
commit e9421bbdec
@@ -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,