From e5a1f60bb6f97dba5ccd021087941ddf733292d5 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Wed, 17 Dec 2025 21:38:33 -0500 Subject: [PATCH] feat(auth): add copilot profile detection - add 'copilot' to ProfileType union - detect copilot profile with enabled check - include copilot in listAvailableProfiles() when enabled --- src/auth/profile-detector.ts | 43 ++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/auth/profile-detector.ts b/src/auth/profile-detector.ts index aa537df7..cefa4fbb 100644 --- a/src/auth/profile-detector.ts +++ b/src/auth/profile-detector.ts @@ -14,12 +14,12 @@ import * as path from 'path'; import * as os from 'os'; import { findSimilarStrings } from '../utils/helpers'; import { Config, Settings, ProfileMetadata } from '../types'; -import { UnifiedConfig } from '../config/unified-config-types'; +import { UnifiedConfig, CopilotConfig } from '../config/unified-config-types'; import { hasUnifiedConfig, loadUnifiedConfig } from '../config/unified-config-loader'; import { getProfileSecrets } from '../config/secrets-manager'; import { isUnifiedConfigEnabled } from '../config/feature-flags'; -export type ProfileType = 'settings' | 'account' | 'cliproxy' | 'default'; +export type ProfileType = 'settings' | 'account' | 'cliproxy' | 'copilot' | 'default'; /** CLIProxy profile names (OAuth-based, zero config) */ export const CLIPROXY_PROFILES = ['gemini', 'codex', 'agy', 'qwen'] as const; @@ -35,6 +35,8 @@ export interface ProfileDetectionResult { provider?: CLIProxyProfileName; /** For unified config profiles: merged env vars (config + secrets) */ env?: Record; + /** For copilot profile: the copilot config */ + copilotConfig?: CopilotConfig; } export interface AllProfiles { @@ -182,6 +184,7 @@ class ProfileDetector { * * Priority order: * 0. Hardcoded CLIProxy profiles (gemini, codex, agy, qwen) + * 0.5. Copilot profile (if enabled in config) * 1. Unified config profiles (if config.yaml exists or CCS_UNIFIED_CONFIG=1) * 2. User-defined CLIProxy variants (config.cliproxy section) [legacy] * 3. Settings-based profiles (config.profiles section) [legacy] @@ -202,6 +205,36 @@ class ProfileDetector { }; } + // Priority 0.5: Check Copilot profile - GitHub Copilot subscription via copilot-api + if (profileName === 'copilot') { + const unifiedConfig = this.readUnifiedConfig(); + const copilotConfig = unifiedConfig?.copilot; + + if (!copilotConfig?.enabled) { + const error = new Error( + 'Copilot profile is not enabled.\n\n' + + 'To enable GitHub Copilot integration:\n' + + ' 1. Run: ccs config\n' + + ' 2. Go to "GitHub Copilot" section\n' + + ' 3. Enable the integration\n' + + ' 4. Authenticate with GitHub: npx copilot-api auth\n\n' + + 'Or manually edit ~/.ccs/config.yaml:\n' + + ' copilot:\n' + + ' enabled: true' + ) as ProfileNotFoundError; + error.profileName = profileName; + error.suggestions = []; + error.availableProfiles = this.listAvailableProfiles(); + throw error; + } + + return { + type: 'copilot', + name: 'copilot', + copilotConfig, + }; + } + // Priority 1: Try unified config if available const unifiedConfig = this.readUnifiedConfig(); if (unifiedConfig) { @@ -327,6 +360,12 @@ class ProfileDetector { // Check unified config first const unifiedConfig = this.readUnifiedConfig(); if (unifiedConfig) { + // Copilot profile (if enabled) + if (unifiedConfig.copilot?.enabled) { + lines.push('GitHub Copilot (via copilot-api):'); + lines.push(` - copilot (model: ${unifiedConfig.copilot.model})`); + } + // CLIProxy variants from unified config const variants = Object.keys(unifiedConfig.cliproxy?.variants || {}); if (variants.length > 0) {