mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 08:17:11 +00:00
feat(cliproxy): add qwen code oauth provider support (#31)
Add 'qwen' as new CLIProxy OAuth provider with --qwen-login flag. Support qwen3-coder model with base config at config/base-qwen.settings.json. Register qwen OAuth endpoints and token prefix handling. Update help documentation across all CLI entry points. Implements issue #29
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/qwen",
|
||||
"ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed",
|
||||
"ANTHROPIC_MODEL": "qwen3-coder",
|
||||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "qwen3-coder",
|
||||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "qwen3-coder",
|
||||
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "qwen3-coder"
|
||||
}
|
||||
}
|
||||
@@ -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', 'agy'] as const;
|
||||
export const CLIPROXY_PROFILES = ['gemini', 'codex', 'agy', 'qwen'] as const;
|
||||
export type CLIProxyProfileName = (typeof CLIPROXY_PROFILES)[number];
|
||||
|
||||
export interface ProfileDetectionResult {
|
||||
@@ -23,7 +23,7 @@ export interface ProfileDetectionResult {
|
||||
settingsPath?: string;
|
||||
profile?: Settings | ProfileMetadata;
|
||||
message?: string;
|
||||
/** For cliproxy variants: the underlying provider (gemini, codex, agy) */
|
||||
/** For cliproxy variants: the underlying provider (gemini, codex, agy, qwen) */
|
||||
provider?: CLIProxyProfileName;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ class ProfileDetector {
|
||||
* Detect profile type and return routing information
|
||||
*
|
||||
* Priority order:
|
||||
* 0. Hardcoded CLIProxy profiles (gemini, codex, agy)
|
||||
* 0. Hardcoded CLIProxy profiles (gemini, codex, agy, qwen)
|
||||
* 1. User-defined CLIProxy variants (config.cliproxy section)
|
||||
* 2. Settings-based profiles (config.profiles section)
|
||||
* 3. Account-based profiles (profiles.json)
|
||||
@@ -100,7 +100,7 @@ class ProfileDetector {
|
||||
return this.resolveDefaultProfile();
|
||||
}
|
||||
|
||||
// Priority 0: Check CLIProxy profiles (gemini, codex, agy) - OAuth-based, zero config
|
||||
// Priority 0: Check CLIProxy profiles (gemini, codex, agy, qwen) - 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, codex, agy) or user-defined variants
|
||||
// CLIPROXY FLOW: OAuth-based profiles (gemini, codex, agy, qwen) or user-defined variants
|
||||
const provider = profileInfo.provider || (profileInfo.name as CLIProxyProvider);
|
||||
const customSettingsPath = profileInfo.settingsPath; // undefined for hardcoded profiles
|
||||
await execClaudeWithCLIProxy(claudeCli, provider, remainingArgs, { customSettingsPath });
|
||||
|
||||
@@ -100,6 +100,13 @@ const OAUTH_CONFIGS: Record<CLIProxyProvider, ProviderOAuthConfig> = {
|
||||
scopes: ['api'],
|
||||
authFlag: '--antigravity-login',
|
||||
},
|
||||
qwen: {
|
||||
provider: 'qwen',
|
||||
displayName: 'Qwen Code',
|
||||
authUrl: 'https://chat.qwen.ai/api/v1/oauth2/device/code',
|
||||
scopes: ['openid', 'profile', 'email', 'model.completion'],
|
||||
authFlag: '--qwen-login',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -129,6 +136,7 @@ const PROVIDER_AUTH_PREFIXES: Record<CLIProxyProvider, string[]> = {
|
||||
gemini: ['gemini-', 'google-'],
|
||||
codex: ['codex-', 'openai-'],
|
||||
agy: ['antigravity-', 'agy-'],
|
||||
qwen: ['qwen-'],
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -139,6 +147,7 @@ const PROVIDER_TYPE_VALUES: Record<CLIProxyProvider, string[]> = {
|
||||
gemini: ['gemini'],
|
||||
codex: ['codex'],
|
||||
agy: ['antigravity'],
|
||||
qwen: ['qwen'],
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -256,7 +265,7 @@ export function getAuthStatus(provider: CLIProxyProvider): AuthStatus {
|
||||
* Get auth status for all providers
|
||||
*/
|
||||
export function getAllAuthStatus(): AuthStatus[] {
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy'];
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen'];
|
||||
return providers.map(getAuthStatus);
|
||||
}
|
||||
|
||||
|
||||
@@ -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, codex, agy)
|
||||
* @param provider CLIProxy provider (gemini, codex, agy, qwen)
|
||||
* @param args Arguments to pass to Claude CLI
|
||||
* @param config Optional executor configuration
|
||||
*/
|
||||
|
||||
@@ -30,6 +30,7 @@ const PROVIDER_DISPLAY_NAMES: Record<CLIProxyProvider, string> = {
|
||||
gemini: 'Gemini',
|
||||
codex: 'Codex',
|
||||
agy: 'Antigravity',
|
||||
qwen: 'Qwen Code',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -112,7 +113,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, agy (concurrent usage)
|
||||
# Supports: gemini, codex, agy, qwen (concurrent usage)
|
||||
# Generated: ${new Date().toISOString()}
|
||||
|
||||
port: ${port}
|
||||
|
||||
@@ -110,8 +110,9 @@ export interface DownloadResult {
|
||||
* - gemini: Google Gemini via OAuth
|
||||
* - codex: OpenAI Codex via OAuth
|
||||
* - agy: Antigravity via OAuth (short name for easy usage)
|
||||
* - qwen: Qwen Code via OAuth (qwen3-coder)
|
||||
*/
|
||||
export type CLIProxyProvider = 'gemini' | 'codex' | 'agy';
|
||||
export type CLIProxyProvider = 'gemini' | 'codex' | 'agy' | 'qwen';
|
||||
|
||||
/**
|
||||
* CLIProxy config.yaml structure (minimal)
|
||||
|
||||
@@ -54,6 +54,7 @@ export function handleHelpCommand(): void {
|
||||
console.log(
|
||||
` ${colored('ccs agy', 'yellow')} Antigravity (gemini-3-pro-preview)`
|
||||
);
|
||||
console.log(` ${colored('ccs qwen', 'yellow')} Qwen Code (qwen3-coder)`);
|
||||
console.log('');
|
||||
console.log(` ${colored('ccs <provider> --auth', 'yellow')} Authenticate only`);
|
||||
console.log(` ${colored('ccs <provider> --logout', 'yellow')} Clear authentication`);
|
||||
|
||||
@@ -754,7 +754,7 @@ class Doctor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check 11: CLIProxy health (OAuth profiles: gemini, codex, agy)
|
||||
* Check 11: CLIProxy health (OAuth profiles: gemini, codex, agy, qwen)
|
||||
*/
|
||||
private async checkCLIProxy(): Promise<void> {
|
||||
// 1. Binary installed?
|
||||
|
||||
+2
-2
@@ -17,8 +17,8 @@ export interface ProfilesConfig {
|
||||
* Example: "flash" → gemini provider with gemini-2.5-flash model
|
||||
*/
|
||||
export interface CLIProxyVariantConfig {
|
||||
/** CLIProxy provider to use (gemini, codex, agy) */
|
||||
provider: 'gemini' | 'codex' | 'agy';
|
||||
/** CLIProxy provider to use (gemini, codex, agy, qwen) */
|
||||
provider: 'gemini' | 'codex' | 'agy' | 'qwen';
|
||||
/** Path to settings.json with custom model configuration */
|
||||
settings: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user