diff --git a/src/config/reserved-names.ts b/src/config/reserved-names.ts index 657e5d78..25262265 100644 --- a/src/config/reserved-names.ts +++ b/src/config/reserved-names.ts @@ -9,6 +9,8 @@ export const RESERVED_PROFILE_NAMES = [ 'agy', 'qwen', 'iflow', + // Copilot API (GitHub Copilot proxy) + 'copilot', // CLI commands and special names 'default', 'config', diff --git a/src/config/unified-config-loader.ts b/src/config/unified-config-loader.ts index 04394694..72f27ceb 100644 --- a/src/config/unified-config-loader.ts +++ b/src/config/unified-config-loader.ts @@ -14,6 +14,7 @@ import { isUnifiedConfig, createEmptyUnifiedConfig, UNIFIED_CONFIG_VERSION, + DEFAULT_COPILOT_CONFIG, } from './unified-config-types'; import { isUnifiedConfigEnabled } from './feature-flags'; @@ -159,6 +160,16 @@ function mergeWithDefaults(partial: Partial): UnifiedConfig { // Legacy fields (keep for backwards compatibility during read) gemini: partial.websearch?.gemini, }, + // Copilot config - strictly opt-in, merge with defaults + copilot: { + enabled: partial.copilot?.enabled ?? DEFAULT_COPILOT_CONFIG.enabled, + auto_start: partial.copilot?.auto_start ?? DEFAULT_COPILOT_CONFIG.auto_start, + port: partial.copilot?.port ?? DEFAULT_COPILOT_CONFIG.port, + account_type: partial.copilot?.account_type ?? DEFAULT_COPILOT_CONFIG.account_type, + rate_limit: partial.copilot?.rate_limit ?? DEFAULT_COPILOT_CONFIG.rate_limit, + wait_on_limit: partial.copilot?.wait_on_limit ?? DEFAULT_COPILOT_CONFIG.wait_on_limit, + model: partial.copilot?.model ?? DEFAULT_COPILOT_CONFIG.model, + }, }; } @@ -302,6 +313,27 @@ function generateYamlWithComments(config: UnifiedConfig): string { lines.push(''); } + // Copilot section (GitHub Copilot proxy) + if (config.copilot) { + lines.push('# ----------------------------------------------------------------------------'); + lines.push('# Copilot: GitHub Copilot API proxy (via copilot-api)'); + lines.push('# Uses your existing GitHub Copilot subscription with Claude Code.'); + lines.push('#'); + lines.push('# WARNING: This uses a reverse-engineered API. Use responsibly.'); + lines.push('# Excessive automated usage may trigger GitHub abuse detection.'); + lines.push('#'); + lines.push('# Setup: npx copilot-api auth (authenticate with GitHub)'); + lines.push('# Usage: ccs copilot (switch to copilot profile)'); + lines.push('#'); + lines.push('# Models: claude-opus-4-5-20250514, claude-sonnet-4-20250514, gpt-4.1, o3'); + lines.push('# Account types: individual, business, enterprise'); + lines.push('# ----------------------------------------------------------------------------'); + lines.push( + yaml.dump({ copilot: config.copilot }, { indent: 2, lineWidth: -1, quotingType: '"' }).trim() + ); + lines.push(''); + } + return lines.join('\n'); } diff --git a/src/config/unified-config-types.ts b/src/config/unified-config-types.ts index 75cbfe31..0834ad48 100644 --- a/src/config/unified-config-types.ts +++ b/src/config/unified-config-types.ts @@ -13,8 +13,9 @@ * Unified config version. * Version 2 = YAML unified format * Version 3 = WebSearch config with model configuration for Gemini/OpenCode + * Version 4 = Copilot API integration (GitHub Copilot proxy) */ -export const UNIFIED_CONFIG_VERSION = 3; +export const UNIFIED_CONFIG_VERSION = 4; /** * Account configuration (formerly in profiles.json). @@ -148,6 +149,36 @@ export interface WebSearchProvidersConfig { opencode?: OpenCodeWebSearchConfig; } +/** + * Copilot API account type. + */ +export type CopilotAccountType = 'individual' | 'business' | 'enterprise'; + +/** + * Copilot API configuration. + * Enables GitHub Copilot subscription usage via copilot-api proxy. + * Strictly opt-in - disabled by default. + * + * WARNING: This uses a reverse-engineered API. Excessive automated usage + * may trigger GitHub's abuse-detection systems. + */ +export interface CopilotConfig { + /** Enable Copilot integration (default: false) - must be explicitly enabled */ + enabled: boolean; + /** Auto-start copilot-api daemon when using profile (default: false) */ + auto_start: boolean; + /** Port for copilot-api proxy (default: 4141) */ + port: number; + /** GitHub Copilot account type (default: individual) */ + account_type: CopilotAccountType; + /** Rate limit in seconds between requests (null = no limit) */ + rate_limit: number | null; + /** Wait instead of error when rate limit is hit (default: true) */ + wait_on_limit: boolean; + /** Selected model (default: claude-opus-4-5-20250514) */ + model: string; +} + /** * WebSearch configuration. * Uses CLI tools (Gemini CLI, Grok CLI, OpenCode) for third-party profiles. @@ -183,7 +214,7 @@ export interface WebSearchConfig { * Stored in ~/.ccs/config.yaml */ export interface UnifiedConfig { - /** Config version (2 for unified format) */ + /** Config version (4 for copilot support) */ version: number; /** Default profile name to use when none specified */ default?: string; @@ -197,6 +228,8 @@ export interface UnifiedConfig { preferences: PreferencesConfig; /** WebSearch configuration */ websearch?: WebSearchConfig; + /** Copilot API configuration (GitHub Copilot proxy) */ + copilot?: CopilotConfig; } /** @@ -211,6 +244,20 @@ export interface SecretsConfig { profiles: Record>; } +/** + * Default Copilot configuration. + * Strictly opt-in - disabled by default. + */ +export const DEFAULT_COPILOT_CONFIG: CopilotConfig = { + enabled: false, + auto_start: false, + port: 4141, + account_type: 'individual', + rate_limit: null, + wait_on_limit: true, + model: 'claude-opus-4-5-20250514', +}; + /** * Create an empty unified config with defaults. */ @@ -253,6 +300,7 @@ export function createEmptyUnifiedConfig(): UnifiedConfig { }, }, }, + copilot: { ...DEFAULT_COPILOT_CONFIG }, }; }