feat: add anthropic claude cli migration

This commit is contained in:
Peter Steinberger
2026-03-26 23:01:44 +00:00
parent b96fccadb9
commit ebf5bd75f4
10 changed files with 394 additions and 12 deletions

View File

@@ -0,0 +1,19 @@
import { describe, expect, it } from "vitest";
import {
formatDeprecatedNonInteractiveAuthChoiceError,
normalizeLegacyOnboardAuthChoice,
resolveDeprecatedAuthChoiceReplacement,
} from "./auth-choice-legacy.js";
describe("auth choice legacy aliases", () => {
it("maps claude-cli to the new anthropic cli choice", () => {
expect(normalizeLegacyOnboardAuthChoice("claude-cli")).toBe("anthropic-cli");
expect(resolveDeprecatedAuthChoiceReplacement("claude-cli")).toEqual({
normalized: "anthropic-cli",
message: 'Auth choice "claude-cli" is deprecated; using Anthropic Claude CLI setup instead.',
});
expect(formatDeprecatedNonInteractiveAuthChoiceError("claude-cli")).toBe(
'Auth choice "claude-cli" is deprecated.\nUse "--auth-choice anthropic-cli".',
);
});
});

View File

@@ -10,9 +10,12 @@ export const AUTH_CHOICE_LEGACY_ALIASES_FOR_CLI: ReadonlyArray<AuthChoice> = [
export function normalizeLegacyOnboardAuthChoice(
authChoice: AuthChoice | undefined,
): AuthChoice | undefined {
if (authChoice === "oauth" || authChoice === "claude-cli") {
if (authChoice === "oauth") {
return "setup-token";
}
if (authChoice === "claude-cli") {
return "anthropic-cli";
}
if (authChoice === "codex-cli") {
return "openai-codex";
}
@@ -31,8 +34,8 @@ export function resolveDeprecatedAuthChoiceReplacement(authChoice: "claude-cli"
} {
if (authChoice === "claude-cli") {
return {
normalized: "setup-token",
message: 'Auth choice "claude-cli" is deprecated; using setup-token flow instead.',
normalized: "anthropic-cli",
message: 'Auth choice "claude-cli" is deprecated; using Anthropic Claude CLI setup instead.',
};
}
return {
@@ -45,8 +48,6 @@ export function formatDeprecatedNonInteractiveAuthChoiceError(
authChoice: "claude-cli" | "codex-cli",
): string {
const replacement =
authChoice === "claude-cli"
? '"--auth-choice token" (Anthropic setup-token)'
: '"--auth-choice openai-codex"';
authChoice === "claude-cli" ? '"--auth-choice anthropic-cli"' : '"--auth-choice openai-codex"';
return [`Auth choice "${authChoice}" is deprecated.`, `Use ${replacement}.`].join("\n");
}

View File

@@ -229,6 +229,52 @@ describe("modelsAuthLoginCommand", () => {
expect(runtime.log).toHaveBeenCalledWith("Default model set to openai-codex/gpt-5.4");
});
it("supports provider-owned Claude CLI migration without writing auth profiles", 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.resolvePluginProviders.mockReturnValue([
{
id: "anthropic",
label: "Anthropic",
auth: [
{
id: "cli",
label: "Claude CLI",
kind: "custom",
run: runClaudeCliMigration,
},
],
},
]);
await modelsAuthLoginCommand(
{ provider: "anthropic", method: "cli", setDefault: true },
runtime,
);
expect(runClaudeCliMigration).toHaveBeenCalledOnce();
expect(mocks.upsertAuthProfile).not.toHaveBeenCalled();
expect(lastUpdatedConfig?.agents?.defaults?.model).toEqual({
primary: "claude-cli/claude-sonnet-4-6",
});
expect(lastUpdatedConfig?.agents?.defaults?.models).toEqual({
"claude-cli/claude-sonnet-4-6": {},
});
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

@@ -9,6 +9,7 @@ export type { AuthProfileStore, OAuthCredential } from "../agents/auth-profiles/
export { CLAUDE_CLI_PROFILE_ID, CODEX_CLI_PROFILE_ID } from "../agents/auth-profiles/constants.js";
export { ensureAuthProfileStore } from "../agents/auth-profiles/store.js";
export { listProfilesForProvider, upsertAuthProfile } from "../agents/auth-profiles/profiles.js";
export { readClaudeCliCredentialsCached } from "../agents/cli-credentials.js";
export { suggestOAuthProfileIdForLegacyDefault } from "../agents/auth-profiles/repair.js";
export {
MINIMAX_OAUTH_MARKER,

View File

@@ -510,6 +510,7 @@ describe("plugin-sdk subpath exports", () => {
expectSourceMentions("provider-auth", [
"buildOauthProviderAuthResult",
"generatePkceVerifierChallenge",
"readClaudeCliCredentialsCached",
"toFormUrlEncoded",
]);
expectSourceOmits("core", ["buildOauthProviderAuthResult"]);

View File

@@ -174,6 +174,16 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
},
providerAuthChoices: [
{
provider: "anthropic",
method: "cli",
choiceId: "anthropic-cli",
choiceLabel: "Anthropic Claude CLI",
choiceHint: "Reuse a local Claude CLI login on this host",
groupId: "anthropic",
groupLabel: "Anthropic",
groupHint: "Claude CLI + setup-token + API key",
},
{
provider: "anthropic",
method: "setup-token",
@@ -182,7 +192,7 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
choiceHint: "Run `claude setup-token` elsewhere, then paste the token here",
groupId: "anthropic",
groupLabel: "Anthropic",
groupHint: "setup-token + API key",
groupHint: "Claude CLI + setup-token + API key",
},
{
provider: "anthropic",
@@ -191,7 +201,7 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = [
choiceLabel: "Anthropic API key",
groupId: "anthropic",
groupLabel: "Anthropic",
groupHint: "setup-token + API key",
groupHint: "Claude CLI + setup-token + API key",
optionKey: "anthropicApiKey",
cliFlag: "--anthropic-api-key",
cliOption: "--anthropic-api-key <key>",