mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-08 06:17:06 +00:00
feat(cliproxy): refactor config loading and enhance base settings support
- Introduce `BaseConfigLoader` for centralized configuration management - Add `base-agy.settings.json` configuration - Refactor `ConfigGenerator` to utilize new loading patterns - Update help command with improved documentation - Clean up lint violations file - Enhance postinstall script
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/agy",
|
||||
"ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed",
|
||||
"ANTHROPIC_MODEL": "gemini-3-pro-preview",
|
||||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "gemini-3-pro-preview",
|
||||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "gemini-3-pro-preview",
|
||||
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "gemini-2.5-flash"
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/gemini",
|
||||
"ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed",
|
||||
"ANTHROPIC_MODEL": "gemini-2.5-pro",
|
||||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "gemini-3-pro-preview",
|
||||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "gemini-2.5-pro",
|
||||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "gemini-2.5-pro",
|
||||
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "gemini-2.5-flash"
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
$ eslint src/
|
||||
+12
-1
@@ -121,6 +121,8 @@ function createConfigFiles() {
|
||||
// This gives users control over when to modify their Claude configuration
|
||||
|
||||
// Create config.json if missing
|
||||
// NOTE: gemini/codex profiles NOT included - they are added on-demand when user
|
||||
// runs `ccs gemini` or `ccs codex` for first time (requires OAuth auth first)
|
||||
const configPath = path.join(ccsDir, 'config.json');
|
||||
if (!fs.existsSync(configPath)) {
|
||||
const config = {
|
||||
@@ -145,12 +147,17 @@ function createConfigFiles() {
|
||||
if (!config.profiles) {
|
||||
config.profiles = {};
|
||||
}
|
||||
let configUpdated = false;
|
||||
if (!config.profiles.glmt) {
|
||||
config.profiles.glmt = '~/.ccs/glmt.settings.json';
|
||||
configUpdated = true;
|
||||
}
|
||||
// NOTE: gemini/codex profiles added on-demand, not during migration
|
||||
if (configUpdated) {
|
||||
const tmpPath = `${configPath}.tmp`;
|
||||
fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
||||
fs.renameSync(tmpPath, configPath);
|
||||
console.log('[OK] Updated config with GLMT profile');
|
||||
console.log('[OK] Updated config with glmt profile');
|
||||
} else {
|
||||
console.log('[OK] Config exists: ~/.ccs/config.json (preserved)');
|
||||
}
|
||||
@@ -299,6 +306,10 @@ function createConfigFiles() {
|
||||
console.log('[OK] Kimi profile exists: ~/.ccs/kimi.settings.json (preserved)');
|
||||
}
|
||||
|
||||
// NOTE: gemini.settings.json and codex.settings.json are NOT created during install
|
||||
// They are created on-demand when user runs `ccs gemini` or `ccs codex` for the first time
|
||||
// This prevents confusion - users need to run `--auth` first anyway
|
||||
|
||||
// Migrate existing Kimi configs to remove deprecated model fields (v4.1.2)
|
||||
// Kimi API changed - model fields now cause 401 errors
|
||||
if (fs.existsSync(kimiSettingsPath)) {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Config, Settings, ProfileMetadata } from '../types';
|
||||
export type ProfileType = 'settings' | 'account' | 'cliproxy' | 'default';
|
||||
|
||||
/** CLIProxy profile names (OAuth-based, zero config) */
|
||||
export const CLIPROXY_PROFILES = ['gemini', 'codex', 'qwen'] as const;
|
||||
export const CLIPROXY_PROFILES = ['gemini', 'codex', 'agy'] as const;
|
||||
export type CLIProxyProfileName = (typeof CLIPROXY_PROFILES)[number];
|
||||
|
||||
export interface ProfileDetectionResult {
|
||||
@@ -92,7 +92,7 @@ class ProfileDetector {
|
||||
return this.resolveDefaultProfile();
|
||||
}
|
||||
|
||||
// Priority 0: Check CLIProxy profiles (gemini, chatgpt, qwen) - OAuth-based, zero config
|
||||
// Priority 0: Check CLIProxy profiles (gemini, codex, agy) - OAuth-based, zero config
|
||||
if (CLIPROXY_PROFILES.includes(profileName as CLIProxyProfileName)) {
|
||||
return {
|
||||
type: 'cliproxy',
|
||||
|
||||
+1
-1
@@ -295,7 +295,7 @@ async function main(): Promise<void> {
|
||||
const profileInfo = detector.detectProfileType(profile);
|
||||
|
||||
if (profileInfo.type === 'cliproxy') {
|
||||
// CLIPROXY FLOW: OAuth-based profiles (gemini, chatgpt, qwen)
|
||||
// CLIPROXY FLOW: OAuth-based profiles (gemini, codex, agy)
|
||||
await execClaudeWithCLIProxy(claudeCli, profileInfo.name as CLIProxyProvider, remainingArgs);
|
||||
} else if (profileInfo.type === 'settings') {
|
||||
// Check if this is GLMT profile (requires proxy)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Auth Handler for CLIProxyAPI
|
||||
*
|
||||
* Manages OAuth authentication for CLIProxy providers (Gemini, Codex, Qwen).
|
||||
* Manages OAuth authentication for CLIProxy providers (Gemini, Codex, Antigravity).
|
||||
* 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
|
||||
@@ -93,12 +93,12 @@ const OAUTH_CONFIGS: Record<CLIProxyProvider, ProviderOAuthConfig> = {
|
||||
scopes: ['openid', 'profile'],
|
||||
authFlag: '-codex-login',
|
||||
},
|
||||
qwen: {
|
||||
provider: 'qwen',
|
||||
displayName: 'Alibaba Qwen',
|
||||
authUrl: 'https://auth.aliyun.com/oauth2/authorize',
|
||||
scopes: ['dashscope'],
|
||||
authFlag: '-qwen-login',
|
||||
agy: {
|
||||
provider: 'agy',
|
||||
displayName: 'Antigravity',
|
||||
authUrl: 'https://antigravity.ai/oauth/authorize',
|
||||
scopes: ['api'],
|
||||
authFlag: '-antigravity-login',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -181,7 +181,7 @@ export function getAuthStatus(provider: CLIProxyProvider): AuthStatus {
|
||||
* Get auth status for all providers
|
||||
*/
|
||||
export function getAllAuthStatus(): AuthStatus[] {
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'qwen'];
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy'];
|
||||
return providers.map(getAuthStatus);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Base Config Loader for CLIProxy Providers
|
||||
*
|
||||
* Loads provider configurations from config/base-{provider}.settings.json files.
|
||||
* This allows model mappings to be easily updated without digging into code.
|
||||
*
|
||||
* Config files are bundled with the npm package and read at runtime.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { CLIProxyProvider, ProviderModelMapping } from './types';
|
||||
|
||||
/** Base settings file structure */
|
||||
interface BaseSettings {
|
||||
env: {
|
||||
ANTHROPIC_BASE_URL: string;
|
||||
ANTHROPIC_AUTH_TOKEN: string;
|
||||
ANTHROPIC_MODEL: string;
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: string;
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: string;
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: string;
|
||||
};
|
||||
}
|
||||
|
||||
/** Cached configs to avoid repeated file reads */
|
||||
const configCache: Map<CLIProxyProvider, BaseSettings> = new Map();
|
||||
|
||||
/**
|
||||
* Get path to base config file for provider
|
||||
* Config files are in the config/ directory relative to package root
|
||||
*/
|
||||
function getBaseConfigPath(provider: CLIProxyProvider): string {
|
||||
// __dirname points to dist/cliproxy at runtime
|
||||
// Config files are at package root: ../config/
|
||||
const configDir = path.join(__dirname, '..', '..', 'config');
|
||||
return path.join(configDir, `base-${provider}.settings.json`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load base config for a provider
|
||||
* Returns parsed settings from config/base-{provider}.settings.json
|
||||
*/
|
||||
export function loadBaseConfig(provider: CLIProxyProvider): BaseSettings {
|
||||
// Check cache first
|
||||
const cached = configCache.get(provider);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const configPath = getBaseConfigPath(provider);
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
throw new Error(
|
||||
`Base config not found for provider '${provider}': ${configPath}\n` +
|
||||
`Expected file: config/base-${provider}.settings.json`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(configPath, 'utf-8');
|
||||
const settings: BaseSettings = JSON.parse(content);
|
||||
|
||||
// Validate required fields
|
||||
if (!settings.env || typeof settings.env !== 'object') {
|
||||
throw new Error('Missing or invalid "env" object');
|
||||
}
|
||||
|
||||
const required = [
|
||||
'ANTHROPIC_MODEL',
|
||||
'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
||||
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
||||
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
|
||||
];
|
||||
|
||||
for (const field of required) {
|
||||
if (!settings.env[field as keyof BaseSettings['env']]) {
|
||||
throw new Error(`Missing required field: env.${field}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Cache and return
|
||||
configCache.set(provider, settings);
|
||||
return settings;
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
throw new Error(`Failed to load base config for '${provider}': ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get model mapping from base config
|
||||
* Extracts model names from env vars
|
||||
*/
|
||||
export function getModelMappingFromConfig(provider: CLIProxyProvider): ProviderModelMapping {
|
||||
const config = loadBaseConfig(provider);
|
||||
|
||||
return {
|
||||
defaultModel: config.env.ANTHROPIC_MODEL,
|
||||
claudeModel: config.env.ANTHROPIC_MODEL,
|
||||
opusModel: config.env.ANTHROPIC_DEFAULT_OPUS_MODEL,
|
||||
sonnetModel: config.env.ANTHROPIC_DEFAULT_SONNET_MODEL,
|
||||
haikuModel: config.env.ANTHROPIC_DEFAULT_HAIKU_MODEL,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full env vars from base config
|
||||
* Returns the complete env object for Claude CLI
|
||||
*/
|
||||
export function getEnvVarsFromConfig(provider: CLIProxyProvider): NodeJS.ProcessEnv {
|
||||
const config = loadBaseConfig(provider);
|
||||
return config.env as unknown as NodeJS.ProcessEnv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear config cache (useful for testing)
|
||||
*/
|
||||
export function clearConfigCache(): void {
|
||||
configCache.clear();
|
||||
}
|
||||
@@ -80,7 +80,7 @@ async function waitForProxyReady(
|
||||
* Execute Claude CLI with CLIProxy (main entry point)
|
||||
*
|
||||
* @param claudeCli Path to Claude CLI executable
|
||||
* @param provider CLIProxy provider (gemini, chatgpt, qwen)
|
||||
* @param provider CLIProxy provider (gemini, codex, agy)
|
||||
* @param args Arguments to pass to Claude CLI
|
||||
* @param config Optional executor configuration
|
||||
*/
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
*
|
||||
* Generates config.yaml for CLIProxyAPI based on provider.
|
||||
* Handles OAuth token paths and provider-specific settings.
|
||||
*
|
||||
* Model mappings are loaded from config/base-{provider}.settings.json files
|
||||
* to allow easy updates without code changes.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { getCcsDir } from '../utils/config-manager';
|
||||
import { CLIProxyProvider, ProviderConfig, ProviderModelMapping } from './types';
|
||||
import { getModelMappingFromConfig } from './base-config-loader';
|
||||
|
||||
/** Settings file structure for user overrides */
|
||||
interface ProviderSettings {
|
||||
@@ -21,61 +25,37 @@ export const CLIPROXY_DEFAULT_PORT = 8317;
|
||||
/** Internal API key for CCS-managed requests */
|
||||
const CCS_INTERNAL_API_KEY = 'ccs-internal-managed';
|
||||
|
||||
/**
|
||||
* Provider configurations with model mappings
|
||||
*/
|
||||
export const PROVIDER_CONFIGS: Record<CLIProxyProvider, ProviderConfig> = {
|
||||
gemini: {
|
||||
name: 'gemini',
|
||||
displayName: 'Gemini',
|
||||
models: {
|
||||
defaultModel: 'gemini-2.5-pro',
|
||||
claudeModel: 'gemini-2.5-pro',
|
||||
opusModel: 'gemini-3-pro-preview',
|
||||
sonnetModel: 'gemini-2.5-pro',
|
||||
haikuModel: 'gemini-2.5-flash',
|
||||
},
|
||||
requiresOAuth: true,
|
||||
},
|
||||
codex: {
|
||||
name: 'codex',
|
||||
displayName: 'Codex',
|
||||
models: {
|
||||
defaultModel: 'gpt-5.1-codex-max',
|
||||
claudeModel: 'gpt-5.1-codex-max',
|
||||
opusModel: 'gpt-5.1-codex-max-high',
|
||||
sonnetModel: 'gpt-5.1-codex-max',
|
||||
haikuModel: 'gpt-5.1-codex-mini-high',
|
||||
},
|
||||
requiresOAuth: true,
|
||||
},
|
||||
qwen: {
|
||||
name: 'qwen',
|
||||
displayName: 'Qwen',
|
||||
models: {
|
||||
defaultModel: 'qwen-max',
|
||||
claudeModel: 'qwen-max',
|
||||
opusModel: 'qwen-max',
|
||||
sonnetModel: 'qwen-plus',
|
||||
haikuModel: 'qwen-turbo',
|
||||
},
|
||||
requiresOAuth: true,
|
||||
},
|
||||
/** Provider display names (static metadata) */
|
||||
const PROVIDER_DISPLAY_NAMES: Record<CLIProxyProvider, string> = {
|
||||
gemini: 'Gemini',
|
||||
codex: 'Codex',
|
||||
agy: 'Antigravity',
|
||||
};
|
||||
|
||||
/**
|
||||
* Get provider configuration
|
||||
* Model mappings are loaded from config/base-{provider}.settings.json
|
||||
*/
|
||||
export function getProviderConfig(provider: CLIProxyProvider): ProviderConfig {
|
||||
const config = PROVIDER_CONFIGS[provider];
|
||||
if (!config) {
|
||||
const displayName = PROVIDER_DISPLAY_NAMES[provider];
|
||||
if (!displayName) {
|
||||
throw new Error(`Unknown provider: ${provider}`);
|
||||
}
|
||||
return config;
|
||||
|
||||
// Load models from base config file
|
||||
const models = getModelMappingFromConfig(provider);
|
||||
|
||||
return {
|
||||
name: provider,
|
||||
displayName,
|
||||
models,
|
||||
requiresOAuth: true, // All CLIProxy providers require OAuth
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get model mapping for provider
|
||||
* Loads from config/base-{provider}.settings.json
|
||||
*/
|
||||
export function getModelMapping(provider: CLIProxyProvider): ProviderModelMapping {
|
||||
return getProviderConfig(provider).models;
|
||||
@@ -124,7 +104,7 @@ export function getBinDir(): string {
|
||||
|
||||
/**
|
||||
* Generate UNIFIED config.yaml content for ALL providers
|
||||
* This enables concurrent usage of gemini/codex/qwen without config conflicts.
|
||||
* This enables concurrent usage of gemini/codex/agy without config conflicts.
|
||||
* CLIProxyAPI routes requests by model name to the appropriate provider.
|
||||
*/
|
||||
function generateUnifiedConfigContent(port: number = CLIPROXY_DEFAULT_PORT): string {
|
||||
@@ -132,7 +112,7 @@ function generateUnifiedConfigContent(port: number = CLIPROXY_DEFAULT_PORT): str
|
||||
|
||||
// Unified config with all providers
|
||||
const config = `# CLIProxyAPI unified config generated by CCS
|
||||
# Supports: gemini, codex, qwen (concurrent usage)
|
||||
# Supports: gemini, codex, agy (concurrent usage)
|
||||
# Generated: ${new Date().toISOString()}
|
||||
|
||||
port: ${port}
|
||||
@@ -144,7 +124,7 @@ usage-statistics-enabled: false
|
||||
api-keys:
|
||||
- "${CCS_INTERNAL_API_KEY}"
|
||||
|
||||
# OAuth tokens stored in subdirectories (gemini/, codex/, qwen/)
|
||||
# OAuth tokens stored in auth/ directory
|
||||
# CLIProxyAPI auto-discovers auth files in subdirectories
|
||||
auth-dir: "${authDir}"
|
||||
|
||||
|
||||
@@ -58,10 +58,17 @@ export {
|
||||
getBinDir,
|
||||
configExists,
|
||||
deleteConfig,
|
||||
PROVIDER_CONFIGS,
|
||||
CLIPROXY_DEFAULT_PORT,
|
||||
} from './config-generator';
|
||||
|
||||
// Base config loader (for reading config/base-*.settings.json)
|
||||
export {
|
||||
loadBaseConfig,
|
||||
getModelMappingFromConfig,
|
||||
getEnvVarsFromConfig,
|
||||
clearConfigCache,
|
||||
} from './base-config-loader';
|
||||
|
||||
// Executor
|
||||
export { execClaudeWithCLIProxy, isPortAvailable, findAvailablePort } from './cliproxy-executor';
|
||||
|
||||
|
||||
@@ -107,8 +107,11 @@ export interface DownloadResult {
|
||||
|
||||
/**
|
||||
* Supported CLIProxy providers
|
||||
* - gemini: Google Gemini via OAuth
|
||||
* - codex: OpenAI Codex via OAuth
|
||||
* - agy: Antigravity via OAuth (short name for easy usage)
|
||||
*/
|
||||
export type CLIProxyProvider = 'gemini' | 'codex' | 'qwen';
|
||||
export type CLIProxyProvider = 'gemini' | 'codex' | 'agy';
|
||||
|
||||
/**
|
||||
* CLIProxy config.yaml structure (minimal)
|
||||
|
||||
@@ -4,9 +4,7 @@ import { colored } from '../utils/helpers';
|
||||
* Display comprehensive help information for CCS (Claude Code Switch)
|
||||
*/
|
||||
export function handleHelpCommand(): void {
|
||||
console.log(
|
||||
colored('CCS (Claude Code Switch) - Instant profile switching for Claude CLI', 'bold')
|
||||
);
|
||||
console.log(colored('CCS (Claude Code Switch) - Profile switching for Claude CLI', 'bold'));
|
||||
console.log('');
|
||||
|
||||
console.log(colored('Usage:', 'cyan'));
|
||||
@@ -14,43 +12,61 @@ export function handleHelpCommand(): void {
|
||||
console.log(` ${colored('ccs', 'yellow')} [flags]`);
|
||||
console.log('');
|
||||
|
||||
console.log(colored('Description:', 'cyan'));
|
||||
console.log(' Switch between multiple Claude accounts and alternative models');
|
||||
console.log(' (GLM, Kimi) instantly. Run different Claude CLI sessions concurrently');
|
||||
console.log(' with auto-recovery. Zero downtime.');
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 1: API KEY MODELS
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log(colored('═══ API Key Models ═══', 'cyanBold'));
|
||||
console.log(' Configure API keys in ~/.ccs/*.settings.json');
|
||||
console.log('');
|
||||
|
||||
console.log(colored('Requirements:', 'cyan'));
|
||||
console.log(' Node.js 14+ (detected automatically by bootstrap)');
|
||||
console.log(' npm 5.2+ (for npx, comes with Node.js 8.2+)');
|
||||
console.log('');
|
||||
|
||||
console.log(colored('Model Switching:', 'cyan'));
|
||||
console.log(` ${colored('ccs', 'yellow')} Use default Claude account`);
|
||||
console.log(
|
||||
` ${colored('ccs codex', 'yellow')} Codex via OAuth (zero config)`
|
||||
);
|
||||
console.log(
|
||||
` ${colored('ccs gemini', 'yellow')} Gemini via OAuth (zero config)`
|
||||
);
|
||||
console.log(` ${colored('ccs glm', 'yellow')} GLM 4.6 (API key required)`);
|
||||
console.log(` ${colored('ccs glmt', 'yellow')} GLM with thinking mode`);
|
||||
console.log(` ${colored('ccs kimi', 'yellow')} Kimi for Coding (API key)`);
|
||||
console.log(
|
||||
` ${colored('ccs qwen', 'yellow')} Qwen via OAuth (zero config)`
|
||||
);
|
||||
console.log(` ${colored('ccs gemini', 'yellow')} "explain this" Use Gemini with prompt`);
|
||||
console.log('');
|
||||
|
||||
console.log(colored('Account Management:', 'cyan'));
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 2: ACCOUNT MANAGEMENT
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log(colored('═══ Account Management ═══', 'cyanBold'));
|
||||
console.log(' Run multiple Claude accounts concurrently');
|
||||
console.log('');
|
||||
console.log(
|
||||
` ${colored('ccs auth --help', 'yellow')} Run multiple Claude accounts concurrently`
|
||||
` ${colored('ccs auth --help', 'yellow')} Show account management commands`
|
||||
);
|
||||
console.log(` ${colored('ccs auth create <name>', 'yellow')} Create new account profile`);
|
||||
console.log(` ${colored('ccs auth list', 'yellow')} List all account profiles`);
|
||||
console.log('');
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 3: CLI PROXY (OAUTH PROVIDERS)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log(colored('═══ CLI Proxy (OAuth Providers) ═══', 'cyanBold'));
|
||||
console.log(' Zero-config OAuth authentication via CLIProxyAPI');
|
||||
console.log(' First run: Browser opens for authentication');
|
||||
console.log(' Settings: ~/.ccs/{provider}.settings.json (created after auth)');
|
||||
console.log('');
|
||||
console.log(
|
||||
` ${colored('ccs gemini', 'yellow')} Google Gemini (gemini-2.5-pro)`
|
||||
);
|
||||
console.log(
|
||||
` ${colored('ccs codex', 'yellow')} OpenAI Codex (gpt-5.1-codex-max)`
|
||||
);
|
||||
console.log(
|
||||
` ${colored('ccs agy', 'yellow')} Antigravity (gemini-3-pro-preview)`
|
||||
);
|
||||
console.log('');
|
||||
console.log(` ${colored('ccs gemini --auth', 'yellow')} Authenticate only`);
|
||||
console.log(` ${colored('ccs gemini --logout', 'yellow')} Clear authentication`);
|
||||
console.log(` ${colored('ccs gemini --headless', 'yellow')} Headless auth (for SSH)`);
|
||||
console.log(` ${colored('ccs gemini "explain code"', 'yellow')} Use with prompt`);
|
||||
console.log('');
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// DELEGATION
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log(colored('Delegation (inside Claude Code CLI):', 'cyan'));
|
||||
console.log(
|
||||
` ${colored('/ccs "task"', 'yellow')} Delegate task (auto-selects best profile)`
|
||||
` ${colored('/ccs "task"', 'yellow')} Delegate task (auto-selects profile)`
|
||||
);
|
||||
console.log(
|
||||
` ${colored('/ccs --glm "task"', 'yellow')} Force GLM-4.6 for simple tasks`
|
||||
@@ -59,7 +75,6 @@ export function handleHelpCommand(): void {
|
||||
console.log(
|
||||
` ${colored('/ccs:continue "follow-up"', 'yellow')} Continue last delegation session`
|
||||
);
|
||||
console.log(' Save tokens by delegating simple tasks to cost-optimized models');
|
||||
console.log('');
|
||||
|
||||
console.log(colored('Diagnostics:', 'cyan'));
|
||||
@@ -87,38 +102,34 @@ export function handleHelpCommand(): void {
|
||||
console.log(' Profiles: ~/.ccs/profiles.json');
|
||||
console.log(' Instances: ~/.ccs/instances/');
|
||||
console.log(' Settings: ~/.ccs/*.settings.json');
|
||||
console.log(' Environment: CCS_CONFIG (override config path)');
|
||||
console.log('');
|
||||
|
||||
console.log(colored('CLI Proxy:', 'cyan'));
|
||||
console.log(' Binary: ~/.ccs/cliproxy/bin/cli-proxy-api');
|
||||
console.log(' Config: ~/.ccs/cliproxy/config.yaml');
|
||||
console.log(' Auth: ~/.ccs/cliproxy/auth/');
|
||||
console.log(' Port: 8317 (default)');
|
||||
console.log('');
|
||||
|
||||
console.log(colored('Shared Data:', 'cyan'));
|
||||
console.log(' Commands: ~/.ccs/shared/commands/');
|
||||
console.log(' Skills: ~/.ccs/shared/skills/');
|
||||
console.log(' Agents: ~/.ccs/shared/agents/');
|
||||
console.log(' Plugins: ~/.ccs/shared/plugins/');
|
||||
console.log(' Note: Commands, skills, agents, and plugins are symlinked across all profiles');
|
||||
console.log(' Note: Symlinked across all profiles');
|
||||
console.log('');
|
||||
|
||||
console.log(colored('Examples:', 'cyan'));
|
||||
console.log(` ${colored('$ ccs', 'yellow')} # Use default account`);
|
||||
console.log(` ${colored('$ ccs gemini "explain code"', 'yellow')} # Zero-config OAuth`);
|
||||
console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # Cost-optimized model`);
|
||||
console.log('');
|
||||
console.log(
|
||||
` For more: ${colored('https://github.com/kaitranntt/ccs/blob/main/README.md', 'cyan')}`
|
||||
` ${colored('$ ccs gemini', 'yellow')} # OAuth (browser opens first time)`
|
||||
);
|
||||
console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # API key model`);
|
||||
console.log('');
|
||||
console.log(` Docs: ${colored('https://github.com/kaitranntt/ccs', 'cyan')}`);
|
||||
console.log('');
|
||||
|
||||
console.log(colored('Uninstall:', 'yellow'));
|
||||
console.log(' npm (recommended): npm uninstall -g @kaitranntt/ccs');
|
||||
console.log(' Legacy (deprecated):');
|
||||
console.log(' macOS/Linux: curl -fsSL ccs.kaitran.ca/uninstall | bash');
|
||||
console.log(' Windows: irm ccs.kaitran.ca/uninstall | iex');
|
||||
console.log('');
|
||||
|
||||
console.log(colored('Documentation:', 'cyan'));
|
||||
console.log(` GitHub: ${colored('https://github.com/kaitranntt/ccs', 'cyan')}`);
|
||||
console.log(' Docs: https://github.com/kaitranntt/ccs/blob/main/README.md');
|
||||
console.log(' Issues: https://github.com/kaitranntt/ccs/issues');
|
||||
console.log(' npm uninstall -g @kaitranntt/ccs');
|
||||
console.log('');
|
||||
|
||||
console.log(`${colored('License:', 'cyan')} MIT`);
|
||||
|
||||
@@ -754,7 +754,7 @@ class Doctor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 11: CLIProxy health (OAuth profiles: gemini, chatgpt, qwen)
|
||||
* Check 11: CLIProxy health (OAuth profiles: gemini, codex, agy)
|
||||
*/
|
||||
private async checkCLIProxy(): Promise<void> {
|
||||
// 1. Binary installed?
|
||||
|
||||
+9
-1
@@ -18,7 +18,15 @@ export enum LogLevel {
|
||||
/**
|
||||
* Color codes (TTY-aware)
|
||||
*/
|
||||
export type ColorName = 'red' | 'green' | 'yellow' | 'blue' | 'cyan' | 'bold' | 'reset';
|
||||
export type ColorName =
|
||||
| 'red'
|
||||
| 'green'
|
||||
| 'yellow'
|
||||
| 'blue'
|
||||
| 'cyan'
|
||||
| 'bold'
|
||||
| 'cyanBold'
|
||||
| 'reset';
|
||||
|
||||
/**
|
||||
* Terminal capabilities
|
||||
|
||||
@@ -20,11 +20,12 @@ function getColors(): Record<ColorName, string> {
|
||||
green: '\x1b[0;32m',
|
||||
blue: '\x1b[0;34m',
|
||||
bold: '\x1b[1m',
|
||||
cyanBold: '\x1b[1;36m',
|
||||
reset: '\x1b[0m',
|
||||
};
|
||||
}
|
||||
|
||||
return { red: '', yellow: '', cyan: '', green: '', blue: '', bold: '', reset: '' };
|
||||
return { red: '', yellow: '', cyan: '', green: '', blue: '', bold: '', cyanBold: '', reset: '' };
|
||||
}
|
||||
|
||||
// Colors object (dynamic)
|
||||
|
||||
Reference in New Issue
Block a user