diff --git a/README.md b/README.md index c1902b03..22653876 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,8 @@ The dashboard provides visual management for all account types: | **Claude** | Subscription | `ccs` | Default, strategic planning | | **Gemini** | OAuth | `ccs gemini` | Zero-config, fast iteration | | **Codex** | OAuth | `ccs codex` | Code generation | -| **Copilot** | OAuth | `ccs copilot` | GitHub Copilot models | +| **Copilot** | OAuth | `ccs copilot` or `ccs ghcp` | GitHub Copilot models | +| **Kiro** | OAuth | `ccs kiro` | AWS CodeWhisperer (Claude-powered) | | **Antigravity** | OAuth | `ccs agy` | Alternative routing | | **OpenRouter** | API Key | `ccs openrouter` | 300+ models, unified API | | **GLM** | API Key | `ccs glm` | Cost-optimized execution | @@ -102,7 +103,8 @@ The dashboard provides visual management for all account types: > **OAuth providers** authenticate via browser on first run. Tokens are cached in `~/.ccs/cliproxy/auth/`. **Powered by:** -- [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) - OAuth proxy for Gemini, Codex, Antigravity +- [CLIProxyAPIPlus](https://github.com/router-for-me/CLIProxyAPIPlus) - Extended OAuth proxy with Kiro ([@fuko2935](https://github.com/fuko2935), [@Ravens2121](https://github.com/Ravens2121)) and Copilot ([@em4go](https://github.com/em4go)) support +- [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) - Core OAuth proxy for Gemini, Codex, Antigravity - [copilot-api](https://github.com/ericc-ch/copilot-api) - GitHub Copilot API integration > [!TIP] @@ -116,8 +118,11 @@ The dashboard provides visual management for all account types: ```bash ccs # Default Claude session -ccs agy # Antigravity (OAuth) ccs gemini # Gemini (OAuth) +ccs codex # OpenAI Codex (OAuth) +ccs kiro # Kiro/AWS CodeWhisperer (OAuth) +ccs ghcp # GitHub Copilot (OAuth device flow) +ccs agy # Antigravity (OAuth) ccs glm # GLM (API key) ``` diff --git a/config/base-ghcp.settings.json b/config/base-ghcp.settings.json new file mode 100644 index 00000000..985e513a --- /dev/null +++ b/config/base-ghcp.settings.json @@ -0,0 +1,10 @@ +{ + "env": { + "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/copilot", + "ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed", + "ANTHROPIC_MODEL": "gpt-4o", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "gpt-4o", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-4o", + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-4o-mini" + } +} diff --git a/config/base-kiro.settings.json b/config/base-kiro.settings.json new file mode 100644 index 00000000..e84d0b18 --- /dev/null +++ b/config/base-kiro.settings.json @@ -0,0 +1,10 @@ +{ + "env": { + "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/kiro", + "ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed", + "ANTHROPIC_MODEL": "kiro", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "kiro", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "kiro", + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "kiro" + } +} diff --git a/docs/codebase-summary.md b/docs/codebase-summary.md index 1f07e61d..f8b4ad44 100644 --- a/docs/codebase-summary.md +++ b/docs/codebase-summary.md @@ -1,8 +1,8 @@ # CCS Codebase Summary -Last Updated: 2025-12-21 +Last Updated: 2025-12-22 -Comprehensive overview of the modularized CCS codebase structure following the Phase 9 modularization effort (Settings, Analytics, Auth Monitor splits + Test Infrastructure) and v7.1 Remote CLIProxy feature. +Comprehensive overview of the modularized CCS codebase structure following the Phase 9 modularization effort (Settings, Analytics, Auth Monitor splits + Test Infrastructure), v7.1 Remote CLIProxy feature, and v7.2 Kiro + GitHub Copilot (ghcp) OAuth providers. ## Repository Structure @@ -163,7 +163,7 @@ src/ | Core | `commands/`, `errors/` | CLI commands, error handling | | Auth | `auth/`, `cliproxy/auth/` | Authentication across providers | | Config | `config/`, `types/` | Configuration & type definitions | -| Providers | `cliproxy/`, `copilot/`, `glmt/` | Provider integrations | +| Providers | `cliproxy/`, `copilot/`, `glmt/` | Provider integrations (7 CLIProxy providers: gemini, codex, agy, qwen, iflow, kiro, ghcp) | | Remote Proxy | `cliproxy/remote-*.ts`, `proxy-config-resolver.ts` | Remote CLIProxy support (v7.1) | | Services | `web-server/`, `api/` | HTTP server, API services | | Utilities | `utils/`, `management/` | Helpers, diagnostics | diff --git a/docs/project-overview-pdr.md b/docs/project-overview-pdr.md index 86e36719..f1f7db05 100644 --- a/docs/project-overview-pdr.md +++ b/docs/project-overview-pdr.md @@ -1,6 +1,6 @@ # CCS Product Development Requirements (PDR) -Last Updated: 2025-12-21 +Last Updated: 2025-12-22 ## Product Overview @@ -10,7 +10,7 @@ Last Updated: 2025-12-21 **Description**: CLI wrapper enabling seamless switching between multiple Claude accounts and alternative AI providers (GLM, Gemini, Codex, OpenRouter) with a React-based dashboard for configuration management. Supports both local and remote CLIProxyAPI instances. -**Current Version**: v7.1.x (Remote CLIProxy routing, OpenRouter integration) +**Current Version**: v7.2.x (Kiro + GitHub Copilot OAuth providers) --- @@ -31,7 +31,7 @@ Developers using Claude Code face these challenges: CCS provides: 1. **Multi-Account Claude**: Isolated instances via `CLAUDE_CONFIG_DIR` -2. **OAuth Providers**: Zero-config Gemini, Codex, Antigravity, Copilot integration +2. **OAuth Providers**: Zero-config Gemini, Codex, Antigravity, Copilot, Kiro (ghcp) integration 3. **API Profiles**: GLM, Kimi, OpenRouter, any Anthropic-compatible API 4. **Visual Dashboard**: React SPA for configuration management 5. **Automatic WebSearch**: MCP fallback for third-party providers @@ -64,8 +64,8 @@ CCS provides: - Share commands, skills, agents across accounts ### FR-003: OAuth Provider Integration -- Support Gemini, Codex, Antigravity, Copilot OAuth flows -- Browser-based authentication +- Support Gemini, Codex, Antigravity, Copilot, Kiro (ghcp) OAuth flows +- Browser-based authentication (Authorization Code flow for most, Device Code for ghcp) - Token caching and refresh ### FR-004: API Profile Management @@ -210,11 +210,16 @@ CCS provides: - [x] Fallback to local when remote unreachable - [x] Protocol-based default ports (HTTPS:443, HTTP:8317) +### v7.2 Release (Complete) +- [x] Kiro (AWS) OAuth provider support via CLIProxyAPIPlus +- [x] GitHub Copilot (ghcp) OAuth provider via Device Code flow +- [x] Authorization Code flow for Kiro (port 9876) +- [x] Device Code flow for ghcp (no local port needed) + ### v8.0 Release (Planned - Q1 2026) - [ ] Multiple CLIProxyAPI instances (load balancing, failover) - [ ] Native git worktree support - [ ] Critical bug fixes (#158, #155, #124) -- [ ] Kiro auth support (#157) ### v9.0 Release (Future - Q2 2026) - [ ] Team collaboration features @@ -230,6 +235,8 @@ CCS provides: - Anthropic Claude API - Google Gemini API - GitHub Codex/Copilot API +- GitHub Copilot (ghcp - Device Code OAuth) +- AWS Kiro (Authorization Code OAuth) - Z.AI GLM API - OpenRouter API diff --git a/docs/project-roadmap.md b/docs/project-roadmap.md index 72f095eb..832a16a3 100644 --- a/docs/project-roadmap.md +++ b/docs/project-roadmap.md @@ -1,6 +1,6 @@ # CCS Project Roadmap -Last Updated: 2025-12-21 +Last Updated: 2025-12-22 Forward-looking roadmap documenting current priorities, GitHub issues, and future feature plans. @@ -22,6 +22,7 @@ All major modularization work is complete. The codebase evolved from monolithic | 8 | Auth Monitor | `monitoring/auth-monitor/` (465->8 files) | | 9 | Test Infrastructure | 99 UI tests + 539 CLI tests, 90% coverage | | 10 | Remote CLIProxy | `proxy-config-resolver.ts`, `remote-proxy-client.ts` | +| 11 | Kiro + ghcp Providers | OAuth support via CLIProxyAPIPlus (v7.2) | **Metrics Achieved**: - Files >500 lines: 12 -> 5 (-58%) @@ -62,7 +63,7 @@ All major modularization work is complete. The codebase evolved from monolithic | Issue | Title | Type | Status | |-------|-------|------|--------| | #142 | Configure with available CLIProxyAPI | enhancement | **COMPLETE** (v7.1) | -| #157 | Support for Kiro auth from CLIProxyAPIPlus | enhancement | - | +| #157 | Support for Kiro auth from CLIProxyAPIPlus | enhancement | **COMPLETE** (v7.2) | | #123 | Add More Models | enhancement | Ongoing | | #114 | OpenCode Zen Free model + Auto Rotation API Key | enhancement | - | @@ -150,7 +151,8 @@ worktrees: ### Priority 5: Authentication Enhancements - **#158**: Fix AGY OAuth flow -- **#157**: Add Kiro auth support from CLIProxyAPIPlus +- **#157**: ~~Add Kiro auth support from CLIProxyAPIPlus~~ **COMPLETE** (v7.2) +- GitHub Copilot (ghcp) Device Code flow **COMPLETE** (v7.2) --- @@ -160,6 +162,7 @@ worktrees: |-----------|--------|--------| | Modularization (Phases 1-9) | COMPLETE | - | | Remote CLIProxy Support (#142) | COMPLETE | v7.1 | +| Kiro + GitHub Copilot OAuth (#157) | COMPLETE | v7.2 | | Critical Bug Fixes (#158, #155, #124) | PLANNED | Q1 2026 | | Multiple CLIProxyAPI Instances | PLANNED | Q1 2026 | | Git Worktree Support | PLANNED | Q1 2026 | diff --git a/docs/system-architecture.md b/docs/system-architecture.md index 91589e57..32d1e091 100644 --- a/docs/system-architecture.md +++ b/docs/system-architecture.md @@ -1,6 +1,6 @@ # CCS System Architecture -Last Updated: 2025-12-21 +Last Updated: 2025-12-22 High-level architecture documentation for the CCS (Claude Code Switch) system. @@ -8,12 +8,12 @@ High-level architecture documentation for the CCS (Claude Code Switch) system. ## System Overview -CCS is a CLI wrapper that enables seamless switching between multiple Claude accounts and alternative AI providers (GLM, Gemini, Codex). It consists of two main components: +CCS is a CLI wrapper that enables seamless switching between multiple Claude accounts and alternative AI providers (GLM, Gemini, Codex, Kiro, GitHub Copilot). It consists of two main components: 1. **CLI Application** (`src/`) - Node.js TypeScript CLI 2. **Dashboard UI** (`ui/`) - React web application served by Express -CCS v7.1 adds support for both **local** and **remote** CLIProxyAPI instances. +CCS v7.2 adds Kiro (AWS) and GitHub Copilot (ghcp) OAuth providers via CLIProxyAPIPlus. ``` +===========================================================================+ @@ -84,8 +84,9 @@ CCS v7.1 adds support for both **local** and **remote** CLIProxyAPI instances. Profile Resolution | v - 1. CLIProxy Hardcoded ----+---> gemini, codex, agy + 1. CLIProxy Hardcoded ----+---> gemini, codex, agy, kiro, ghcp (OAuth-based) | Zero-config OAuth providers + | (kiro: Auth Code, ghcp: Device Code) | 2. CLIProxy Variants -----+---> config.cliproxy section (User-defined) | Custom provider configurations @@ -296,11 +297,21 @@ CCS v7.1 adds support for both **local** and **remote** CLIProxyAPI instances. | ANTHROPIC_BASE_URL = localhost:XXXX v +------------------+ - | CLIProxyAPI | Local proxy binary + | CLIProxyAPI | Local proxy binary (CLIProxyAPIPlus for kiro/ghcp) | (binary) | +------------------+ | - +---> OAuth Authentication (Gemini, Codex, AGY) + +---> OAuth Authentication + | | + | +---> Authorization Code Flow (port-based) + | | - Gemini, Codex, Antigravity, Kiro (port 9876) + | | - Opens browser for user auth + | | - Callback to localhost:PORT + | | + | +---> Device Code Flow (no port needed) + | - GitHub Copilot (ghcp) + | - User enters code at github.com/login/device + | - Polls for token completion | | | v | +------------------+ @@ -317,6 +328,8 @@ CCS v7.1 adds support for both **local** and **remote** CLIProxyAPI instances. +---> Google (Gemini) +---> GitHub (Codex) +---> Antigravity + +---> AWS Kiro (Claude-powered) + +---> GitHub Copilot (ghcp) +---> OpenAI-compatible endpoints ``` @@ -497,10 +510,10 @@ CCS v7.1 adds support for both **local** and **remote** CLIProxyAPI instances. | Authentication Flow | +===========================================================================+ - OAuth Providers (Gemini, Codex, AGY) - ----------------------------------- + OAuth Providers - Authorization Code Flow (Gemini, Codex, AGY, Kiro) + -------------------------------------------------------------------- - 1. User runs: ccs gemini + 1. User runs: ccs gemini (or ccs kiro) | v 2. Check token cache (~/.ccs/cliproxy/auth/) @@ -510,8 +523,8 @@ CCS v7.1 adds support for both **local** and **remote** CLIProxyAPI instances. +---> [No/Expired token] | v - 3. Open browser for OAuth - | + 3. Open browser for OAuth (localhost:PORT callback) + | - Kiro uses port 9876 v 4. Callback with auth code | @@ -522,6 +535,31 @@ CCS v7.1 adds support for both **local** and **remote** CLIProxyAPI instances. 6. Cache token locally + OAuth Providers - Device Code Flow (GitHub Copilot/ghcp) + -------------------------------------------------------- + + 1. User runs: ccs ghcp + | + v + 2. Check token cache (~/.ccs/cliproxy/auth/) + | + +---> [Valid token] ---> Use cached token + | + +---> [No/Expired token] + | + v + 3. Request device code from GitHub + | + v + 4. Display user code + verification URL + | "Enter code XXXX-XXXX at github.com/login/device" + v + 5. Poll for token (user completes auth in browser) + | + v + 6. Receive and cache token locally + + API Key Profiles (GLM, Kimi) ---------------------------- diff --git a/package.json b/package.json index 195157d1..26c5ce7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.2.0", + "version": "7.2.0-dev.1", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli", diff --git a/src/auth/profile-detector.ts b/src/auth/profile-detector.ts index 72955947..014d5407 100644 --- a/src/auth/profile-detector.ts +++ b/src/auth/profile-detector.ts @@ -20,7 +20,15 @@ import { loadUnifiedConfig, isUnifiedMode } from '../config/unified-config-loade 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; +export const CLIPROXY_PROFILES = [ + 'gemini', + 'codex', + 'agy', + 'qwen', + 'iflow', + 'kiro', + 'ghcp', +] as const; export type CLIProxyProfileName = (typeof CLIPROXY_PROFILES)[number]; export interface ProfileDetectionResult { diff --git a/src/cliproxy/account-manager.ts b/src/cliproxy/account-manager.ts index 23ffbb7d..71cc2d3e 100644 --- a/src/cliproxy/account-manager.ts +++ b/src/cliproxy/account-manager.ts @@ -495,7 +495,7 @@ export function discoverExistingAccounts(): void { * Get summary of all accounts across providers */ export function getAllAccountsSummary(): Record { - const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow']; + const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp']; const summary: Record = {} as Record< CLIProxyProvider, AccountInfo[] diff --git a/src/cliproxy/auth/auth-types.ts b/src/cliproxy/auth/auth-types.ts index ddabae1c..4bb06256 100644 --- a/src/cliproxy/auth/auth-types.ts +++ b/src/cliproxy/auth/auth-types.ts @@ -16,12 +16,16 @@ import { AccountInfo } from '../account-manager'; * - Codex: Authorization Code Flow with local callback server on port 1455 * - Agy: Authorization Code Flow with local callback server on port 51121 * - Qwen: Device Code Flow (polling-based, NO callback port needed) + * - Kiro: Authorization Code Flow with local callback server on port 9876 + * - GHCP: Device Code Flow (polling-based, NO callback port needed) */ export const OAUTH_CALLBACK_PORTS: Partial> = { gemini: 8085, + kiro: 9876, // codex uses 1455 // agy uses 51121 // qwen uses Device Code Flow - no callback port needed + // ghcp uses Device Code Flow - no callback port needed }; /** @@ -100,6 +104,20 @@ export const OAUTH_CONFIGS: Record = { scopes: ['phone', 'profile', 'email'], authFlag: '--iflow-login', }, + kiro: { + provider: 'kiro', + displayName: 'Kiro (AWS)', + authUrl: 'https://oidc.us-east-1.amazonaws.com', + scopes: ['codewhisperer:completions', 'codewhisperer:conversations'], + authFlag: '--kiro-login', + }, + ghcp: { + provider: 'ghcp', + displayName: 'GitHub Copilot (OAuth)', + authUrl: 'https://github.com/login/device/code', + scopes: ['copilot'], + authFlag: '--github-copilot-login', + }, }; /** @@ -113,6 +131,8 @@ export const PROVIDER_AUTH_PREFIXES: Record = { agy: ['antigravity-', 'agy-'], qwen: ['qwen-'], iflow: ['iflow-'], + kiro: ['kiro-', 'aws-', 'codewhisperer-'], + ghcp: ['github-copilot-', 'copilot-', 'gh-'], }; /** @@ -125,6 +145,8 @@ export const PROVIDER_TYPE_VALUES: Record = { agy: ['antigravity'], qwen: ['qwen'], iflow: ['iflow'], + kiro: ['kiro', 'codewhisperer'], + ghcp: ['github-copilot', 'copilot'], }; /** diff --git a/src/cliproxy/auth/oauth-handler.ts b/src/cliproxy/auth/oauth-handler.ts index b27201d6..f2bb51c2 100644 --- a/src/cliproxy/auth/oauth-handler.ts +++ b/src/cliproxy/auth/oauth-handler.ts @@ -1,12 +1,13 @@ /** * OAuth Handler for CLIProxyAPI * - * Manages OAuth authentication flow for CLIProxy providers (Gemini, Codex, Antigravity). + * Manages OAuth authentication flow for CLIProxy providers (Gemini, Codex, Antigravity, Kiro, Copilot). * CLIProxyAPI handles OAuth internally - we just need to: * 1. Check if auth exists (token files in CCS auth directory) * 2. Trigger OAuth flow by spawning binary with auth flag * 3. Auto-detect headless environments (SSH, no DISPLAY) * 4. Use --no-browser flag for headless, display OAuth URL for manual auth + * 5. Handle Device Code flows for Copilot/Qwen (no callback server) */ import * as fs from 'fs'; @@ -118,6 +119,7 @@ async function prepareBinary( * Trigger OAuth flow for provider * Auto-detects headless environment and uses --no-browser flag accordingly * Shows real-time step-by-step progress for better user feedback + * Handles both Authorization Code (callback server) and Device Code (polling) flows */ export async function triggerOAuth( provider: CLIProxyProvider, @@ -128,6 +130,7 @@ export async function triggerOAuth( const callbackPort = OAUTH_PORTS[provider]; const isCLI = !fromUI; const headless = options.headless ?? isHeadlessEnvironment(); + const isDeviceCodeFlow = callbackPort === null; // Check for existing accounts const existingAccounts = getProviderAccounts(provider); @@ -145,8 +148,8 @@ export async function triggerOAuth( } } - // Pre-flight checks - if (!(await runPreflightChecks(provider, oauthConfig))) { + // Pre-flight checks (skip for device code flows which don't need callback ports) + if (!isDeviceCodeFlow && !(await runPreflightChecks(provider, oauthConfig))) { return null; } @@ -158,7 +161,7 @@ export async function triggerOAuth( const { binaryPath, tokenDir, configPath } = prepared; - // Free callback port if needed + // Free callback port if needed (only for authorization code flows) const localCallbackPort = OAUTH_CALLBACK_PORTS[provider]; if (localCallbackPort) { const killed = killProcessOnPort(localCallbackPort, verbose); @@ -173,19 +176,25 @@ export async function triggerOAuth( args.push('--no-browser'); } - // Show callback server step - showStep(2, 4, 'progress', `Starting callback server on port ${callbackPort || 'N/A'}...`); + // Show step based on flow type + if (isDeviceCodeFlow) { + showStep(2, 4, 'progress', `Starting ${oauthConfig.displayName} Device Code flow...`); + console.log(''); + console.log(info('Device Code Flow - follow the instructions below')); + } else { + showStep(2, 4, 'progress', `Starting callback server on port ${callbackPort}...`); - // Show headless instructions - if (headless) { - console.log(''); - console.log(warn('PORT FORWARDING REQUIRED')); - console.log(` OAuth callback uses localhost:${callbackPort} which must be reachable.`); - console.log(' Run this on your LOCAL machine:'); - console.log( - ` ${color(`ssh -L ${callbackPort}:localhost:${callbackPort} @`, 'command')}` - ); - console.log(''); + // Show headless instructions (only for authorization code flows) + if (headless) { + console.log(''); + console.log(warn('PORT FORWARDING REQUIRED')); + console.log(` OAuth callback uses localhost:${callbackPort} which must be reachable.`); + console.log(' Run this on your LOCAL machine:'); + console.log( + ` ${color(`ssh -L ${callbackPort}:localhost:${callbackPort} @`, 'command')}` + ); + console.log(''); + } } // Execute OAuth process diff --git a/src/cliproxy/auth/oauth-process.ts b/src/cliproxy/auth/oauth-process.ts index 94b70d1e..f9e0026e 100644 --- a/src/cliproxy/auth/oauth-process.ts +++ b/src/cliproxy/auth/oauth-process.ts @@ -174,7 +174,7 @@ function handleTokenNotFound(provider: CLIProxyProvider, callbackPort: number | /** Handle process exit with error */ function handleProcessError(code: number | null, state: ProcessState, headless: boolean): void { console.log(''); - console.log(fail(`CLIProxyAPI auth exited with code ${code}`)); + console.log(fail(`CLIProxy Plus auth exited with code ${code}`)); if (state.stderrData && !state.urlDisplayed) { console.log(` ${state.stderrData.trim().split('\n')[0]}`); } diff --git a/src/cliproxy/auth/token-manager.ts b/src/cliproxy/auth/token-manager.ts index c1e4fafe..4eb4fa72 100644 --- a/src/cliproxy/auth/token-manager.ts +++ b/src/cliproxy/auth/token-manager.ts @@ -145,7 +145,7 @@ export function getAuthStatus(provider: CLIProxyProvider): AuthStatus { * Get auth status for all providers */ export function getAllAuthStatus(): AuthStatus[] { - const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow']; + const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp']; return providers.map(getAuthStatus); } diff --git a/src/cliproxy/binary-manager.ts b/src/cliproxy/binary-manager.ts index 70adfec1..4e23f880 100644 --- a/src/cliproxy/binary-manager.ts +++ b/src/cliproxy/binary-manager.ts @@ -25,10 +25,10 @@ import { ensureBinary, } from './binary'; -/** Default configuration */ +/** Default configuration (uses CLIProxyAPIPlus fork with Kiro + Copilot support) */ const DEFAULT_CONFIG: BinaryManagerConfig = { version: CLIPROXY_FALLBACK_VERSION, - releaseUrl: 'https://github.com/router-for-me/CLIProxyAPI/releases/download', + releaseUrl: 'https://github.com/router-for-me/CLIProxyAPIPlus/releases/download', binPath: getBinDir(), maxRetries: 3, verbose: false, @@ -110,7 +110,7 @@ export async function installCliproxyVersion(version: string, verbose = false): const manager = new BinaryManager({ version, verbose, forceVersion: true }); if (manager.isBinaryInstalled()) { if (verbose) - console.log(info(`Removing existing CLIProxyAPI v${getInstalledCliproxyVersion()}`)); + console.log(info(`Removing existing CLIProxy Plus v${getInstalledCliproxyVersion()}`)); manager.deleteBinary(); } await manager.ensureBinary(); diff --git a/src/cliproxy/binary/downloader.ts b/src/cliproxy/binary/downloader.ts index 8ef38ac0..9ab46e60 100644 --- a/src/cliproxy/binary/downloader.ts +++ b/src/cliproxy/binary/downloader.ts @@ -180,7 +180,7 @@ export function fetchJson(url: string, verbose = false): Promise { const options = { headers: { - 'User-Agent': 'CCS-CLIProxyAPI-Updater/1.0', + 'User-Agent': 'CCS-CLIProxyPlus-Updater/1.0', Accept: 'application/vnd.github.v3+json', }, }; diff --git a/src/cliproxy/binary/installer.ts b/src/cliproxy/binary/installer.ts index e640a043..864d55d0 100644 --- a/src/cliproxy/binary/installer.ts +++ b/src/cliproxy/binary/installer.ts @@ -32,7 +32,7 @@ export async function downloadAndInstall( fs.mkdirSync(config.binPath, { recursive: true }); const archivePath = path.join(config.binPath, `cliproxy-archive.${platform.extension}`); - const spinner = new ProgressIndicator(`Downloading CLIProxyAPI v${config.version}`); + const spinner = new ProgressIndicator(`Downloading CLIProxy Plus v${config.version}`); spinner.start(); try { @@ -64,7 +64,7 @@ export async function downloadAndInstall( spinner.update('Extracting binary'); await extractArchive(archivePath, config.binPath, platform.extension, verbose); - spinner.succeed('CLIProxyAPI ready'); + spinner.succeed('CLIProxy Plus ready'); fs.unlinkSync(archivePath); const binaryPath = path.join(config.binPath, getExecutableName()); @@ -74,7 +74,7 @@ export async function downloadAndInstall( } writeInstalledVersion(config.binPath, config.version); - console.log(ok(`CLIProxyAPI v${config.version} installed successfully`)); + console.log(ok(`CLIProxy Plus v${config.version} installed successfully`)); } catch (error) { spinner.fail('Installation failed'); throw error; diff --git a/src/cliproxy/binary/lifecycle.ts b/src/cliproxy/binary/lifecycle.ts index aed9ef3c..30156777 100644 --- a/src/cliproxy/binary/lifecycle.ts +++ b/src/cliproxy/binary/lifecycle.ts @@ -22,15 +22,15 @@ async function handleAutoUpdate(config: BinaryManagerConfig, verbose: boolean): if (!updateResult.hasUpdate) return; const proxyRunning = await isCliproxyRunning(CLIPROXY_DEFAULT_PORT); - const updateMsg = `CLIProxyAPI update available: v${updateResult.currentVersion} -> v${updateResult.latestVersion}`; + const updateMsg = `CLIProxy Plus update available: v${updateResult.currentVersion} -> v${updateResult.latestVersion}`; if (proxyRunning) { console.log(info(updateMsg)); console.log(info('Run "ccs cliproxy stop" then restart to apply update')); - log('Skipping update: CLIProxyAPI is currently running', verbose); + log('Skipping update: CLIProxy Plus is currently running', verbose); } else { console.log(info(updateMsg)); - console.log(info('Updating CLIProxyAPI...')); + console.log(info('Updating CLIProxy Plus...')); deleteBinary(config.binPath, verbose); config.version = updateResult.latestVersion; await downloadAndInstall(config, verbose); diff --git a/src/cliproxy/binary/types.ts b/src/cliproxy/binary/types.ts index 461100f0..133faaf4 100644 --- a/src/cliproxy/binary/types.ts +++ b/src/cliproxy/binary/types.ts @@ -24,6 +24,6 @@ export const VERSION_CACHE_DURATION_MS = 60 * 60 * 1000; /** Version pin file name - stores user's explicit version choice */ export const VERSION_PIN_FILE = '.version-pin'; -/** GitHub API URL for latest release */ +/** GitHub API URL for latest release (CLIProxyAPIPlus fork with Kiro + Copilot support) */ export const GITHUB_API_LATEST_RELEASE = - 'https://api.github.com/repos/router-for-me/CLIProxyAPI/releases/latest'; + 'https://api.github.com/repos/router-for-me/CLIProxyAPIPlus/releases/latest'; diff --git a/src/cliproxy/binary/version-checker.ts b/src/cliproxy/binary/version-checker.ts index c21398e7..6108bd82 100644 --- a/src/cliproxy/binary/version-checker.ts +++ b/src/cliproxy/binary/version-checker.ts @@ -9,10 +9,15 @@ import { UpdateCheckResult, GITHUB_API_LATEST_RELEASE } from './types'; /** * Compare semver versions (true if latest > current) + * Handles CLIProxyAPIPlus versioning: strips -0 suffix before comparison */ export function isNewerVersion(latest: string, current: string): boolean { - const latestParts = latest.split('.').map((p) => parseInt(p, 10) || 0); - const currentParts = current.split('.').map((p) => parseInt(p, 10) || 0); + // Strip -0 suffix from CLIProxyAPIPlus versions (e.g., "6.6.40-0" -> "6.6.40") + const cleanLatest = latest.replace(/-\d+$/, ''); + const cleanCurrent = current.replace(/-\d+$/, ''); + + const latestParts = cleanLatest.split('.').map((p) => parseInt(p, 10) || 0); + const currentParts = cleanCurrent.split('.').map((p) => parseInt(p, 10) || 0); // Pad arrays to same length while (latestParts.length < 3) latestParts.push(0); diff --git a/src/cliproxy/cliproxy-executor.ts b/src/cliproxy/cliproxy-executor.ts index e895d251..16e08434 100644 --- a/src/cliproxy/cliproxy-executor.ts +++ b/src/cliproxy/cliproxy-executor.ts @@ -505,12 +505,12 @@ export async function execClaudeWithCLIProxy( await waitForProxyReady(cfg.port, cfg.timeout, cfg.pollInterval); readySpinner.succeed(`CLIProxy ready on port ${cfg.port}`); } catch (error) { - readySpinner.fail('CLIProxy startup failed'); + readySpinner.fail('CLIProxy Plus startup failed'); proxy.kill('SIGTERM'); const err = error as Error; console.error(''); - console.error(fail('CLIProxy failed to start')); + console.error(fail('CLIProxy Plus failed to start')); console.error(''); console.error('Possible causes:'); console.error(` 1. Port ${cfg.port} already in use`); diff --git a/src/cliproxy/config-generator.ts b/src/cliproxy/config-generator.ts index 57022dcd..7e9f940e 100644 --- a/src/cliproxy/config-generator.ts +++ b/src/cliproxy/config-generator.ts @@ -44,8 +44,9 @@ export function getCliproxyWritablePath(): string { * v1: Initial config (port, auth-dir, api-keys only) * v2: Full-featured config with dashboard, quota mgmt, simplified key * v3: Logging disabled by default (user opt-in via ~/.ccs/config.yaml) + * v4: Added Kiro (AWS) and GitHub Copilot providers */ -export const CLIPROXY_CONFIG_VERSION = 3; +export const CLIPROXY_CONFIG_VERSION = 4; /** Provider display names (static metadata) */ const PROVIDER_DISPLAY_NAMES: Record = { @@ -54,6 +55,8 @@ const PROVIDER_DISPLAY_NAMES: Record = { agy: 'Antigravity', qwen: 'Qwen Code', iflow: 'iFlow', + kiro: 'Kiro (AWS)', + ghcp: 'GitHub Copilot (OAuth)', }; /** diff --git a/src/cliproxy/platform-detector.ts b/src/cliproxy/platform-detector.ts index b3f45984..1d8333b2 100644 --- a/src/cliproxy/platform-detector.ts +++ b/src/cliproxy/platform-detector.ts @@ -8,10 +8,11 @@ import { PlatformInfo, SupportedOS, SupportedArch, ArchiveExtension } from './types'; /** - * CLIProxyAPI fallback version (used when GitHub API unavailable) + * CLIProxyAPIPlus fallback version (used when GitHub API unavailable) * Auto-update fetches latest from GitHub; this is only a safety net + * Note: CLIProxyAPIPlus uses v6.6.X-0 suffix pattern */ -export const CLIPROXY_FALLBACK_VERSION = '6.5.53'; +export const CLIPROXY_FALLBACK_VERSION = '6.6.40-0'; /** @deprecated Use CLIPROXY_FALLBACK_VERSION instead */ export const CLIPROXY_VERSION = CLIPROXY_FALLBACK_VERSION; @@ -56,7 +57,7 @@ export function detectPlatform(version: string = CLIPROXY_FALLBACK_VERSION): Pla } const extension: ArchiveExtension = os === 'windows' ? 'zip' : 'tar.gz'; - const binaryName = `CLIProxyAPI_${version}_${os}_${arch}.${extension}`; + const binaryName = `CLIProxyAPIPlus_${version}_${os}_${arch}.${extension}`; return { os, @@ -69,11 +70,11 @@ export function detectPlatform(version: string = CLIPROXY_FALLBACK_VERSION): Pla /** * Get executable name based on platform * @returns Binary executable name (with .exe on Windows) - * Note: The actual binary inside the archive is named 'cli-proxy-api' + * Note: The actual binary inside the archive is named 'cli-proxy-api-plus' */ export function getExecutableName(): string { const platform = detectPlatform(); - return platform.os === 'windows' ? 'cli-proxy-api.exe' : 'cli-proxy-api'; + return platform.os === 'windows' ? 'cli-proxy-api-plus.exe' : 'cli-proxy-api-plus'; } /** @@ -82,7 +83,7 @@ export function getExecutableName(): string { */ export function getArchiveBinaryName(): string { const platform = detectPlatform(); - return platform.os === 'windows' ? 'cli-proxy-api.exe' : 'cli-proxy-api'; + return platform.os === 'windows' ? 'cli-proxy-api-plus.exe' : 'cli-proxy-api-plus'; } /** @@ -92,7 +93,7 @@ export function getArchiveBinaryName(): string { */ export function getDownloadUrl(version: string = CLIPROXY_FALLBACK_VERSION): string { const platform = detectPlatform(version); - const baseUrl = `https://github.com/router-for-me/CLIProxyAPI/releases/download/v${version}`; + const baseUrl = `https://github.com/router-for-me/CLIProxyAPIPlus/releases/download/v${version}`; return `${baseUrl}/${platform.binaryName}`; } @@ -102,7 +103,7 @@ export function getDownloadUrl(version: string = CLIPROXY_FALLBACK_VERSION): str * @returns Full URL to checksums.txt */ export function getChecksumsUrl(version: string = CLIPROXY_FALLBACK_VERSION): string { - return `https://github.com/router-for-me/CLIProxyAPI/releases/download/v${version}/checksums.txt`; + return `https://github.com/router-for-me/CLIProxyAPIPlus/releases/download/v${version}/checksums.txt`; } /** diff --git a/src/cliproxy/service-manager.ts b/src/cliproxy/service-manager.ts index 1e814fbe..5befc836 100644 --- a/src/cliproxy/service-manager.ts +++ b/src/cliproxy/service-manager.ts @@ -242,7 +242,7 @@ export async function ensureCliproxyService( started: false, alreadyRunning: false, port, - error: `CLIProxy failed to start within 5s on port ${port}`, + error: `CLIProxy Plus failed to start within 5s on port ${port}`, }; } diff --git a/src/cliproxy/services/variant-config-adapter.ts b/src/cliproxy/services/variant-config-adapter.ts index a4b09058..69a36ded 100644 --- a/src/cliproxy/services/variant-config-adapter.ts +++ b/src/cliproxy/services/variant-config-adapter.ts @@ -80,7 +80,7 @@ export function saveVariantUnified( if (!config.cliproxy) { config.cliproxy = { oauth_accounts: {}, - providers: ['gemini', 'codex', 'agy', 'qwen', 'iflow'], + providers: ['gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp'], variants: {}, }; } diff --git a/src/cliproxy/types.ts b/src/cliproxy/types.ts index 9fc1c1a9..b501fa71 100644 --- a/src/cliproxy/types.ts +++ b/src/cliproxy/types.ts @@ -114,8 +114,10 @@ export interface DownloadResult { * - agy: Antigravity via OAuth (short name for easy usage) * - qwen: Qwen Code via OAuth (qwen3-coder) * - iflow: iFlow via OAuth + * - kiro: Kiro (AWS CodeWhisperer) via OAuth + * - ghcp: GitHub Copilot via Device Code (OAuth through CLIProxyAPIPlus) */ -export type CLIProxyProvider = 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow'; +export type CLIProxyProvider = 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow' | 'kiro' | 'ghcp'; /** * CLIProxy config.yaml structure (minimal) diff --git a/src/commands/cliproxy-command.ts b/src/commands/cliproxy-command.ts index 38090a9b..83c3b5a5 100644 --- a/src/commands/cliproxy-command.ts +++ b/src/commands/cliproxy-command.ts @@ -102,7 +102,7 @@ function formatModelOption(model: ModelEntry): string { async function handleCreate(args: string[]): Promise { await initUI(); const parsedArgs = parseProfileArgs(args); - console.log(header('Create CLIProxy Variant')); + console.log(header('Create CLIProxy Plus Variant')); console.log(''); // Step 1: Profile name @@ -241,7 +241,7 @@ async function handleCreate(args: string[]): Promise { // Create variant console.log(''); - console.log(info('Creating CLIProxy variant...')); + console.log(info('Creating CLIProxy Plus variant...')); const result = createVariant(name, provider, model, account); if (!result.success) { @@ -423,7 +423,7 @@ async function showStatus(verbose: boolean): Promise { const status = getBinaryStatus(); console.log(''); - console.log(color('CLIProxyAPI Status', 'primary')); + console.log(color('CLIProxy Plus Status', 'primary')); console.log(''); if (status.installed) { @@ -471,13 +471,13 @@ async function showStatus(verbose: boolean): Promise { } async function handleInstallVersion(version: string, verbose: boolean): Promise { - console.log(info(`Installing CLIProxyAPI v${version}...`)); + console.log(info(`Installing CLIProxy Plus v${version}...`)); console.log(''); const result = await installVersion(version, verbose); if (!result.success) { console.error(''); - console.error(fail(`Failed to install CLIProxyAPI v${version}`)); + console.error(fail(`Failed to install CLIProxy Plus v${version}`)); console.error(` ${result.error}`); console.error(''); console.error('Possible causes:'); @@ -486,12 +486,12 @@ async function handleInstallVersion(version: string, verbose: boolean): Promise< console.error(' 3. GitHub API rate limiting'); console.error(''); console.error('Check available versions at:'); - console.error(' https://github.com/router-for-me/CLIProxyAPI/releases'); + console.error(' https://github.com/router-for-me/CLIProxyAPIPlus/releases'); process.exit(1); } console.log(''); - console.log(ok(`CLIProxyAPI v${version} installed (pinned)`)); + console.log(ok(`CLIProxy Plus v${version} installed (pinned)`)); console.log(''); console.log(dim('This version will be used until you run:')); console.log( @@ -501,7 +501,7 @@ async function handleInstallVersion(version: string, verbose: boolean): Promise< } async function handleInstallLatest(verbose: boolean): Promise { - console.log(info('Fetching latest CLIProxyAPI version...')); + console.log(info('Fetching latest CLIProxy Plus version...')); const result = await installLatest(verbose); if (!result.success) { @@ -515,7 +515,7 @@ async function handleInstallLatest(verbose: boolean): Promise { } console.log(''); - console.log(ok(`CLIProxyAPI updated to v${result.version}`)); + console.log(ok(`CLIProxy Plus updated to v${result.version}`)); console.log(dim('Auto-update is now enabled.')); console.log(''); } @@ -569,7 +569,7 @@ async function showHelp(): Promise { console.log(subheader('Notes:')); console.log(` Default fallback version: ${color(CLIPROXY_FALLBACK_VERSION, 'info')}`); console.log( - ` Releases: ${color('https://github.com/router-for-me/CLIProxyAPI/releases', 'path')}` + ` Releases: ${color('https://github.com/router-for-me/CLIProxyAPIPlus/releases', 'path')}` ); console.log(''); } diff --git a/src/commands/help-command.ts b/src/commands/help-command.ts index 4490126b..2333c729 100644 --- a/src/commands/help-command.ts +++ b/src/commands/help-command.ts @@ -150,9 +150,9 @@ Claude Code Profile & Model Switcher`.trim(); // MAJOR SECTION 3: CLI Proxy (OAuth Providers) // ═══════════════════════════════════════════════════════════════════════════ printMajorSection( - 'CLI Proxy (OAuth Providers)', + 'CLI Proxy Plus (OAuth Providers)', [ - 'Zero-config OAuth authentication via CLIProxyAPI', + 'Zero-config OAuth authentication via CLIProxy Plus', 'First run: Browser opens for authentication, then model selection', 'Settings: ~/.ccs/{provider}.settings.json (created after auth)', ], @@ -161,6 +161,8 @@ Claude Code Profile & Model Switcher`.trim(); ['ccs codex', 'OpenAI Codex (gpt-5.1-codex-max)'], ['ccs agy', 'Antigravity (Claude/Gemini models)'], ['ccs qwen', 'Qwen Code (qwen3-coder)'], + ['ccs kiro', 'Kiro (AWS CodeWhisperer Claude models)'], + ['ccs ghcp', 'GitHub Copilot (OAuth via CLIProxy Plus)'], ['', ''], // Spacer ['ccs --auth', 'Authenticate only'], ['ccs --auth --add', 'Add another account'], @@ -174,16 +176,17 @@ Claude Code Profile & Model Switcher`.trim(); ); // ═══════════════════════════════════════════════════════════════════════════ - // MAJOR SECTION 4: GitHub Copilot Integration + // MAJOR SECTION 4: GitHub Copilot Integration (copilot-api) // ═══════════════════════════════════════════════════════════════════════════ printMajorSection( - 'GitHub Copilot Integration', + 'GitHub Copilot Integration (copilot-api)', [ - 'Use your GitHub Copilot subscription with Claude Code', + 'Use your GitHub Copilot subscription with Claude Code via copilot-api', 'Requires: npm install -g copilot-api', + 'Note: For OAuth-based access, use ccs ghcp instead', ], [ - ['ccs copilot', 'Use Copilot as API backend'], + ['ccs copilot', 'Use Copilot via copilot-api daemon'], ['ccs copilot auth', 'Authenticate with GitHub'], ['ccs copilot status', 'Show integration status'], ['ccs copilot models', 'List available models'], @@ -234,10 +237,10 @@ Claude Code Profile & Model Switcher`.trim(); ]); // CLI Proxy management - printSubSection('CLI Proxy Management', [ - ['ccs cliproxy', 'Show CLIProxyAPI status and version'], - ['ccs cliproxy --help', 'Full CLIProxy management help'], - ['ccs cliproxy --install ', 'Install specific version (e.g., 6.5.53)'], + printSubSection('CLI Proxy Plus Management', [ + ['ccs cliproxy', 'Show CLIProxy Plus status and version'], + ['ccs cliproxy --help', 'Full CLIProxy Plus management help'], + ['ccs cliproxy --install ', 'Install specific version (e.g., 6.6.6)'], ['ccs cliproxy --latest', 'Update to latest version'], ]); @@ -262,7 +265,7 @@ Claude Code Profile & Model Switcher`.trim(); // CLI Proxy paths console.log(subheader('CLI Proxy:')); - console.log(` Binary: ${color('~/.ccs/cliproxy/bin/cli-proxy-api', 'path')}`); + console.log(` Binary: ${color('~/.ccs/cliproxy/bin/cli-proxy-api-plus', 'path')}`); console.log(` Config: ${color('~/.ccs/cliproxy/config.yaml', 'path')}`); console.log(` Auth: ${color('~/.ccs/cliproxy/auth/', 'path')}`); console.log(` ${dim('Port: 8317 (default)')}`); diff --git a/src/config/unified-config-types.ts b/src/config/unified-config-types.ts index f8548cec..a5e2bc4c 100644 --- a/src/config/unified-config-types.ts +++ b/src/config/unified-config-types.ts @@ -58,7 +58,7 @@ export type OAuthAccounts = Record; */ export interface CLIProxyVariantConfig { /** Base provider to use */ - provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow'; + provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow' | 'kiro' | 'ghcp'; /** Account nickname (references oauth_accounts) */ account?: string; /** Path to settings file (e.g., "~/.ccs/gemini-custom.settings.json") */ @@ -370,7 +370,7 @@ export function createEmptyUnifiedConfig(): UnifiedConfig { profiles: {}, cliproxy: { oauth_accounts: {}, - providers: ['gemini', 'codex', 'agy', 'qwen', 'iflow'], + providers: ['gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp'], variants: {}, logging: { enabled: false, diff --git a/src/management/checks/cliproxy-check.ts b/src/management/checks/cliproxy-check.ts index 2ad57d1d..a4424f30 100644 --- a/src/management/checks/cliproxy-check.ts +++ b/src/management/checks/cliproxy-check.ts @@ -1,5 +1,5 @@ /** - * CLIProxy Health Checks - Binary, config, auth, and port status + * CLIProxy Plus Health Checks - Binary, config, auth, and port status */ import * as fs from 'fs'; @@ -21,28 +21,26 @@ import { HealthCheck, IHealthChecker, createSpinner } from './types'; const ora = createSpinner(); /** - * Check CLIProxy binary installation + * Check CLIProxy Plus binary installation */ export class CLIProxyBinaryChecker implements IHealthChecker { - name = 'CLIProxy Binary'; + name = 'CLIProxy Plus Binary'; run(results: HealthCheck): void { - const spinner = ora('Checking CLIProxy binary').start(); + const spinner = ora('Checking CLIProxy Plus binary').start(); if (isCLIProxyInstalled()) { const binaryPath = getCLIProxyPath(); const installedVersion = getInstalledCliproxyVersion(); spinner.succeed(); - console.log(` ${ok('CLIProxy Binary'.padEnd(22))} v${installedVersion}`); + console.log(` ${ok('CLIProxy Plus'.padEnd(22))} v${installedVersion}`); results.addCheck('CLIProxy Binary', 'success', undefined, undefined, { status: 'OK', info: `v${installedVersion} (${binaryPath})`, }); } else { spinner.info(); - console.log( - ` ${info('CLIProxy Binary'.padEnd(22))} Not installed (downloads on first use)` - ); + console.log(` ${info('CLIProxy Plus'.padEnd(22))} Not installed (downloads on first use)`); results.addCheck( 'CLIProxy Binary', 'success', diff --git a/src/management/doctor.ts b/src/management/doctor.ts index 8667e405..b19dd4f4 100644 --- a/src/management/doctor.ts +++ b/src/management/doctor.ts @@ -66,8 +66,8 @@ class Doctor { runSymlinkChecks(this.results); console.log(''); - // Group 6: CLIProxy (OAuth profiles) - console.log(header('CLIPROXY (OAUTH PROFILES)')); + // Group 6: CLIProxy Plus (OAuth profiles) + console.log(header('CLIPROXY PLUS (OAUTH PROFILES)')); await runCLIProxyChecks(this.results); console.log(''); diff --git a/src/management/oauth-port-diagnostics.ts b/src/management/oauth-port-diagnostics.ts index 84828f8d..e3d28317 100644 --- a/src/management/oauth-port-diagnostics.ts +++ b/src/management/oauth-port-diagnostics.ts @@ -32,6 +32,8 @@ export const OAUTH_CALLBACK_PORTS: Record = { agy: 51121, qwen: null, // Device Code Flow - no callback port iflow: null, // Device Code Flow - no callback port + kiro: 9876, // Authorization Code Flow + ghcp: null, // Device Code Flow - no callback port }; /** @@ -48,6 +50,8 @@ export const OAUTH_FLOW_TYPES: Record = { agy: 'authorization_code', qwen: 'device_code', iflow: 'device_code', + kiro: 'authorization_code', + ghcp: 'device_code', }; /** @@ -134,7 +138,7 @@ export async function checkOAuthPort(provider: CLIProxyProvider): Promise { - const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow']; + const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp']; const results: OAuthPortDiagnostic[] = []; for (const provider of providers) { @@ -149,7 +153,7 @@ export async function checkAllOAuthPorts(): Promise { * Check OAuth ports for providers that use Authorization Code flow only */ export async function checkAuthCodePorts(): Promise { - const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy']; + const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'kiro']; const results: OAuthPortDiagnostic[] = []; for (const provider of providers) { diff --git a/src/utils/platform-commands.ts b/src/utils/platform-commands.ts index 98672a64..6e8ea47f 100644 --- a/src/utils/platform-commands.ts +++ b/src/utils/platform-commands.ts @@ -32,13 +32,13 @@ export function getCatCommand(filePath: string): string { } /** - * Get platform-specific command to kill CLIProxy processes + * Get platform-specific command to kill CLIProxy Plus processes */ export function getKillCLIProxyCommand(): string { if (isWindows) { - return 'taskkill /F /IM cli-proxy-api.exe'; + return 'taskkill /F /IM cli-proxy-api-plus.exe'; } - return 'pkill -f cli-proxy-api'; + return 'pkill -f cli-proxy-api-plus'; } /** @@ -81,7 +81,7 @@ export function killProcessByPid(pid: number, verbose = false): boolean { } /** - * Kill all CLIProxy processes (cross-platform) + * Kill all CLIProxy Plus processes (cross-platform) * @returns number of processes killed */ export function killAllCLIProxyProcesses(verbose = false): number { @@ -91,12 +91,12 @@ export function killAllCLIProxyProcesses(verbose = false): number { if (isWindows) { // Windows: taskkill by image name // Use /T to kill child processes too - execSync('taskkill /F /IM cli-proxy-api.exe /T 2>nul', { stdio: 'pipe' }); + execSync('taskkill /F /IM cli-proxy-api-plus.exe /T 2>nul', { stdio: 'pipe' }); killed++; } else { // Unix: pkill with pattern matching try { - execSync('pkill -9 -f cli-proxy-api', { stdio: 'pipe' }); + execSync('pkill -9 -f cli-proxy-api-plus', { stdio: 'pipe' }); killed++; } catch { // pkill returns non-zero if no processes matched - that's OK @@ -107,7 +107,7 @@ export function killAllCLIProxyProcesses(verbose = false): number { } if (verbose && killed > 0) { - console.error(`[cleanup] Killed ${killed} CLIProxy process(es)`); + console.error(`[cleanup] Killed ${killed} CLIProxy Plus process(es)`); } return killed; diff --git a/src/web-server/routes/cliproxy-auth-routes.ts b/src/web-server/routes/cliproxy-auth-routes.ts index feeff110..1abf36d2 100644 --- a/src/web-server/routes/cliproxy-auth-routes.ts +++ b/src/web-server/routes/cliproxy-auth-routes.ts @@ -28,7 +28,15 @@ import type { CLIProxyProvider } from '../../cliproxy/types'; const router = Router(); // Valid providers list -const validProviders: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow']; +const validProviders: CLIProxyProvider[] = [ + 'gemini', + 'codex', + 'agy', + 'qwen', + 'iflow', + 'kiro', + 'ghcp', +]; /** * GET /api/cliproxy/auth - Get auth status for built-in CLIProxy profiles @@ -57,6 +65,8 @@ router.get('/', async (_req: Request, res: Response): Promise => { codex: 'codex', qwen: 'qwen', iflow: 'iflow', + kiro: 'kiro', + copilot: 'ghcp', // CLIProxyAPI returns 'copilot', we map to 'ghcp' }; // Update lastUsedAt for providers with recent activity diff --git a/src/web-server/routes/cliproxy-stats-routes.ts b/src/web-server/routes/cliproxy-stats-routes.ts index 85517dff..a4ac196c 100644 --- a/src/web-server/routes/cliproxy-stats-routes.ts +++ b/src/web-server/routes/cliproxy-stats-routes.ts @@ -32,7 +32,7 @@ const handleStatsRequest = async (_req: Request, res: Response): Promise = const running = await isCliproxyRunning(); if (!running) { res.status(503).json({ - error: 'CLIProxyAPI not running', + error: 'CLIProxy Plus not running', message: 'Start a CLIProxy session (gemini, codex, agy) to collect stats', }); return; @@ -43,7 +43,7 @@ const handleStatsRequest = async (_req: Request, res: Response): Promise = if (!stats) { res.status(503).json({ error: 'Stats unavailable', - message: 'CLIProxyAPI is running but stats endpoint not responding', + message: 'CLIProxy Plus is running but stats endpoint not responding', }); return; } @@ -164,7 +164,7 @@ router.get('/models', async (_req: Request, res: Response): Promise => { const running = await isCliproxyRunning(); if (!running) { res.status(503).json({ - error: 'CLIProxyAPI not running', + error: 'CLIProxy Plus not running', message: 'Start a CLIProxy session (gemini, codex, agy) to fetch available models', }); return; @@ -175,7 +175,7 @@ router.get('/models', async (_req: Request, res: Response): Promise => { if (!modelsResponse) { res.status(503).json({ error: 'Models unavailable', - message: 'CLIProxyAPI is running but /v1/models endpoint not responding', + message: 'CLIProxy Plus is running but /v1/models endpoint not responding', }); return; } @@ -197,7 +197,7 @@ router.get('/error-logs', async (_req: Request, res: Response): Promise => const running = await isCliproxyRunning(); if (!running) { res.status(503).json({ - error: 'CLIProxyAPI not running', + error: 'CLIProxy Plus not running', message: 'Start a CLIProxy session to view error logs', }); return; @@ -207,7 +207,7 @@ router.get('/error-logs', async (_req: Request, res: Response): Promise => if (files === null) { res.status(503).json({ error: 'Error logs unavailable', - message: 'CLIProxyAPI is running but error logs endpoint not responding', + message: 'CLIProxy Plus is running but error logs endpoint not responding', }); return; } @@ -248,7 +248,7 @@ router.get('/error-logs/:name', async (req: Request, res: Response): PromiseGithubCopilot \ No newline at end of file diff --git a/ui/public/assets/providers/kiro.png b/ui/public/assets/providers/kiro.png new file mode 100644 index 00000000..919b7fd5 Binary files /dev/null and b/ui/public/assets/providers/kiro.png differ diff --git a/ui/src/components/cliproxy/cliproxy-dialog.tsx b/ui/src/components/cliproxy/cliproxy-dialog.tsx index f699305d..fa8fc3fb 100644 --- a/ui/src/components/cliproxy/cliproxy-dialog.tsx +++ b/ui/src/components/cliproxy/cliproxy-dialog.tsx @@ -14,7 +14,7 @@ import { Label } from '@/components/ui/label'; import { useCreateVariant, useCliproxyAuth } from '@/hooks/use-cliproxy'; import { usePrivacy } from '@/contexts/privacy-context'; -const providers = ['gemini', 'codex', 'agy', 'qwen', 'iflow'] as const; +const providers = ['gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp'] as const; const schema = z.object({ name: z @@ -39,6 +39,8 @@ const providerOptions = [ { value: 'agy', label: 'Antigravity' }, { value: 'qwen', label: 'Alibaba Qwen' }, { value: 'iflow', label: 'iFlow' }, + { value: 'kiro', label: 'Kiro (AWS)' }, + { value: 'ghcp', label: 'GitHub Copilot (OAuth)' }, ]; export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) { diff --git a/ui/src/components/cliproxy/cliproxy-header.tsx b/ui/src/components/cliproxy/cliproxy-header.tsx index c610c60c..a2d39178 100644 --- a/ui/src/components/cliproxy/cliproxy-header.tsx +++ b/ui/src/components/cliproxy/cliproxy-header.tsx @@ -116,6 +116,10 @@ export function CliproxyHeader({ { id: 'gemini', displayName: 'Gemini' }, { id: 'codex', displayName: 'Codex' }, { id: 'agy', displayName: 'Agy' }, + { id: 'qwen', displayName: 'Qwen' }, + { id: 'iflow', displayName: 'iFlow' }, + { id: 'kiro', displayName: 'Kiro' }, + { id: 'ghcp', displayName: 'GitHub Copilot' }, ]; const getProviderStatus = (providerId: string) => { @@ -131,10 +135,8 @@ export function CliproxyHeader({ {/* Top row: Title and Login Buttons */}
-

CLIProxy

-

- Manage OAuth providers and configuration -

+

CLIProxy Plus

+

CCS-level account management

{/* Login Buttons - Wrap on mobile */} diff --git a/ui/src/components/cliproxy/cliproxy-stats-overview.tsx b/ui/src/components/cliproxy/cliproxy-stats-overview.tsx index 0e871eb9..e3f114d2 100644 --- a/ui/src/components/cliproxy/cliproxy-stats-overview.tsx +++ b/ui/src/components/cliproxy/cliproxy-stats-overview.tsx @@ -71,7 +71,7 @@ export function CliproxyStatsOverview({ className }: CliproxyStatsOverviewProps) Session Statistics

- Real-time usage metrics from CLIProxyAPI + Real-time usage metrics from CLIProxy Plus

diff --git a/ui/src/components/cliproxy/cliproxy-table.tsx b/ui/src/components/cliproxy/cliproxy-table.tsx index b9f7e2bb..ab4c5ff2 100644 --- a/ui/src/components/cliproxy/cliproxy-table.tsx +++ b/ui/src/components/cliproxy/cliproxy-table.tsx @@ -35,6 +35,8 @@ const providerLabels: Record = { agy: 'Antigravity', qwen: 'Alibaba Qwen', iflow: 'iFlow', + kiro: 'Kiro (AWS)', + ghcp: 'GitHub Copilot (OAuth)', }; export function CliproxyTable({ data }: CliproxyTableProps) { diff --git a/ui/src/components/cliproxy/overview/model-preferences-grid.tsx b/ui/src/components/cliproxy/overview/model-preferences-grid.tsx index 294d1e9d..62630fce 100644 --- a/ui/src/components/cliproxy/overview/model-preferences-grid.tsx +++ b/ui/src/components/cliproxy/overview/model-preferences-grid.tsx @@ -101,7 +101,9 @@ export function ModelPreferencesGrid() { {modelsData.totalCount} total - Models available through CLIProxyAPI, grouped by provider + + Models available through CLIProxy Plus, grouped by provider +
diff --git a/ui/src/components/cliproxy/provider-editor/index.tsx b/ui/src/components/cliproxy/provider-editor/index.tsx index a153dbbd..ab278d5e 100644 --- a/ui/src/components/cliproxy/provider-editor/index.tsx +++ b/ui/src/components/cliproxy/provider-editor/index.tsx @@ -54,6 +54,8 @@ export function ProviderEditor({ codex: ['openai'], qwen: ['alibaba', 'qwen'], iflow: ['iflow'], + kiro: ['kiro', 'aws'], + ghcp: ['github', 'copilot'], }; const owners = ownerMap[provider.toLowerCase()] || [provider.toLowerCase()]; return modelsData.models.filter((m) => diff --git a/ui/src/components/cliproxy/provider-logo.tsx b/ui/src/components/cliproxy/provider-logo.tsx index 3c7bb486..66ad0420 100644 --- a/ui/src/components/cliproxy/provider-logo.tsx +++ b/ui/src/components/cliproxy/provider-logo.tsx @@ -18,6 +18,8 @@ const PROVIDER_IMAGES: Record = { agy: '/assets/providers/agy.png', qwen: '/assets/providers/qwen-color.svg', iflow: '/assets/providers/iflow.png', + kiro: '/assets/providers/kiro.png', + ghcp: '/assets/providers/copilot.svg', }; /** Provider color configuration (for fallback only - no background for image logos) */ @@ -28,6 +30,8 @@ const PROVIDER_CONFIG: Record = { agy: { text: 'text-violet-600', letter: 'A' }, qwen: { text: 'text-cyan-600', letter: 'Q' }, iflow: { text: 'text-indigo-600', letter: 'i' }, + kiro: { text: 'text-teal-600', letter: 'K' }, + ghcp: { text: 'text-green-600', letter: 'C' }, }; /** Size configuration */ diff --git a/ui/src/components/layout/app-sidebar.tsx b/ui/src/components/layout/app-sidebar.tsx index 6aeaac7b..bd5b489a 100644 --- a/ui/src/components/layout/app-sidebar.tsx +++ b/ui/src/components/layout/app-sidebar.tsx @@ -54,7 +54,7 @@ const navGroups = [ { path: '/cliproxy', icon: Zap, - label: 'CLIProxy', + label: 'CLIProxy Plus', isCollapsible: true, children: [ { path: '/cliproxy', label: 'Overview' }, diff --git a/ui/src/components/monitoring/proxy-status-widget.tsx b/ui/src/components/monitoring/proxy-status-widget.tsx index 6c4af3b0..f0801c16 100644 --- a/ui/src/components/monitoring/proxy-status-widget.tsx +++ b/ui/src/components/monitoring/proxy-status-widget.tsx @@ -147,7 +147,7 @@ export function ProxyStatusWidget() { isRunning ? 'bg-green-500 animate-pulse' : 'bg-muted-foreground/30' )} /> - CLIProxy Service + CLIProxy Plus {hasUpdate && ( = { gemini: '/gemini-cli-auth-url', codex: '/codex-auth-url', agy: '/antigravity-auth-url', + qwen: '/qwen-auth-url', + iflow: '/iflow-auth-url', + kiro: '/kiro-auth-url', + ghcp: '/ghcp-auth-url', }; const AUTH_TIMEOUT_MS = 300000; // 5 minutes diff --git a/ui/src/lib/api-client.ts b/ui/src/lib/api-client.ts index 840965cc..87013507 100644 --- a/ui/src/lib/api-client.ts +++ b/ui/src/lib/api-client.ts @@ -47,20 +47,20 @@ export interface UpdateProfile { export interface Variant { name: string; - provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow'; + provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow' | 'kiro' | 'ghcp'; settings: string; account?: string; } export interface CreateVariant { name: string; - provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow'; + provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow' | 'kiro' | 'ghcp'; model?: string; account?: string; } export interface UpdateVariant { - provider?: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow'; + provider?: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow' | 'kiro' | 'ghcp'; model?: string; account?: string; } @@ -69,7 +69,7 @@ export interface UpdateVariant { export interface OAuthAccount { id: string; email?: string; - provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow'; + provider: 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow' | 'kiro' | 'ghcp'; isDefault: boolean; tokenFile: string; createdAt: string; diff --git a/ui/src/lib/model-catalogs.ts b/ui/src/lib/model-catalogs.ts index 06f78c71..a4924c47 100644 --- a/ui/src/lib/model-catalogs.ts +++ b/ui/src/lib/model-catalogs.ts @@ -151,4 +151,119 @@ export const MODEL_CATALOGS: Record = { }, ], }, + kiro: { + provider: 'kiro', + displayName: 'Kiro (AWS)', + defaultModel: 'kiro-claude-sonnet-4-5', + models: [ + { + id: 'kiro-claude-opus-4-5', + name: 'Kiro Claude Opus 4.5', + description: 'Claude Opus 4.5 via Kiro (2.2x credit)', + presetMapping: { + default: 'kiro-claude-opus-4-5', + opus: 'kiro-claude-opus-4-5', + sonnet: 'kiro-claude-sonnet-4-5', + haiku: 'kiro-claude-haiku-4-5', + }, + }, + { + id: 'kiro-claude-sonnet-4-5', + name: 'Kiro Claude Sonnet 4.5', + description: 'Claude Sonnet 4.5 via Kiro (1.3x credit)', + presetMapping: { + default: 'kiro-claude-sonnet-4-5', + opus: 'kiro-claude-opus-4-5', + sonnet: 'kiro-claude-sonnet-4-5', + haiku: 'kiro-claude-haiku-4-5', + }, + }, + { + id: 'kiro-claude-sonnet-4', + name: 'Kiro Claude Sonnet 4', + description: 'Claude Sonnet 4 via Kiro (1.3x credit)', + presetMapping: { + default: 'kiro-claude-sonnet-4', + opus: 'kiro-claude-opus-4-5', + sonnet: 'kiro-claude-sonnet-4', + haiku: 'kiro-claude-haiku-4-5', + }, + }, + { + id: 'kiro-claude-haiku-4-5', + name: 'Kiro Claude Haiku 4.5', + description: 'Claude Haiku 4.5 via Kiro (0.4x credit)', + }, + ], + }, + ghcp: { + provider: 'ghcp', + displayName: 'GitHub Copilot (OAuth)', + defaultModel: 'claude-sonnet-4.5', + models: [ + { + id: 'claude-opus-4.5', + name: 'Claude Opus 4.5', + description: 'Anthropic Claude Opus 4.5 via GitHub Copilot', + presetMapping: { + default: 'claude-opus-4.5', + opus: 'claude-opus-4.5', + sonnet: 'claude-sonnet-4.5', + haiku: 'claude-haiku-4.5', + }, + }, + { + id: 'claude-sonnet-4.5', + name: 'Claude Sonnet 4.5', + description: 'Anthropic Claude Sonnet 4.5 via GitHub Copilot', + presetMapping: { + default: 'claude-sonnet-4.5', + opus: 'claude-opus-4.5', + sonnet: 'claude-sonnet-4.5', + haiku: 'claude-haiku-4.5', + }, + }, + { + id: 'claude-sonnet-4', + name: 'Claude Sonnet 4', + description: 'Anthropic Claude Sonnet 4 via GitHub Copilot', + }, + { + id: 'claude-haiku-4.5', + name: 'Claude Haiku 4.5', + description: 'Anthropic Claude Haiku 4.5 via GitHub Copilot', + }, + { + id: 'gpt-5.2', + name: 'GPT-5.2', + description: 'OpenAI GPT-5.2 via GitHub Copilot', + presetMapping: { + default: 'gpt-5.2', + opus: 'gpt-5.2', + sonnet: 'gpt-5.1', + haiku: 'gpt-5-mini', + }, + }, + { + id: 'gpt-5.1', + name: 'GPT-5.1', + description: 'OpenAI GPT-5.1 via GitHub Copilot', + }, + { + id: 'gpt-5', + name: 'GPT-5', + description: 'OpenAI GPT-5 via GitHub Copilot', + }, + { + id: 'gpt-5-mini', + name: 'GPT-5 Mini', + description: 'OpenAI GPT-5 Mini via GitHub Copilot', + }, + { + id: 'gemini-3-pro', + name: 'Gemini 3 Pro', + description: 'Google Gemini 3 Pro via GitHub Copilot', + }, + ], + }, }; diff --git a/ui/src/lib/provider-config.ts b/ui/src/lib/provider-config.ts index 299e8145..989479a2 100644 --- a/ui/src/lib/provider-config.ts +++ b/ui/src/lib/provider-config.ts @@ -9,6 +9,8 @@ export const PROVIDER_ASSETS: Record = { agy: '/assets/providers/agy.png', codex: '/assets/providers/openai.svg', qwen: '/assets/providers/qwen-color.svg', + kiro: '/assets/providers/kiro.png', + ghcp: '/assets/providers/copilot.svg', }; // Provider brand colors @@ -19,6 +21,8 @@ export const PROVIDER_COLORS: Record = { vertex: '#4285F4', iflow: '#f94144', qwen: '#6236FF', + kiro: '#4d908e', // Dark Cyan (AWS-inspired) + ghcp: '#43aa8b', // Seaweed (GitHub-inspired) }; // Provider display names @@ -29,6 +33,8 @@ const PROVIDER_NAMES: Record = { vertex: 'Vertex AI', iflow: 'iFlow', qwen: 'Qwen', + kiro: 'Kiro (AWS)', + ghcp: 'GitHub Copilot (OAuth)', }; // Map provider to display name diff --git a/ui/src/lib/utils.ts b/ui/src/lib/utils.ts index 326a4440..29bdb99d 100644 --- a/ui/src/lib/utils.ts +++ b/ui/src/lib/utils.ts @@ -27,6 +27,8 @@ const PROVIDER_COLORS: Record = { vertex: '#577590', // Blue Slate iflow: '#f94144', // Strawberry qwen: '#f9c74f', // Tuscan + kiro: '#4d908e', // Dark Cyan (AWS-inspired) + copilot: '#43aa8b', // Seaweed (GitHub-inspired) }; // Status colors (from Analytics Cost breakdown) - darker for light theme contrast diff --git a/ui/src/pages/cliproxy.tsx b/ui/src/pages/cliproxy.tsx index b9fc21a9..9a5840fa 100644 --- a/ui/src/pages/cliproxy.tsx +++ b/ui/src/pages/cliproxy.tsx @@ -235,7 +235,7 @@ export function CliproxyPage() {
-

CLIProxy

+

CLIProxy Plus

@@ -187,7 +187,7 @@ export default function ProxySection() { Remote

- Connect to a remote CLIProxyAPI server + Connect to a remote CLIProxy Plus server