fix(models): restore anthropic cli auth login

This commit is contained in:
Peter Steinberger
2026-04-05 04:38:58 +09:00
parent 50ed91a589
commit f9f44b9b96
2 changed files with 87 additions and 4 deletions

View File

@@ -14,6 +14,7 @@ const mocks = vi.hoisted(() => ({
resolveAgentWorkspaceDir: vi.fn(),
resolveDefaultAgentWorkspaceDir: vi.fn(),
upsertAuthProfile: vi.fn(),
resolveOwningPluginIdsForProvider: vi.fn(),
resolvePluginProviders: vi.fn(),
createClackPrompter: vi.fn(),
loadValidConfigOrThrow: vi.fn(),
@@ -54,6 +55,10 @@ vi.mock("../../plugins/providers.runtime.js", () => ({
resolvePluginProviders: mocks.resolvePluginProviders,
}));
vi.mock("../../plugins/providers.js", () => ({
resolveOwningPluginIdsForProvider: mocks.resolveOwningPluginIdsForProvider,
}));
vi.mock("../../wizard/clack-prompter.js", () => ({
createClackPrompter: mocks.createClackPrompter,
}));
@@ -148,6 +153,7 @@ describe("modelsAuthLoginCommand", () => {
mocks.resolveAgentWorkspaceDir.mockReturnValue("/tmp/openclaw/workspace");
mocks.resolveDefaultAgentWorkspaceDir.mockReturnValue("/tmp/openclaw/workspace");
mocks.loadValidConfigOrThrow.mockImplementation(async () => currentConfig);
mocks.resolveOwningPluginIdsForProvider.mockReturnValue(undefined);
mocks.updateConfig.mockImplementation(
async (mutator: (cfg: OpenClawConfig) => OpenClawConfig) => {
lastUpdatedConfig = mutator(currentConfig);
@@ -278,6 +284,67 @@ describe("modelsAuthLoginCommand", () => {
expect(runtime.log).toHaveBeenCalledWith("Default model set to claude-cli/claude-sonnet-4-6");
});
it("loads the owning plugin for an explicit provider even in a clean config", async () => {
const runtime = createRuntime();
const runClaudeCliMigration = vi.fn().mockResolvedValue({
profiles: [],
defaultModel: "claude-cli/claude-sonnet-4-6",
configPatch: {
agents: {
defaults: {
models: {
"claude-cli/claude-sonnet-4-6": {},
},
},
},
},
});
mocks.resolveOwningPluginIdsForProvider.mockReturnValue(["anthropic"]);
mocks.resolvePluginProviders.mockImplementation(
(params: { activate?: boolean; onlyPluginIds?: string[] } | undefined) =>
params?.activate === true && params?.onlyPluginIds?.[0] === "anthropic"
? [
{
id: "anthropic",
label: "Anthropic",
auth: [
{
id: "cli",
label: "Claude CLI",
kind: "custom",
run: runClaudeCliMigration,
},
],
},
]
: [],
);
await modelsAuthLoginCommand(
{ provider: "anthropic", method: "cli", setDefault: true },
runtime,
);
expect(mocks.resolveOwningPluginIdsForProvider).toHaveBeenCalledWith({
provider: "anthropic",
config: {},
workspaceDir: "/tmp/openclaw/workspace",
env: process.env,
});
expect(mocks.resolvePluginProviders).toHaveBeenCalledWith(
expect.objectContaining({
config: {},
workspaceDir: "/tmp/openclaw/workspace",
bundledProviderAllowlistCompat: true,
bundledProviderVitestCompat: true,
onlyPluginIds: ["anthropic"],
activate: true,
}),
);
expect(runClaudeCliMigration).toHaveBeenCalledOnce();
expect(runtime.log).toHaveBeenCalledWith("Default model set to claude-cli/claude-sonnet-4-6");
});
it("clears stale auth lockouts before attempting openai-codex login", async () => {
const runtime = createRuntime();
const fakeStore = {

View File

@@ -101,7 +101,9 @@ function listProvidersWithTokenMethods(providers: ProviderPlugin[]): ProviderPlu
return providers.filter((provider) => listTokenAuthMethods(provider).length > 0);
}
async function resolveModelsAuthContext(): Promise<ResolvedModelsAuthContext> {
async function resolveModelsAuthContext(params?: {
requestedProvider?: string;
}): Promise<ResolvedModelsAuthContext> {
const config = await loadValidConfigOrThrow();
const defaultAgentId = resolveDefaultAgentId(config);
const agentDir = resolveAgentDir(config, defaultAgentId);
@@ -111,8 +113,18 @@ async function resolveModelsAuthContext(): Promise<ResolvedModelsAuthContext> {
config,
workspaceDir,
mode: "setup",
bundledProviderAllowlistCompat: true,
bundledProviderVitestCompat: true,
...(params?.requestedProvider?.trim()
? { providerRefs: [params.requestedProvider], activate: true }
: {}),
});
return { config, agentDir, workspaceDir, providers };
return {
config,
agentDir,
workspaceDir,
providers,
};
}
function resolveRequestedProviderOrThrow(
@@ -308,7 +320,9 @@ export async function modelsAuthSetupTokenCommand(
throw new Error("setup-token requires an interactive TTY.");
}
const { config, agentDir, workspaceDir, providers } = await resolveModelsAuthContext();
const { config, agentDir, workspaceDir, providers } = await resolveModelsAuthContext({
requestedProvider: opts.provider,
});
const tokenProviders = listProvidersWithTokenMethods(providers);
if (tokenProviders.length === 0) {
throw new Error(
@@ -566,7 +580,9 @@ export async function modelsAuthLoginCommand(opts: LoginOptions, runtime: Runtim
throw new Error("models auth login requires an interactive TTY.");
}
const { config, agentDir, workspaceDir, providers } = await resolveModelsAuthContext();
const { config, agentDir, workspaceDir, providers } = await resolveModelsAuthContext({
requestedProvider: opts.provider,
});
const prompter = createClackPrompter();
const authProviders = listProvidersWithAuthMethods(providers);
if (authProviders.length === 0) {