fix(agents): harden host-managed claude-cli auth path (#61276)

This commit is contained in:
Vincent Koc
2026-04-05 11:02:18 +01:00
committed by GitHub
parent afca9540bf
commit 3b84884793
17 changed files with 992 additions and 39 deletions

View File

@@ -0,0 +1,74 @@
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { applyProviderAuthConfigPatch } from "./provider-auth-choice-helpers.js";
describe("applyProviderAuthConfigPatch", () => {
it("replaces patched default model maps instead of recursively merging them", () => {
const base = {
agents: {
defaults: {
model: {
primary: "anthropic/claude-sonnet-4-6",
fallbacks: ["anthropic/claude-opus-4-6", "openai/gpt-5.2"],
},
models: {
"anthropic/claude-sonnet-4-6": { alias: "Sonnet" },
"anthropic/claude-opus-4-6": { alias: "Opus" },
"openai/gpt-5.2": {},
},
},
},
};
const patch = {
agents: {
defaults: {
models: {
"claude-cli/claude-sonnet-4-6": { alias: "Sonnet" },
"claude-cli/claude-opus-4-6": { alias: "Opus" },
"openai/gpt-5.2": {},
},
},
},
};
const next = applyProviderAuthConfigPatch(base, patch);
expect(next.agents?.defaults?.models).toEqual(patch.agents.defaults.models);
expect(next.agents?.defaults?.model).toEqual(base.agents?.defaults?.model);
});
it("keeps normal recursive merges for unrelated provider auth patch fields", () => {
const base = {
agents: {
defaults: {
contextPruning: {
mode: "cache-ttl",
ttl: "30m",
},
},
},
} satisfies OpenClawConfig;
const patch = {
agents: {
defaults: {
contextPruning: {
ttl: "1h",
},
},
},
};
const next = applyProviderAuthConfigPatch(base, patch);
expect(next).toEqual({
agents: {
defaults: {
contextPruning: {
mode: "cache-ttl",
ttl: "1h",
},
},
},
});
});
});

View File

@@ -58,6 +58,33 @@ export function mergeConfigPatch<T>(base: T, patch: unknown): T {
return next as T;
}
export function applyProviderAuthConfigPatch(cfg: OpenClawConfig, patch: unknown): OpenClawConfig {
const merged = mergeConfigPatch(cfg, patch);
if (!isPlainRecord(patch)) {
return merged;
}
const patchModels = (patch.agents as { defaults?: { models?: unknown } } | undefined)?.defaults
?.models;
if (!isPlainRecord(patchModels)) {
return merged;
}
return {
...merged,
agents: {
...merged.agents,
defaults: {
...merged.agents?.defaults,
// Provider auth migrations can intentionally replace the exact allowlist.
models: patchModels as NonNullable<
NonNullable<OpenClawConfig["agents"]>["defaults"]
>["models"],
},
},
};
}
export function applyDefaultModel(cfg: OpenClawConfig, model: string): OpenClawConfig {
const models = { ...cfg.agents?.defaults?.models };
models[model] = models[model] ?? {};

View File

@@ -11,8 +11,8 @@ import type { RuntimeEnv } from "../runtime.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import { enablePluginInConfig } from "./enable.js";
import {
applyProviderAuthConfigPatch,
applyDefaultModel,
mergeConfigPatch,
pickAuthMethod,
resolveProviderMatch,
} from "./provider-auth-choice-helpers.js";
@@ -129,7 +129,7 @@ export async function runProviderPluginAuthMethod(params: {
let nextConfig = params.config;
if (result.configPatch) {
nextConfig = mergeConfigPatch(nextConfig, result.configPatch);
nextConfig = applyProviderAuthConfigPatch(nextConfig, result.configPatch);
}
for (const profile of result.profiles) {