From 0a98c2d626972cd8d30089bc9958d63c9efef18b Mon Sep 17 00:00:00 2001 From: FullerStackDev <263060202+fuller-stack-dev@users.noreply.github.com> Date: Sat, 23 May 2026 14:53:48 -0600 Subject: [PATCH] address migrate auth review comments --- docs/cli/migrate.md | 47 +++++++++++++- docs/install/migrating-hermes.md | 6 +- docs/plugins/sdk-subpaths.md | 2 +- extensions/codex/src/migration/auth.ts | 65 ++++++++++++++++--- extensions/migrate-hermes/auth.ts | 59 +++++++++++++++-- src/plugin-sdk/provider-auth.ts | 1 - .../contracts/plugin-sdk-subpaths.test.ts | 1 - 7 files changed, 157 insertions(+), 24 deletions(-) diff --git a/docs/cli/migrate.md b/docs/cli/migrate.md index 6df3460fabba..4412e85d6c1f 100644 --- a/docs/cli/migrate.md +++ b/docs/cli/migrate.md @@ -236,12 +236,53 @@ The bundled Hermes provider detects state at `~/.hermes` by default. Use `--from - Memory config defaults for OpenClaw file memory, plus archive or manual-review items for external memory providers such as Honcho. - Skills that include a `SKILL.md` file under `skills//`. - Per-skill config values from `skills.config`. -- Supported OAuth credentials from `auth.json` when interactive credential migration is accepted, or when `--include-secrets` is set. -- Supported API keys from `.env` when interactive credential migration is accepted, or when `--include-secrets` is set. +- Supported OAuth credentials from Hermes `auth.json` and OpenCode OpenAI OAuth credentials from OpenCode `auth.json` when interactive credential migration is accepted, or when `--include-secrets` is set. +- Supported API keys and tokens from Hermes `.env` and OpenCode `auth.json` when interactive credential migration is accepted, or when `--include-secrets` is set. ### Supported `.env` keys -`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`, `GOOGLE_API_KEY`, `GEMINI_API_KEY`, `GROQ_API_KEY`, `XAI_API_KEY`, `MISTRAL_API_KEY`, `DEEPSEEK_API_KEY`. +- `AI_GATEWAY_API_KEY` +- `ALIBABA_API_KEY` +- `ANTHROPIC_API_KEY` +- `ARCEEAI_API_KEY` +- `CEREBRAS_API_KEY` +- `CHUTES_API_KEY` +- `CLOUDFLARE_AI_GATEWAY_API_KEY` +- `COPILOT_GITHUB_TOKEN` +- `DASHSCOPE_API_KEY` +- `DEEPINFRA_API_KEY` +- `DEEPSEEK_API_KEY` +- `FIREWORKS_API_KEY` +- `GEMINI_API_KEY` +- `GH_TOKEN` +- `GITHUB_TOKEN` +- `GLM_API_KEY` +- `GOOGLE_API_KEY` +- `GROQ_API_KEY` +- `HF_TOKEN` +- `HUGGINGFACE_HUB_TOKEN` +- `KILOCODE_API_KEY` +- `KIMICODE_API_KEY` +- `KIMI_API_KEY` +- `MINIMAX_API_KEY` +- `MINIMAX_CODING_API_KEY` +- `MISTRAL_API_KEY` +- `MODELSTUDIO_API_KEY` +- `MOONSHOT_API_KEY` +- `NVIDIA_API_KEY` +- `OPENAI_API_KEY` +- `OPENCODE_API_KEY` +- `OPENCODE_GO_API_KEY` +- `OPENCODE_ZEN_API_KEY` +- `OPENROUTER_API_KEY` +- `QIANFAN_API_KEY` +- `QWEN_API_KEY` +- `TOGETHER_API_KEY` +- `VENICE_API_KEY` +- `XAI_API_KEY` +- `XIAOMI_API_KEY` +- `ZAI_API_KEY` +- `Z_AI_API_KEY` ### Archive-only state diff --git a/docs/install/migrating-hermes.md b/docs/install/migrating-hermes.md index d45963936c5a..93bd69199786 100644 --- a/docs/install/migrating-hermes.md +++ b/docs/install/migrating-hermes.md @@ -66,7 +66,7 @@ Imports require a fresh OpenClaw setup. If you already have local OpenClaw state Skills with a `SKILL.md` file under `skills//` are copied, along with per-skill config values from `skills.config`. - Interactive `openclaw migrate` asks before importing auth credentials, with yes selected by default. Accepted imports include supported OAuth credentials from `auth.json` and supported `.env` keys: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `OPENROUTER_API_KEY`, `GOOGLE_API_KEY`, `GEMINI_API_KEY`, `GROQ_API_KEY`, `XAI_API_KEY`, `MISTRAL_API_KEY`, `DEEPSEEK_API_KEY`. Use `--include-secrets` for non-interactive `openclaw migrate` credential import, `--no-auth-credentials` to skip it, or onboarding `--import-secrets` when importing from the onboarding wizard. + Interactive `openclaw migrate` asks before importing auth credentials, with yes selected by default. Accepted imports include supported OAuth credentials from Hermes `auth.json`, OpenCode OpenAI OAuth credentials from OpenCode `auth.json`, OpenCode and GitHub Copilot entries from OpenCode `auth.json`, and the [supported `.env` keys](/cli/migrate#supported-env-keys). Use `--include-secrets` for non-interactive `openclaw migrate` credential import, `--no-auth-credentials` to skip it, or onboarding `--import-secrets` when importing from the onboarding wizard. @@ -137,7 +137,7 @@ If a conflict surfaces mid-apply (for example, an unexpected race on a config fi Interactive `openclaw migrate` asks whether to import detected auth credentials, with yes selected by default. -- Accepting the prompt imports supported OAuth credentials from `auth.json` and supported `.env` keys. +- Accepting the prompt imports supported OAuth credentials from Hermes `auth.json`, OpenCode OpenAI OAuth credentials from OpenCode `auth.json`, OpenCode and GitHub Copilot entries from OpenCode `auth.json`, and the [supported `.env` keys](/cli/migrate#supported-env-keys). - Use `--no-auth-credentials` or choose no at the prompt to import non-secret state only. - Use `--include-secrets` when running unattended with `--yes`. - Use onboarding `--import-secrets` when importing credentials from the onboarding wizard. @@ -165,7 +165,7 @@ With `--json` and no `--yes`, apply prints the plan and does not mutate state. T Onboarding imports require a fresh setup. Either reset state and re-onboard, or use `openclaw migrate apply hermes` directly, which supports `--overwrite` and explicit backup control. - Interactive `openclaw migrate` runs import API keys only when you accept the credential prompt. Non-interactive `--yes` runs require `--include-secrets`; onboarding imports require `--import-secrets`. Only the keys listed above are recognized; other variables in `.env` are ignored. + Interactive `openclaw migrate` imports API keys only when you accept the credential prompt. Non-interactive `--yes` runs require `--include-secrets`; onboarding imports require `--import-secrets`. Only the [supported `.env` keys](/cli/migrate#supported-env-keys) are recognized; other variables in `.env` are ignored. diff --git a/docs/plugins/sdk-subpaths.md b/docs/plugins/sdk-subpaths.md index 3c05e9ff9554..226e810e4657 100644 --- a/docs/plugins/sdk-subpaths.md +++ b/docs/plugins/sdk-subpaths.md @@ -166,7 +166,7 @@ focused channel/runtime subpaths, `config-contracts`, `string-coerce-runtime`, | `plugin-sdk/provider-auth-api-key` | API-key onboarding/profile-write helpers such as `upsertApiKeyProfile` | | `plugin-sdk/provider-auth-result` | Standard OAuth auth-result builder | | `plugin-sdk/provider-env-vars` | Provider auth env-var lookup helpers | - | `plugin-sdk/provider-auth` | `createProviderApiKeyAuthMethod`, `ensureApiKeyFromOptionEnvOrPrompt`, `applyProviderAuthConfigPatch`, `upsertAuthProfile`, `upsertApiKeyProfile`, `writeOAuthCredentials`, deprecated `resolveOpenClawAgentDir` compatibility export | + | `plugin-sdk/provider-auth` | `createProviderApiKeyAuthMethod`, `ensureApiKeyFromOptionEnvOrPrompt`, `upsertAuthProfile`, `upsertApiKeyProfile`, `writeOAuthCredentials`, deprecated `resolveOpenClawAgentDir` compatibility export | | `plugin-sdk/provider-model-shared` | `ProviderReplayFamily`, `buildProviderReplayFamilyHooks`, `normalizeModelCompat`, shared replay-policy builders, provider-endpoint helpers, and shared model-id normalization helpers | | `plugin-sdk/provider-catalog-runtime` | Provider catalog augmentation runtime hook and plugin-provider registry seams for contract tests | | `plugin-sdk/provider-catalog-shared` | `findCatalogTemplate`, `buildSingleProviderApiKeyCatalog`, `buildManifestModelProviderConfig`, `supportsNativeStreamingUsageCompat`, `applyProviderNativeStreamingUsageCompat` | diff --git a/extensions/codex/src/migration/auth.ts b/extensions/codex/src/migration/auth.ts index 494f699f1558..b4c55fea9ff7 100644 --- a/extensions/codex/src/migration/auth.ts +++ b/extensions/codex/src/migration/auth.ts @@ -8,7 +8,6 @@ import { import type { MigrationItem, MigrationProviderContext } from "openclaw/plugin-sdk/plugin-entry"; import { applyAuthProfileConfig, - applyProviderAuthConfigPatch, buildApiKeyCredential, buildOauthProviderAuthResult, readCodexCliCredentialsCached, @@ -34,6 +33,10 @@ const CODEX_REASON_MISSING_AUTH_METADATA = "missing auth metadata"; const CODEX_CONFIG_PATCH_MODE_RETURN = "return"; type CodexMigrationTargets = ReturnType; +type AgentDefaultModelConfigs = NonNullable< + NonNullable["defaults"]>["models"] +>; +type AgentDefaultModelConfigEntry = AgentDefaultModelConfigs[string]; type CodexAuthCredential = | { @@ -41,6 +44,7 @@ type CodexAuthCredential = provider: typeof OPENAI_CODEX_PROVIDER_ID; profileId: string; result: ProviderAuthResult; + modelConfigs: AgentDefaultModelConfigs; } | { kind: "api_key"; @@ -170,6 +174,15 @@ async function readModelRefs(source: CodexSource): Promise { return [...refs].toSorted(); } +function readProviderAuthModelConfigs(result: ProviderAuthResult): AgentDefaultModelConfigs { + const models = result.configPatch?.agents?.defaults?.models; + if (isRecord(models)) { + return { ...models }; + } + const defaultModel = readString(result.defaultModel) ?? OPENAI_CODEX_DEFAULT_MODEL; + return { [defaultModel]: {} }; +} + async function buildCodexOAuthCredential(source: CodexSource): Promise { const credential = readCodexCliCredentialsCached({ codexHome: source.codexHome, @@ -206,7 +219,13 @@ async function buildCodexOAuthCredential(source: CodexSource): Promise, +): OpenClawConfig { + const existingModels = cfg.agents?.defaults?.models ?? {}; + const models: AgentDefaultModelConfigs = credential.result.replaceDefaultModels + ? { ...credential.modelConfigs } + : { ...existingModels }; + if (!credential.result.replaceDefaultModels) { + for (const [modelRef, modelConfig] of Object.entries(credential.modelConfigs)) { + models[modelRef] = mergeModelConfigEntry(models[modelRef], modelConfig); + } + } + return { + ...cfg, + agents: { + ...cfg.agents, + defaults: { + ...cfg.agents?.defaults, + models, + }, + }, + }; +} + function applyOAuthConfigToConfig( cfg: OpenClawConfig, credential: Extract, profileId: string, ): OpenClawConfig { - let next = cfg; - if (credential.result.configPatch) { - next = applyProviderAuthConfigPatch(next, credential.result.configPatch, { - replaceDefaultModels: credential.result.replaceDefaultModels, - }); - } + let next = applyOAuthModelConfigsToConfig(cfg, credential); const profile = credential.result.profiles[0]; if (profile) { next = applyAuthProfileConfig(next, { diff --git a/extensions/migrate-hermes/auth.ts b/extensions/migrate-hermes/auth.ts index 5c0e3fa9d021..c7eb917ad90c 100644 --- a/extensions/migrate-hermes/auth.ts +++ b/extensions/migrate-hermes/auth.ts @@ -8,11 +8,11 @@ import { } from "openclaw/plugin-sdk/migration"; import type { MigrationItem, MigrationProviderContext } from "openclaw/plugin-sdk/plugin-entry"; import { - applyProviderAuthConfigPatch, buildOauthProviderAuthResult, updateAuthProfileStoreWithLock, type AuthProfileStore, type OAuthCredential, + type OpenClawConfig, type ProviderAuthResult, } from "openclaw/plugin-sdk/provider-auth"; import { @@ -37,6 +37,11 @@ const OPENAI_CODEX_PROVIDER_ID = "openai-codex"; const OPENAI_CODEX_DEFAULT_MODEL = "openai/gpt-5.5"; const HERMES_AUTH_DISPLAY_NAME = "Hermes import"; +type AgentDefaultModelConfigs = NonNullable< + NonNullable["defaults"]>["models"] +>; +type AgentDefaultModelConfigEntry = AgentDefaultModelConfigs[string]; + type HermesCodexAuthCandidate = { access: string; accountId?: string; @@ -303,6 +308,51 @@ function buildAuthResult( }); } +function readProviderAuthModelConfigs(result: ProviderAuthResult): AgentDefaultModelConfigs { + const models = result.configPatch?.agents?.defaults?.models; + if (isRecord(models)) { + return { ...models }; + } + const defaultModel = readString(result.defaultModel) ?? OPENAI_CODEX_DEFAULT_MODEL; + return { [defaultModel]: {} }; +} + +function mergeModelConfigEntry( + existing: AgentDefaultModelConfigEntry | undefined, + patch: AgentDefaultModelConfigEntry, +): AgentDefaultModelConfigEntry { + if (existing && isRecord(existing) && isRecord(patch)) { + return { ...existing, ...patch } as AgentDefaultModelConfigEntry; + } + return existing ?? patch; +} + +function applyOAuthModelConfigsToConfig( + cfg: OpenClawConfig, + result: ProviderAuthResult, +): OpenClawConfig { + const patchModels = readProviderAuthModelConfigs(result); + const existingModels = cfg.agents?.defaults?.models ?? {}; + const models: AgentDefaultModelConfigs = result.replaceDefaultModels + ? { ...patchModels } + : { ...existingModels }; + if (!result.replaceDefaultModels) { + for (const [modelRef, modelConfig] of Object.entries(patchModels)) { + models[modelRef] = mergeModelConfigEntry(models[modelRef], modelConfig); + } + } + return { + ...cfg, + agents: { + ...cfg.agents, + defaults: { + ...cfg.agents?.defaults, + models, + }, + }, + }; +} + function authProfileDedupeKey(profile: HermesCodexAuthProfile): string { if (profile.credential.accountId) { return `${profile.credential.provider}:account:${profile.credential.accountId}`; @@ -564,12 +614,7 @@ export async function applyAuthItem( ctx, profile: configProfile, applyConfigPatch(config) { - if (!profile.result.configPatch) { - return config; - } - return applyProviderAuthConfigPatch(config, profile.result.configPatch, { - replaceDefaultModels: profile.result.replaceDefaultModels, - }); + return applyOAuthModelConfigsToConfig(config, profile.result); }, }); if (configResult === "conflict") { diff --git a/src/plugin-sdk/provider-auth.ts b/src/plugin-sdk/provider-auth.ts index 1701a0594966..c47a6e2ee790 100644 --- a/src/plugin-sdk/provider-auth.ts +++ b/src/plugin-sdk/provider-auth.ts @@ -81,7 +81,6 @@ export { type ApiKeyStorageOptions, type WriteOAuthCredentialsOptions, } from "../plugins/provider-auth-helpers.js"; -export { applyProviderAuthConfigPatch } from "../plugins/provider-auth-choice-helpers.js"; export { createProviderApiKeyAuthMethod } from "../plugins/provider-api-key-auth.js"; export { coerceSecretRef, hasConfiguredSecretInput } from "../config/types.secrets.js"; export { resolveDefaultSecretProviderAlias } from "../secrets/ref-contract.js"; diff --git a/src/plugins/contracts/plugin-sdk-subpaths.test.ts b/src/plugins/contracts/plugin-sdk-subpaths.test.ts index 6ed823f1a0a8..ec8f44c0424f 100644 --- a/src/plugins/contracts/plugin-sdk-subpaths.test.ts +++ b/src/plugins/contracts/plugin-sdk-subpaths.test.ts @@ -1206,7 +1206,6 @@ describe("plugin-sdk subpath exports", () => { expectSourceOmitsImportPattern("provider-setup", "./vllm.js"); expectSourceOmitsImportPattern("provider-setup", "./sglang.js"); expectSourceMentions("provider-auth", [ - "applyProviderAuthConfigPatch", "buildOauthProviderAuthResult", "generateHexPkceVerifierChallenge", "generatePkceVerifierChallenge",