feat: unified Claude API key and profile system with z.AI, MiniMax, OpenRouter support (#600)

* feat: add Claude API provider profiles for alternative endpoints

Add support for managing multiple Claude-compatible API endpoints
(z.AI GLM, AWS Bedrock, etc.) through provider profiles in settings.

Features:
- New ClaudeApiProfile type with base URL, API key, model mappings
- Pre-configured z.AI GLM template with correct model names
- Profile selector in Settings > Claude > API Profiles
- Clean switching between profiles and direct Anthropic API
- Immediate persistence to prevent data loss on restart

Profile support added to all execution paths:
- Agent service (chat)
- Ideation service
- Auto-mode service (feature agents, enhancements)
- Simple query service (title generation, descriptions, etc.)
- Backlog planning, commit messages, spec generation
- GitHub issue validation, suggestions

Environment variables set when profile is active:
- ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN/API_KEY
- ANTHROPIC_DEFAULT_HAIKU/SONNET/OPUS_MODEL
- API_TIMEOUT_MS, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC
This commit is contained in:
Stefan de Vogelaere
2026-01-19 20:36:58 +01:00
committed by GitHub
parent 63b8eb0991
commit d97c4b7b57
45 changed files with 2661 additions and 146 deletions

View File

@@ -161,6 +161,10 @@ export type {
EventHookHttpAction,
EventHookAction,
EventHook,
// Claude API profile types
ApiKeySource,
ClaudeApiProfile,
ClaudeApiProfileTemplate,
} from './settings.js';
export {
DEFAULT_KEYBOARD_SHORTCUTS,
@@ -175,6 +179,8 @@ export {
getThinkingTokenBudget,
// Event hook constants
EVENT_HOOK_TRIGGER_LABELS,
// Claude API profile constants
CLAUDE_API_PROFILE_TEMPLATES,
} from './settings.js';
// Model display constants

View File

@@ -2,7 +2,7 @@
* Shared types for AI model providers
*/
import type { ThinkingLevel } from './settings.js';
import type { ThinkingLevel, ClaudeApiProfile, Credentials } from './settings.js';
import type { CodexSandboxMode, CodexApprovalPolicy } from './codex.js';
/**
@@ -209,6 +209,17 @@ export interface ExecuteOptions {
type: 'json_schema';
schema: Record<string, unknown>;
};
/**
* Active Claude API profile for alternative endpoint configuration.
* When set, uses profile's settings (base URL, auth, model mappings) instead of direct Anthropic API.
* When undefined, uses direct Anthropic API (via API key or Claude Max CLI OAuth).
*/
claudeApiProfile?: ClaudeApiProfile;
/**
* Credentials for resolving 'credentials' apiKeySource in Claude API profiles.
* When a profile has apiKeySource='credentials', the Anthropic key from this object is used.
*/
credentials?: Credentials;
}
/**

View File

@@ -101,6 +101,137 @@ export function getThinkingTokenBudget(level: ThinkingLevel | undefined): number
/** ModelProvider - AI model provider for credentials and API key management */
export type ModelProvider = 'claude' | 'cursor' | 'codex' | 'opencode';
// ============================================================================
// Claude API Profiles - Configuration for Claude-compatible API endpoints
// ============================================================================
/**
* ApiKeySource - Strategy for sourcing API keys
*
* - 'inline': API key stored directly in the profile (legacy/default behavior)
* - 'env': Use ANTHROPIC_API_KEY environment variable
* - 'credentials': Use the Anthropic key from Settings → API Keys (credentials.json)
*/
export type ApiKeySource = 'inline' | 'env' | 'credentials';
/**
* ClaudeApiProfile - Configuration for a Claude-compatible API endpoint
*
* Allows using alternative providers like z.AI GLM, AWS Bedrock, etc.
*/
export interface ClaudeApiProfile {
/** Unique identifier (uuid) */
id: string;
/** Display name (e.g., "z.AI GLM", "AWS Bedrock") */
name: string;
/** ANTHROPIC_BASE_URL - custom API endpoint */
baseUrl: string;
/**
* API key sourcing strategy (default: 'inline' for backwards compatibility)
* - 'inline': Use apiKey field value
* - 'env': Use ANTHROPIC_API_KEY environment variable
* - 'credentials': Use the Anthropic key from credentials.json
*/
apiKeySource?: ApiKeySource;
/** API key value (only required when apiKeySource = 'inline' or undefined) */
apiKey?: string;
/** If true, use ANTHROPIC_AUTH_TOKEN instead of ANTHROPIC_API_KEY */
useAuthToken?: boolean;
/** API_TIMEOUT_MS override in milliseconds */
timeoutMs?: number;
/** Optional model name mappings */
modelMappings?: {
/** Maps to ANTHROPIC_DEFAULT_HAIKU_MODEL */
haiku?: string;
/** Maps to ANTHROPIC_DEFAULT_SONNET_MODEL */
sonnet?: string;
/** Maps to ANTHROPIC_DEFAULT_OPUS_MODEL */
opus?: string;
};
/** Set CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 */
disableNonessentialTraffic?: boolean;
}
/** Known provider templates for quick setup */
export interface ClaudeApiProfileTemplate {
name: string;
baseUrl: string;
/** Default API key source for this template (user chooses when creating) */
defaultApiKeySource?: ApiKeySource;
useAuthToken: boolean;
timeoutMs?: number;
modelMappings?: ClaudeApiProfile['modelMappings'];
disableNonessentialTraffic?: boolean;
description: string;
apiKeyUrl?: string;
}
/** Predefined templates for known Claude-compatible providers */
export const CLAUDE_API_PROFILE_TEMPLATES: ClaudeApiProfileTemplate[] = [
{
name: 'Direct Anthropic',
baseUrl: 'https://api.anthropic.com',
defaultApiKeySource: 'credentials',
useAuthToken: false,
description: 'Standard Anthropic API with your API key',
apiKeyUrl: 'https://console.anthropic.com/settings/keys',
},
{
name: 'OpenRouter',
baseUrl: 'https://openrouter.ai/api',
defaultApiKeySource: 'inline',
useAuthToken: true,
description: 'Access Claude and 300+ models via OpenRouter',
apiKeyUrl: 'https://openrouter.ai/keys',
},
{
name: 'z.AI GLM',
baseUrl: 'https://api.z.ai/api/anthropic',
defaultApiKeySource: 'inline',
useAuthToken: true,
timeoutMs: 3000000,
modelMappings: {
haiku: 'GLM-4.5-Air',
sonnet: 'GLM-4.7',
opus: 'GLM-4.7',
},
disableNonessentialTraffic: true,
description: '3× usage at fraction of cost via GLM Coding Plan',
apiKeyUrl: 'https://z.ai/manage-apikey/apikey-list',
},
{
name: 'MiniMax',
baseUrl: 'https://api.minimax.io/anthropic',
defaultApiKeySource: 'inline',
useAuthToken: true,
timeoutMs: 3000000,
modelMappings: {
haiku: 'MiniMax-M2.1',
sonnet: 'MiniMax-M2.1',
opus: 'MiniMax-M2.1',
},
disableNonessentialTraffic: true,
description: 'MiniMax M2.1 coding model with extended context',
apiKeyUrl: 'https://platform.minimax.io/user-center/basic-information/interface-key',
},
{
name: 'MiniMax (China)',
baseUrl: 'https://api.minimaxi.com/anthropic',
defaultApiKeySource: 'inline',
useAuthToken: true,
timeoutMs: 3000000,
modelMappings: {
haiku: 'MiniMax-M2.1',
sonnet: 'MiniMax-M2.1',
opus: 'MiniMax-M2.1',
},
disableNonessentialTraffic: true,
description: 'MiniMax M2.1 for users in China',
apiKeyUrl: 'https://platform.minimaxi.com/user-center/basic-information/interface-key',
},
// Future: Add AWS Bedrock, Google Vertex, etc.
];
// ============================================================================
// Event Hooks - Custom actions triggered by system events
// ============================================================================
@@ -658,6 +789,19 @@ export interface GlobalSettings {
* @see EventHook for configuration details
*/
eventHooks?: EventHook[];
// Claude API Profiles Configuration
/**
* Claude-compatible API endpoint profiles
* Allows using alternative providers like z.AI GLM, AWS Bedrock, etc.
*/
claudeApiProfiles?: ClaudeApiProfile[];
/**
* Active profile ID (null/undefined = use direct Anthropic API)
* When set, the corresponding profile's settings will be used for Claude API calls
*/
activeClaudeApiProfileId?: string | null;
}
/**
@@ -794,6 +938,15 @@ export interface ProjectSettings {
automodeEnabled?: boolean;
/** Maximum concurrent agents for this project (overrides global maxConcurrency) */
maxConcurrentAgents?: number;
// Claude API Profile Override (per-project)
/**
* Override the active Claude API profile for this project.
* - undefined: Use global setting (activeClaudeApiProfileId)
* - null: Explicitly use Direct Anthropic API (no profile)
* - string: Use specific profile by ID
*/
activeClaudeApiProfileId?: string | null;
}
/**
@@ -827,7 +980,7 @@ export const DEFAULT_PHASE_MODELS: PhaseModelConfig = {
};
/** Current version of the global settings schema */
export const SETTINGS_VERSION = 4;
export const SETTINGS_VERSION = 5;
/** Current version of the credentials schema */
export const CREDENTIALS_VERSION = 1;
/** Current version of the project settings schema */
@@ -913,6 +1066,8 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
skillsSources: ['user', 'project'],
enableSubagents: true,
subagentsSources: ['user', 'project'],
claudeApiProfiles: [],
activeClaudeApiProfileId: null,
};
/** Default credentials (empty strings - user must provide API keys) */