mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 06:15:03 +00:00
feat(api): add Anthropic direct API key support
Support native Anthropic API keys (sk-ant-...) via `ccs api create --preset anthropic`. Profiles use ANTHROPIC_API_KEY env var without BASE_URL, letting Claude CLI authenticate natively with x-api-key header instead of proxy Bearer token. - Add anthropic preset to provider catalog - Branch profile-writer env structure (native vs proxy) - Add validateAnthropicKey() preflight with x-api-key header - Auto-detect api.anthropic.com URL in interactive flow - Fix hasApiKey/isConfigured to recognize ANTHROPIC_API_KEY - Update delegation-validator for native mode profiles - Add Droid target API key fallback (ANTHROPIC_API_KEY) Closes #688 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
0c4d5244c8
commit
fedb4d4cde
11 files changed
+301
-20
No files matched your search
@@ -49,7 +49,7 @@ export function isApiProfileConfigured(apiName: string): boolean {
|
||||
if (!fs.existsSync(settingsPath)) return false;
|
||||
|
||||
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
||||
const token = settings?.env?.ANTHROPIC_AUTH_TOKEN || '';
|
||||
const token = settings?.env?.ANTHROPIC_AUTH_TOKEN || settings?.env?.ANTHROPIC_API_KEY || '';
|
||||
return token.length > 0 && !token.includes('YOUR_') && !token.includes('your-');
|
||||
} catch {
|
||||
return false;
|
||||
|
||||
@@ -32,6 +32,11 @@ function isOpenRouterUrl(baseUrl: string): boolean {
|
||||
return baseUrl.toLowerCase().includes('openrouter.ai');
|
||||
}
|
||||
|
||||
/** Detect Anthropic direct API profile (native auth, no proxy) */
|
||||
function isAnthropicDirect(baseUrl: string, apiKey: string): boolean {
|
||||
return apiKey.startsWith('sk-ant-') || baseUrl.includes('api.anthropic.com');
|
||||
}
|
||||
|
||||
function resolveProviderFromBaseUrl(baseUrl: string): CLIProxyProvider | null {
|
||||
if (baseUrl.trim().length === 0) {
|
||||
return null;
|
||||
@@ -76,17 +81,23 @@ function createSettingsFile(
|
||||
model: models.default,
|
||||
});
|
||||
|
||||
const isNative = isAnthropicDirect(baseUrl, apiKey);
|
||||
const settings = {
|
||||
env: {
|
||||
ANTHROPIC_BASE_URL: baseUrl,
|
||||
ANTHROPIC_AUTH_TOKEN: apiKey,
|
||||
// Native mode: ANTHROPIC_API_KEY only, no BASE_URL/AUTH_TOKEN
|
||||
// Proxy mode: ANTHROPIC_BASE_URL + AUTH_TOKEN (existing behavior)
|
||||
...(isNative
|
||||
? { ANTHROPIC_API_KEY: apiKey }
|
||||
: {
|
||||
ANTHROPIC_BASE_URL: baseUrl,
|
||||
ANTHROPIC_AUTH_TOKEN: apiKey,
|
||||
...(isOpenRouterUrl(baseUrl) && { ANTHROPIC_API_KEY: '' }),
|
||||
}),
|
||||
ANTHROPIC_MODEL: models.default,
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: models.opus,
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: models.sonnet,
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: models.haiku,
|
||||
CCS_DROID_PROVIDER: droidProvider,
|
||||
// OpenRouter requires explicitly blanking the API key to prevent conflicts
|
||||
...(isOpenRouterUrl(baseUrl) && { ANTHROPIC_API_KEY: '' }),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -151,17 +162,21 @@ function createApiProfileUnified(
|
||||
model: models.default,
|
||||
});
|
||||
|
||||
const isNative = isAnthropicDirect(baseUrl, apiKey);
|
||||
const settings = {
|
||||
env: {
|
||||
ANTHROPIC_BASE_URL: baseUrl,
|
||||
ANTHROPIC_AUTH_TOKEN: apiKey,
|
||||
...(isNative
|
||||
? { ANTHROPIC_API_KEY: apiKey }
|
||||
: {
|
||||
ANTHROPIC_BASE_URL: baseUrl,
|
||||
ANTHROPIC_AUTH_TOKEN: apiKey,
|
||||
...(isOpenRouterUrl(baseUrl) && { ANTHROPIC_API_KEY: '' }),
|
||||
}),
|
||||
ANTHROPIC_MODEL: models.default,
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: models.opus,
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: models.sonnet,
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: models.haiku,
|
||||
CCS_DROID_PROVIDER: droidProvider,
|
||||
// OpenRouter requires explicitly blanking the API key to prevent conflicts
|
||||
...(isOpenRouterUrl(baseUrl) && { ANTHROPIC_API_KEY: '' }),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user