mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
address migrate auth review comments
This commit is contained in:
committed by
Peter Steinberger
parent
17edec75e4
commit
0a98c2d626
@@ -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/<name>/`.
|
||||
- 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
|
||||
|
||||
|
||||
@@ -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/<name>/` are copied, along with per-skill config values from `skills.config`.
|
||||
</Accordion>
|
||||
<Accordion title="Auth credentials">
|
||||
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.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -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.
|
||||
</Accordion>
|
||||
<Accordion title="API keys did not import">
|
||||
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.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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<typeof resolveCodexMigrationTargets>;
|
||||
type AgentDefaultModelConfigs = NonNullable<
|
||||
NonNullable<NonNullable<OpenClawConfig["agents"]>["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<string[]> {
|
||||
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<CodexAuthCredential | null> {
|
||||
const credential = readCodexCliCredentialsCached({
|
||||
codexHome: source.codexHome,
|
||||
@@ -206,7 +219,13 @@ async function buildCodexOAuthCredential(source: CodexSource): Promise<CodexAuth
|
||||
});
|
||||
const profile = result.profiles[0];
|
||||
return profile
|
||||
? { kind: "oauth", provider: OPENAI_CODEX_PROVIDER_ID, profileId: profile.profileId, result }
|
||||
? {
|
||||
kind: "oauth",
|
||||
provider: OPENAI_CODEX_PROVIDER_ID,
|
||||
profileId: profile.profileId,
|
||||
result,
|
||||
modelConfigs: readProviderAuthModelConfigs(result),
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -351,17 +370,47 @@ function applyDefaultModelIfMissing(cfg: OpenClawConfig): OpenClawConfig {
|
||||
};
|
||||
}
|
||||
|
||||
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,
|
||||
credential: Extract<CodexAuthCredential, { kind: "oauth" }>,
|
||||
): 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<CodexAuthCredential, { kind: "oauth" }>,
|
||||
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, {
|
||||
|
||||
@@ -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<NonNullable<OpenClawConfig["agents"]>["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") {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user