Merge pull request #3987 from mastertyko/fix/3911-preserve-anthropic-api-provider

fix(cli): preserve anthropic api provider
This commit is contained in:
Jeremy McSpadden 2026-04-11 22:51:18 -05:00 committed by GitHub
commit 9dde1b9410
3 changed files with 122 additions and 2 deletions

View file

@ -18,6 +18,7 @@ import { ensureManagedTools } from './tool-bootstrap.js'
import { loadStoredEnvKeys } from './wizard.js'
import { migratePiCredentials } from './pi-migration.js'
import { validateConfiguredModel } from './startup-model-validation.js'
import { shouldMigrateAnthropicToClaudeCode } from './provider-migrations.js'
import { shouldRunOnboarding, runOnboarding } from './onboarding.js'
import chalk from 'chalk'
import { checkForUpdates } from './update-check.js'
@ -470,7 +471,11 @@ if (isPrintMode) {
// Migrate anthropic OAuth users to claude-code provider when CLI is available (#3772).
// Anthropic blocks third-party apps from using subscription quotas — routing through
// the local claude CLI binary is TOS-compliant.
if (modelRegistry.isProviderRequestReady('claude-code') && settingsManager.getDefaultProvider() === 'anthropic') {
if (shouldMigrateAnthropicToClaudeCode({
authStorage,
isClaudeCodeReady: modelRegistry.isProviderRequestReady('claude-code'),
defaultProvider: settingsManager.getDefaultProvider(),
})) {
const currentModelId = settingsManager.getDefaultModel()
if (currentModelId) {
const ccModel = modelRegistry.find('claude-code', currentModelId)
@ -662,7 +667,11 @@ markStartup('createAgentSession')
// Migrate anthropic OAuth users to claude-code provider when CLI is available (#3772).
// Anthropic blocks third-party apps from using subscription quotas — routing through
// the local claude CLI binary is TOS-compliant.
if (modelRegistry.isProviderRequestReady('claude-code') && settingsManager.getDefaultProvider() === 'anthropic') {
if (shouldMigrateAnthropicToClaudeCode({
authStorage,
isClaudeCodeReady: modelRegistry.isProviderRequestReady('claude-code'),
defaultProvider: settingsManager.getDefaultProvider(),
})) {
const currentModelId = settingsManager.getDefaultModel()
if (currentModelId) {
const ccModel = modelRegistry.find('claude-code', currentModelId)

View file

@ -0,0 +1,34 @@
import type { AuthStorage } from "@gsd/pi-coding-agent"
type AnthropicMigrationDeps = {
authStorage: Pick<AuthStorage, "getCredentialsForProvider">
isClaudeCodeReady: boolean
defaultProvider: string | undefined
env?: NodeJS.ProcessEnv
}
export function hasDirectAnthropicApiKey(
authStorage: Pick<AuthStorage, "getCredentialsForProvider">,
env: NodeJS.ProcessEnv = process.env,
): boolean {
if ((env.ANTHROPIC_API_KEY ?? "").trim()) {
return true
}
return authStorage.getCredentialsForProvider("anthropic").some((credential: any) =>
credential?.type === "api_key" && typeof credential?.key === "string" && credential.key.trim().length > 0,
)
}
export function shouldMigrateAnthropicToClaudeCode({
authStorage,
isClaudeCodeReady,
defaultProvider,
env = process.env,
}: AnthropicMigrationDeps): boolean {
if (!isClaudeCodeReady || defaultProvider !== "anthropic") {
return false
}
return !hasDirectAnthropicApiKey(authStorage, env)
}

View file

@ -0,0 +1,77 @@
import test from "node:test"
import assert from "node:assert/strict"
import { hasDirectAnthropicApiKey, shouldMigrateAnthropicToClaudeCode } from "../provider-migrations.ts"
function makeAuthStorage(credentials: unknown[]) {
return {
getCredentialsForProvider(provider: string) {
return provider === "anthropic" ? credentials : []
},
}
}
test("hasDirectAnthropicApiKey detects non-empty auth storage keys", () => {
assert.equal(
hasDirectAnthropicApiKey(
makeAuthStorage([{ type: "api_key", key: "sk-ant-test" }]) as any,
{} as NodeJS.ProcessEnv,
),
true,
)
})
test("hasDirectAnthropicApiKey ignores empty placeholder keys", () => {
assert.equal(
hasDirectAnthropicApiKey(
makeAuthStorage([{ type: "api_key", key: "" }]) as any,
{} as NodeJS.ProcessEnv,
),
false,
)
})
test("hasDirectAnthropicApiKey detects ANTHROPIC_API_KEY env fallback", () => {
assert.equal(
hasDirectAnthropicApiKey(
makeAuthStorage([]) as any,
{ ANTHROPIC_API_KEY: "sk-ant-env" } as NodeJS.ProcessEnv,
),
true,
)
})
test("shouldMigrateAnthropicToClaudeCode blocks migration for direct-key users", () => {
assert.equal(
shouldMigrateAnthropicToClaudeCode({
authStorage: makeAuthStorage([{ type: "api_key", key: "sk-ant-test" }]) as any,
isClaudeCodeReady: true,
defaultProvider: "anthropic",
env: {} as NodeJS.ProcessEnv,
}),
false,
)
})
test("shouldMigrateAnthropicToClaudeCode allows OAuth-only anthropic users", () => {
assert.equal(
shouldMigrateAnthropicToClaudeCode({
authStorage: makeAuthStorage([{ type: "oauth" }]) as any,
isClaudeCodeReady: true,
defaultProvider: "anthropic",
env: {} as NodeJS.ProcessEnv,
}),
true,
)
})
test("shouldMigrateAnthropicToClaudeCode stays off for other providers", () => {
assert.equal(
shouldMigrateAnthropicToClaudeCode({
authStorage: makeAuthStorage([{ type: "oauth" }]) as any,
isClaudeCodeReady: true,
defaultProvider: "openai",
env: {} as NodeJS.ProcessEnv,
}),
false,
)
})