mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-17 04:17:11 +00:00
Merge pull request #178 from kaitranntt/dev
feat(cliproxy): migrate to CLIProxyAPIPlus with Kiro and GitHub Copilot OAuth
This commit is contained in:
@@ -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)
|
||||
```
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
+49
-11
@@ -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)
|
||||
----------------------------
|
||||
|
||||
|
||||
+1
-1
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -495,7 +495,7 @@ export function discoverExistingAccounts(): void {
|
||||
* Get summary of all accounts across providers
|
||||
*/
|
||||
export function getAllAccountsSummary(): Record<CLIProxyProvider, AccountInfo[]> {
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow'];
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp'];
|
||||
const summary: Record<CLIProxyProvider, AccountInfo[]> = {} as Record<
|
||||
CLIProxyProvider,
|
||||
AccountInfo[]
|
||||
|
||||
@@ -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<Record<CLIProxyProvider, number>> = {
|
||||
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<CLIProxyProvider, ProviderOAuthConfig> = {
|
||||
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<CLIProxyProvider, string[]> = {
|
||||
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<CLIProxyProvider, string[]> = {
|
||||
agy: ['antigravity'],
|
||||
qwen: ['qwen'],
|
||||
iflow: ['iflow'],
|
||||
kiro: ['kiro', 'codewhisperer'],
|
||||
ghcp: ['github-copilot', 'copilot'],
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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} <USER>@<HOST>`, '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} <USER>@<HOST>`, 'command')}`
|
||||
);
|
||||
console.log('');
|
||||
}
|
||||
}
|
||||
|
||||
// Execute OAuth process
|
||||
|
||||
@@ -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]}`);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -180,7 +180,7 @@ export function fetchJson(url: string, verbose = false): Promise<Record<string,
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
headers: {
|
||||
'User-Agent': 'CCS-CLIProxyAPI-Updater/1.0',
|
||||
'User-Agent': 'CCS-CLIProxyPlus-Updater/1.0',
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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`);
|
||||
|
||||
@@ -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<CLIProxyProvider, string> = {
|
||||
@@ -54,6 +55,8 @@ const PROVIDER_DISPLAY_NAMES: Record<CLIProxyProvider, string> = {
|
||||
agy: 'Antigravity',
|
||||
qwen: 'Qwen Code',
|
||||
iflow: 'iFlow',
|
||||
kiro: 'Kiro (AWS)',
|
||||
ghcp: 'GitHub Copilot (OAuth)',
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -102,7 +102,7 @@ function formatModelOption(model: ModelEntry): string {
|
||||
async function handleCreate(args: string[]): Promise<void> {
|
||||
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<void> {
|
||||
|
||||
// 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<void> {
|
||||
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<void> {
|
||||
}
|
||||
|
||||
async function handleInstallVersion(version: string, verbose: boolean): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
}
|
||||
|
||||
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<void> {
|
||||
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('');
|
||||
}
|
||||
|
||||
@@ -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 <provider> --auth', 'Authenticate only'],
|
||||
['ccs <provider> --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 <ver>', '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 <ver>', '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)')}`);
|
||||
|
||||
@@ -58,7 +58,7 @@ export type OAuthAccounts = Record<string, string>;
|
||||
*/
|
||||
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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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('');
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ export const OAUTH_CALLBACK_PORTS: Record<CLIProxyProvider, number | null> = {
|
||||
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<CLIProxyProvider, OAuthFlowType> = {
|
||||
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<OAuthP
|
||||
* Check OAuth ports for all providers
|
||||
*/
|
||||
export async function checkAllOAuthPorts(): Promise<OAuthPortDiagnostic[]> {
|
||||
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<OAuthPortDiagnostic[]> {
|
||||
* Check OAuth ports for providers that use Authorization Code flow only
|
||||
*/
|
||||
export async function checkAuthCodePorts(): Promise<OAuthPortDiagnostic[]> {
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy'];
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'kiro'];
|
||||
const results: OAuthPortDiagnostic[] = [];
|
||||
|
||||
for (const provider of providers) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<void> => {
|
||||
codex: 'codex',
|
||||
qwen: 'qwen',
|
||||
iflow: 'iflow',
|
||||
kiro: 'kiro',
|
||||
copilot: 'ghcp', // CLIProxyAPI returns 'copilot', we map to 'ghcp'
|
||||
};
|
||||
|
||||
// Update lastUsedAt for providers with recent activity
|
||||
|
||||
@@ -32,7 +32,7 @@ const handleStatsRequest = async (_req: Request, res: Response): Promise<void> =
|
||||
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<void> =
|
||||
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<void> => {
|
||||
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<void> => {
|
||||
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<void> =>
|
||||
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<void> =>
|
||||
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): Promise<voi
|
||||
try {
|
||||
const running = await isCliproxyRunning();
|
||||
if (!running) {
|
||||
res.status(503).json({ error: 'CLIProxyAPI not running' });
|
||||
res.status(503).json({ error: 'CLIProxy Plus not running' });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>GithubCopilot</title><path d="M19.245 5.364c1.322 1.36 1.877 3.216 2.11 5.817.622 0 1.2.135 1.592.654l.73.964c.21.278.323.61.323.955v2.62c0 .339-.173.669-.453.868C20.239 19.602 16.157 21.5 12 21.5c-4.6 0-9.205-2.583-11.547-4.258-.28-.2-.452-.53-.453-.868v-2.62c0-.345.113-.679.321-.956l.73-.963c.392-.517.974-.654 1.593-.654l.029-.297c.25-2.446.81-4.213 2.082-5.52 2.461-2.54 5.71-2.851 7.146-2.864h.198c1.436.013 4.685.323 7.146 2.864zm-7.244 4.328c-.284 0-.613.016-.962.05-.123.447-.305.85-.57 1.108-1.05 1.023-2.316 1.18-2.994 1.18-.638 0-1.306-.13-1.851-.464-.516.165-1.012.403-1.044.996a65.882 65.882 0 00-.063 2.884l-.002.48c-.002.563-.005 1.126-.013 1.69.002.326.204.63.51.765 2.482 1.102 4.83 1.657 6.99 1.657 2.156 0 4.504-.555 6.985-1.657a.854.854 0 00.51-.766c.03-1.682.006-3.372-.076-5.053-.031-.596-.528-.83-1.046-.996-.546.333-1.212.464-1.85.464-.677 0-1.942-.157-2.993-1.18-.266-.258-.447-.661-.57-1.108-.32-.032-.64-.049-.96-.05zm-2.525 4.013c.539 0 .976.426.976.95v1.753c0 .525-.437.95-.976.95a.964.964 0 01-.976-.95v-1.752c0-.525.437-.951.976-.951zm5 0c.539 0 .976.426.976.95v1.753c0 .525-.437.95-.976.95a.964.964 0 01-.976-.95v-1.752c0-.525.437-.951.976-.951zM7.635 5.087c-1.05.102-1.935.438-2.385.906-.975 1.037-.765 3.668-.21 4.224.405.394 1.17.657 1.995.657h.09c.649-.013 1.785-.176 2.73-1.11.435-.41.705-1.433.675-2.47-.03-.834-.27-1.52-.63-1.813-.39-.336-1.275-.482-2.265-.394zm6.465.394c-.36.292-.6.98-.63 1.813-.03 1.037.24 2.06.675 2.47.968.957 2.136 1.104 2.776 1.11h.044c.825 0 1.59-.263 1.995-.657.555-.556.765-3.187-.21-4.224-.45-.468-1.335-.804-2.385-.906-.99-.088-1.875.058-2.265.394zM12 7.615c-.24 0-.525.015-.84.044.03.16.045.336.06.526l-.001.159a2.94 2.94 0 01-.014.25c.225-.022.425-.027.612-.028h.366c.187 0 .387.006.612.028-.015-.146-.015-.277-.015-.409.015-.19.03-.365.06-.526a9.29 9.29 0 00-.84-.044z"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
@@ -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) {
|
||||
|
||||
@@ -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 */}
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">CLIProxy</h1>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Manage OAuth providers and configuration
|
||||
</p>
|
||||
<h1 className="text-2xl font-bold tracking-tight">CLIProxy Plus</h1>
|
||||
<p className="text-sm text-muted-foreground mt-1">CCS-level account management</p>
|
||||
</div>
|
||||
|
||||
{/* Login Buttons - Wrap on mobile */}
|
||||
|
||||
@@ -71,7 +71,7 @@ export function CliproxyStatsOverview({ className }: CliproxyStatsOverviewProps)
|
||||
Session Statistics
|
||||
</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Real-time usage metrics from CLIProxyAPI
|
||||
Real-time usage metrics from CLIProxy Plus
|
||||
</p>
|
||||
</div>
|
||||
<Badge variant="secondary" className="w-fit gap-1.5">
|
||||
|
||||
@@ -35,6 +35,8 @@ const providerLabels: Record<string, string> = {
|
||||
agy: 'Antigravity',
|
||||
qwen: 'Alibaba Qwen',
|
||||
iflow: 'iFlow',
|
||||
kiro: 'Kiro (AWS)',
|
||||
ghcp: 'GitHub Copilot (OAuth)',
|
||||
};
|
||||
|
||||
export function CliproxyTable({ data }: CliproxyTableProps) {
|
||||
|
||||
@@ -101,7 +101,9 @@ export function ModelPreferencesGrid() {
|
||||
{modelsData.totalCount} total
|
||||
</Badge>
|
||||
</CardTitle>
|
||||
<CardDescription>Models available through CLIProxyAPI, grouped by provider</CardDescription>
|
||||
<CardDescription>
|
||||
Models available through CLIProxy Plus, grouped by provider
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -18,6 +18,8 @@ const PROVIDER_IMAGES: Record<string, string> = {
|
||||
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<string, { text: string; letter: string }> = {
|
||||
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 */
|
||||
|
||||
@@ -54,7 +54,7 @@ const navGroups = [
|
||||
{
|
||||
path: '/cliproxy',
|
||||
icon: Zap,
|
||||
label: 'CLIProxy',
|
||||
label: 'CLIProxy Plus',
|
||||
isCollapsible: true,
|
||||
children: [
|
||||
{ path: '/cliproxy', label: 'Overview' },
|
||||
|
||||
@@ -147,7 +147,7 @@ export function ProxyStatusWidget() {
|
||||
isRunning ? 'bg-green-500 animate-pulse' : 'bg-muted-foreground/30'
|
||||
)}
|
||||
/>
|
||||
<span className="text-sm font-medium">CLIProxy Service</span>
|
||||
<span className="text-sm font-medium">CLIProxy Plus</span>
|
||||
{hasUpdate && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
|
||||
@@ -10,6 +10,8 @@ export const PROVIDERS: ProviderOption[] = [
|
||||
{ id: 'agy', name: 'Antigravity', description: 'Antigravity AI models' },
|
||||
{ id: 'qwen', name: 'Alibaba Qwen', description: 'Qwen Code models' },
|
||||
{ id: 'iflow', name: 'iFlow', description: 'iFlow AI models' },
|
||||
{ id: 'kiro', name: 'Kiro (AWS)', description: 'AWS CodeWhisperer models' },
|
||||
{ id: 'ghcp', name: 'GitHub Copilot (OAuth)', description: 'GitHub Copilot via OAuth' },
|
||||
];
|
||||
|
||||
export const ALL_STEPS = ['provider', 'auth', 'variant', 'success'];
|
||||
|
||||
@@ -18,6 +18,10 @@ const AUTH_ENDPOINTS: Record<string, string> = {
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -151,4 +151,119 @@ export const MODEL_CATALOGS: Record<string, ProviderCatalog> = {
|
||||
},
|
||||
],
|
||||
},
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -9,6 +9,8 @@ export const PROVIDER_ASSETS: Record<string, string> = {
|
||||
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<string, string> = {
|
||||
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<string, string> = {
|
||||
vertex: 'Vertex AI',
|
||||
iflow: 'iFlow',
|
||||
qwen: 'Qwen',
|
||||
kiro: 'Kiro (AWS)',
|
||||
ghcp: 'GitHub Copilot (OAuth)',
|
||||
};
|
||||
|
||||
// Map provider to display name
|
||||
|
||||
@@ -27,6 +27,8 @@ const PROVIDER_COLORS: Record<string, string> = {
|
||||
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
|
||||
|
||||
@@ -235,7 +235,7 @@ export function CliproxyPage() {
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Zap className="w-5 h-5 text-primary" />
|
||||
<h1 className="font-semibold">CLIProxy</h1>
|
||||
<h1 className="font-semibold">CLIProxy Plus</h1>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
@@ -81,7 +81,7 @@ export default function GlobalEnvSection() {
|
||||
<div className="p-5 space-y-6">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Environment variables injected into all non-Claude subscription profiles (gemini, codex,
|
||||
agy, copilot, etc.)
|
||||
agy, ghcp, etc.)
|
||||
</p>
|
||||
|
||||
{/* Enable/Disable Toggle */}
|
||||
|
||||
@@ -142,7 +142,7 @@ export default function ProxySection() {
|
||||
<ScrollArea className="flex-1">
|
||||
<div className="p-5 space-y-6">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Configure local or remote CLIProxyAPI connection for proxy-based profiles
|
||||
Configure local or remote CLIProxy Plus connection for proxy-based profiles
|
||||
</p>
|
||||
|
||||
{/* Mode Toggle - Card based selection */}
|
||||
@@ -166,7 +166,7 @@ export default function ProxySection() {
|
||||
<span className="font-medium">Local</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Run CLIProxyAPI binary on this machine
|
||||
Run CLIProxy Plus binary on this machine
|
||||
</p>
|
||||
</button>
|
||||
|
||||
@@ -187,7 +187,7 @@ export default function ProxySection() {
|
||||
<span className="font-medium">Remote</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Connect to a remote CLIProxyAPI server
|
||||
Connect to a remote CLIProxy Plus server
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user