From 2c25e1d58db2b511f781e39e01eb6ee18066b70a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 23 Apr 2026 05:05:35 +0100 Subject: [PATCH] fix(openai): align auth picker copy metadata --- extensions/openai/auth-choice-copy.ts | 11 ++++ .../openai/openai-codex-provider.test.ts | 4 +- extensions/openai/openai-codex-provider.ts | 16 +++--- extensions/openai/openai-provider.test.ts | 2 +- extensions/openai/openai-provider.ts | 9 ++-- extensions/openai/openclaw.plugin.json | 6 +-- extensions/openai/openclaw.plugin.test.ts | 53 +++++++++++++++++-- extensions/openai/provider-contract-api.ts | 33 ++++++------ 8 files changed, 96 insertions(+), 38 deletions(-) create mode 100644 extensions/openai/auth-choice-copy.ts diff --git a/extensions/openai/auth-choice-copy.ts b/extensions/openai/auth-choice-copy.ts new file mode 100644 index 00000000000..e9f3d73f296 --- /dev/null +++ b/extensions/openai/auth-choice-copy.ts @@ -0,0 +1,11 @@ +export const OPENAI_API_KEY_LABEL = "OpenAI API Key"; +export const OPENAI_CODEX_LOGIN_LABEL = "OpenAI Codex Browser Login"; +export const OPENAI_CODEX_LOGIN_HINT = "Sign in with OpenAI in your browser"; +export const OPENAI_CODEX_DEVICE_PAIRING_LABEL = "OpenAI Codex Device Pairing"; +export const OPENAI_CODEX_DEVICE_PAIRING_HINT = "Pair in browser with a device code"; + +export const OPENAI_WIZARD_GROUP = { + groupId: "openai", + groupLabel: "OpenAI", + groupHint: "API key or Codex sign-in", +} as const; diff --git a/extensions/openai/openai-codex-provider.test.ts b/extensions/openai/openai-codex-provider.test.ts index 7aea2fa7934..dac350406b9 100644 --- a/extensions/openai/openai-codex-provider.test.ts +++ b/extensions/openai/openai-codex-provider.test.ts @@ -117,11 +117,11 @@ describe("openai codex provider", () => { expect(oauth?.wizard).toMatchObject({ choiceLabel: "OpenAI Codex Browser Login", - groupHint: "API key + Codex auth", + groupHint: "API key or Codex sign-in", }); expect(deviceCode?.wizard).toMatchObject({ choiceLabel: "OpenAI Codex Device Pairing", - groupHint: "API key + Codex auth", + groupHint: "API key or Codex sign-in", }); }); diff --git a/extensions/openai/openai-codex-provider.ts b/extensions/openai/openai-codex-provider.ts index b6ee97de8f5..9b5c89a5f23 100644 --- a/extensions/openai/openai-codex-provider.ts +++ b/extensions/openai/openai-codex-provider.ts @@ -20,6 +20,13 @@ import { } from "openclaw/plugin-sdk/provider-model-shared"; import { fetchCodexUsage } from "openclaw/plugin-sdk/provider-usage"; import { normalizeLowercaseStringOrEmpty, readStringValue } from "openclaw/plugin-sdk/text-runtime"; +import { + OPENAI_CODEX_DEVICE_PAIRING_HINT, + OPENAI_CODEX_DEVICE_PAIRING_LABEL, + OPENAI_CODEX_LOGIN_HINT, + OPENAI_CODEX_LOGIN_LABEL, + OPENAI_WIZARD_GROUP, +} from "./auth-choice-copy.js"; import { isOpenAIApiBaseUrl, isOpenAICodexBaseUrl } from "./base-url.js"; import { OPENAI_CODEX_DEFAULT_MODEL } from "./default-models.js"; import { resolveCodexAuthIdentity } from "./openai-codex-auth-identity.js"; @@ -35,17 +42,8 @@ import { const PROVIDER_ID = "openai-codex"; const OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api/codex"; -const OPENAI_WIZARD_GROUP = { - groupId: "openai", - groupLabel: "OpenAI", - groupHint: "API key + Codex auth", -} as const; const OPENAI_CODEX_LOGIN_ASSISTANT_PRIORITY = -30; const OPENAI_CODEX_DEVICE_PAIRING_ASSISTANT_PRIORITY = -10; -const OPENAI_CODEX_LOGIN_LABEL = "OpenAI Codex Browser Login"; -const OPENAI_CODEX_LOGIN_HINT = "Sign in with OpenAI in your browser"; -const OPENAI_CODEX_DEVICE_PAIRING_LABEL = "OpenAI Codex Device Pairing"; -const OPENAI_CODEX_DEVICE_PAIRING_HINT = "Pair in browser with a device code"; const OPENAI_CODEX_GPT_54_MODEL_ID = "gpt-5.4"; const OPENAI_CODEX_GPT_54_LEGACY_MODEL_ID = "gpt-5.4-codex"; const OPENAI_CODEX_GPT_54_PRO_MODEL_ID = "gpt-5.4-pro"; diff --git a/extensions/openai/openai-provider.test.ts b/extensions/openai/openai-provider.test.ts index 2314001b0b5..67cfe50c004 100644 --- a/extensions/openai/openai-provider.test.ts +++ b/extensions/openai/openai-provider.test.ts @@ -55,7 +55,7 @@ describe("buildOpenAIProvider", () => { expect(apiKey?.wizard).toMatchObject({ choiceLabel: "OpenAI API Key", - groupHint: "API key + Codex auth", + groupHint: "API key or Codex sign-in", }); }); diff --git a/extensions/openai/openai-provider.ts b/extensions/openai/openai-provider.ts index 6e4b5405cd7..a49507828a8 100644 --- a/extensions/openai/openai-provider.ts +++ b/extensions/openai/openai-provider.ts @@ -10,6 +10,7 @@ import { type ProviderPlugin, } from "openclaw/plugin-sdk/provider-model-shared"; import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; +import { OPENAI_API_KEY_LABEL, OPENAI_WIZARD_GROUP } from "./auth-choice-copy.js"; import { isOpenAIApiBaseUrl } from "./base-url.js"; import { applyOpenAIConfig, OPENAI_DEFAULT_MODEL } from "./default-models.js"; import { @@ -185,7 +186,7 @@ export function buildOpenAIProvider(): ProviderPlugin { createProviderApiKeyAuthMethod({ providerId: PROVIDER_ID, methodId: "api-key", - label: "OpenAI API Key", + label: OPENAI_API_KEY_LABEL, hint: "Use your OpenAI API key directly", optionKey: "openaiApiKey", flagName: "--openai-api-key", @@ -196,10 +197,8 @@ export function buildOpenAIProvider(): ProviderPlugin { applyConfig: (cfg) => applyOpenAIConfig(cfg), wizard: { choiceId: "openai-api-key", - choiceLabel: "OpenAI API Key", - groupId: "openai", - groupLabel: "OpenAI", - groupHint: "API key + Codex auth", + choiceLabel: OPENAI_API_KEY_LABEL, + ...OPENAI_WIZARD_GROUP, }, }), ], diff --git a/extensions/openai/openclaw.plugin.json b/extensions/openai/openclaw.plugin.json index a4b4ea7a899..99187a4449c 100644 --- a/extensions/openai/openclaw.plugin.json +++ b/extensions/openai/openclaw.plugin.json @@ -20,7 +20,7 @@ "assistantPriority": -30, "groupId": "openai", "groupLabel": "OpenAI", - "groupHint": "API key + Codex auth" + "groupHint": "API key or Codex sign-in" }, { "provider": "openai-codex", @@ -31,7 +31,7 @@ "assistantPriority": -10, "groupId": "openai", "groupLabel": "OpenAI", - "groupHint": "API key + Codex auth" + "groupHint": "API key or Codex sign-in" }, { "provider": "openai", @@ -41,7 +41,7 @@ "assistantPriority": -40, "groupId": "openai", "groupLabel": "OpenAI", - "groupHint": "API key + Codex auth", + "groupHint": "API key or Codex sign-in", "optionKey": "openaiApiKey", "cliFlag": "--openai-api-key", "cliOption": "--openai-api-key ", diff --git a/extensions/openai/openclaw.plugin.test.ts b/extensions/openai/openclaw.plugin.test.ts index 114715bfbd6..953e201a11f 100644 --- a/extensions/openai/openclaw.plugin.test.ts +++ b/extensions/openai/openclaw.plugin.test.ts @@ -1,5 +1,7 @@ import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; +import { buildOpenAICodexProviderPlugin } from "./openai-codex-provider.js"; +import { buildOpenAIProvider } from "./openai-provider.js"; const manifest = JSON.parse( readFileSync(new URL("./openclaw.plugin.json", import.meta.url), "utf8"), @@ -15,6 +17,41 @@ const manifest = JSON.parse( }>; }; +function manifestComparableWizardFields(choice: { + choiceId?: string; + choiceLabel?: string; + choiceHint?: string; + groupId?: string; + groupLabel?: string; + groupHint?: string; +}) { + return Object.fromEntries( + Object.entries({ + choiceId: choice.choiceId, + choiceLabel: choice.choiceLabel, + choiceHint: choice.choiceHint, + groupId: choice.groupId, + groupLabel: choice.groupLabel, + groupHint: choice.groupHint, + }).filter(([, value]) => value !== undefined), + ); +} + +function providerWizardByKey() { + const providers = [buildOpenAIProvider(), buildOpenAICodexProviderPlugin()]; + const wizards = new Map>(); + + for (const provider of providers) { + for (const authMethod of provider.auth ?? []) { + if (authMethod.wizard) { + wizards.set(`${provider.id}:${authMethod.id}`, authMethod.wizard); + } + } + } + + return wizards; +} + describe("OpenAI plugin manifest", () => { it("keeps removed Codex CLI import auth choice as a deprecated browser-login alias", () => { const codexBrowserLogin = manifest.providerAuthChoices?.find( @@ -37,20 +74,30 @@ describe("OpenAI plugin manifest", () => { expect(codexBrowserLogin).toMatchObject({ choiceLabel: "OpenAI Codex Browser Login", choiceHint: "Sign in with OpenAI in your browser", - groupHint: "API key + Codex auth", + groupHint: "API key or Codex sign-in", }); expect(codexDeviceCode).toMatchObject({ choiceLabel: "OpenAI Codex Device Pairing", choiceHint: "Pair in browser with a device code", - groupHint: "API key + Codex auth", + groupHint: "API key or Codex sign-in", }); expect(apiKey).toMatchObject({ choiceLabel: "OpenAI API Key", - groupHint: "API key + Codex auth", + groupHint: "API key or Codex sign-in", }); expect(choices.map((choice) => choice.choiceLabel)).not.toContain( "OpenAI Codex (ChatGPT OAuth)", ); expect(choices.map((choice) => choice.groupHint)).not.toContain("Codex OAuth + API key"); }); + + it("keeps auth choice copy aligned with provider wizard metadata", () => { + const wizards = providerWizardByKey(); + + for (const choice of manifest.providerAuthChoices ?? []) { + const key = `${choice.provider}:${choice.method}`; + + expect(wizards.get(key), key).toMatchObject(manifestComparableWizardFields(choice)); + } + }); }); diff --git a/extensions/openai/provider-contract-api.ts b/extensions/openai/provider-contract-api.ts index 69f936d928e..622ca58e607 100644 --- a/extensions/openai/provider-contract-api.ts +++ b/extensions/openai/provider-contract-api.ts @@ -1,11 +1,14 @@ import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-model-shared"; +import { + OPENAI_API_KEY_LABEL, + OPENAI_CODEX_DEVICE_PAIRING_HINT, + OPENAI_CODEX_DEVICE_PAIRING_LABEL, + OPENAI_CODEX_LOGIN_HINT, + OPENAI_CODEX_LOGIN_LABEL, + OPENAI_WIZARD_GROUP, +} from "./auth-choice-copy.js"; const noopAuth = async () => ({ profiles: [] }); -const OPENAI_WIZARD_GROUP = { - groupId: "openai", - groupLabel: "OpenAI", - groupHint: "API key + Codex auth", -} as const; export function createOpenAICodexProvider(): ProviderPlugin { return { @@ -22,13 +25,13 @@ export function createOpenAICodexProvider(): ProviderPlugin { { id: "oauth", kind: "oauth", - label: "OpenAI Codex Browser Login", - hint: "Sign in with OpenAI in your browser", + label: OPENAI_CODEX_LOGIN_LABEL, + hint: OPENAI_CODEX_LOGIN_HINT, run: noopAuth, wizard: { choiceId: "openai-codex", - choiceLabel: "OpenAI Codex Browser Login", - choiceHint: "Sign in with OpenAI in your browser", + choiceLabel: OPENAI_CODEX_LOGIN_LABEL, + choiceHint: OPENAI_CODEX_LOGIN_HINT, assistantPriority: -30, ...OPENAI_WIZARD_GROUP, }, @@ -36,13 +39,13 @@ export function createOpenAICodexProvider(): ProviderPlugin { { id: "device-code", kind: "device_code", - label: "OpenAI Codex Device Pairing", - hint: "Pair in browser with a device code", + label: OPENAI_CODEX_DEVICE_PAIRING_LABEL, + hint: OPENAI_CODEX_DEVICE_PAIRING_HINT, run: noopAuth, wizard: { choiceId: "openai-codex-device-code", - choiceLabel: "OpenAI Codex Device Pairing", - choiceHint: "Pair in browser with a device code", + choiceLabel: OPENAI_CODEX_DEVICE_PAIRING_LABEL, + choiceHint: OPENAI_CODEX_DEVICE_PAIRING_HINT, assistantPriority: -10, ...OPENAI_WIZARD_GROUP, }, @@ -62,12 +65,12 @@ export function createOpenAIProvider(): ProviderPlugin { { id: "api-key", kind: "api_key", - label: "OpenAI API Key", + label: OPENAI_API_KEY_LABEL, hint: "Use your OpenAI API key directly", run: noopAuth, wizard: { choiceId: "openai-api-key", - choiceLabel: "OpenAI API Key", + choiceLabel: OPENAI_API_KEY_LABEL, assistantPriority: -40, ...OPENAI_WIZARD_GROUP, },