feat(cliproxy): add backend selection for CLIProxyAPI vs CLIProxyAPIPlus

Add cliproxy.backend config field to choose between CLIProxyAPI (original)
and CLIProxyAPIPlus backends. Includes CLI --backend flag, Dashboard toggle,
and provider validation to block Kiro/ghcp on original backend.
This commit is contained in:
kaitranntt committed 2026-01-18 10:10:01 -05:00
1 parent ca86772208
commit 8ade4a6b26
12 files changed
+601 -71

No files matched your search

+74 -19
View File
@@ -29,11 +29,11 @@ import {
} from '../cliproxy/account-manager';
import { fetchAllProviderQuotas } from '../cliproxy/quota-fetcher';
import { isOnCooldown } from '../cliproxy/quota-manager';
import { CLIPROXY_FALLBACK_VERSION } from '../cliproxy/platform-detector';
import { DEFAULT_BACKEND, CLIPROXY_FALLBACK_VERSION } from '../cliproxy/platform-detector';
import { CLIPROXY_PROFILES, CLIProxyProfileName } from '../auth/profile-detector';
import { supportsModelConfig, getProviderCatalog, ModelEntry } from '../cliproxy/model-catalog';
import { CLIProxyProvider } from '../cliproxy/types';
import { isUnifiedMode } from '../config/unified-config-loader';
import { CLIProxyProvider, CLIProxyBackend } from '../cliproxy/types';
import { isUnifiedMode, loadOrCreateUnifiedConfig } from '../config/unified-config-loader';
import {
initUI,
header,
@@ -68,6 +68,47 @@ import {
// ARGUMENT PARSING
// ============================================================================
/**
* Parse --backend flag from args
* Returns the backend value and remaining args without --backend flag
*/
function parseBackendArg(args: string[]): {
backend: CLIProxyBackend | undefined;
remainingArgs: string[];
} {
const backendIdx = args.indexOf('--backend');
if (backendIdx === -1) {
// Also check for --backend=value format
const backendEqualsIdx = args.findIndex((a) => a.startsWith('--backend='));
if (backendEqualsIdx !== -1) {
const value = args[backendEqualsIdx].split('=')[1] as CLIProxyBackend;
if (value !== 'original' && value !== 'plus') {
return { backend: undefined, remainingArgs: args };
}
const remainingArgs = [...args];
remainingArgs.splice(backendEqualsIdx, 1);
return { backend: value, remainingArgs };
}
return { backend: undefined, remainingArgs: args };
}
const value = args[backendIdx + 1];
if (value !== 'original' && value !== 'plus') {
return { backend: undefined, remainingArgs: args };
}
const remainingArgs = [...args];
remainingArgs.splice(backendIdx, 2);
return { backend: value, remainingArgs };
}
/**
* Get effective backend (CLI flag > config.yaml > default)
*/
function getEffectiveBackend(cliBackend?: CLIProxyBackend): CLIProxyBackend {
if (cliBackend) return cliBackend;
const config = loadOrCreateUnifiedConfig();
return config.cliproxy?.backend ?? DEFAULT_BACKEND;
}
interface CliproxyProfileArgs {
name?: string;
provider?: CLIProxyProfileName;
@@ -431,14 +472,18 @@ async function handleProxyStatus(): Promise<void> {
console.log('');
}
async function showStatus(verbose: boolean): Promise<void> {
async function showStatus(verbose: boolean, backend: CLIProxyBackend): Promise<void> {
await initUI();
const status = getBinaryStatus();
const status = getBinaryStatus(backend);
console.log('');
console.log(color('CLIProxy Plus Status', 'primary'));
const backendLabel = backend === 'plus' ? 'CLIProxy Plus' : 'CLIProxy (Original)';
console.log(color(`${backendLabel} Status`, 'primary'));
console.log('');
console.log(
` Backend: ${color(backend, 'info')}${backend === DEFAULT_BACKEND ? dim(' (default)') : ''}`
);
if (status.installed) {
console.log(` Installed: ${color('Yes', 'success')}`);
const versionLabel = status.pinnedVersion
@@ -576,7 +621,13 @@ async function showHelp(): Promise<void> {
['--update', 'Unpin and update to latest version'],
],
],
['Options:', [['--verbose, -v', 'Show detailed quota fetch diagnostics']]],
[
'Options:',
[
['--backend <type>', 'Use specific backend: original | plus (default: from config)'],
['--verbose, -v', 'Show detailed quota fetch diagnostics'],
],
],
];
for (const [title, cmds] of sections) {
@@ -909,16 +960,20 @@ async function handleQuotaStatus(verbose = false): Promise<void> {
// ============================================================================
export async function handleCliproxyCommand(args: string[]): Promise<void> {
const verbose = args.includes('--verbose') || args.includes('-v');
const command = args[0];
// Parse --backend flag first (before other processing)
const { backend: cliBackend, remainingArgs } = parseBackendArg(args);
const effectiveBackend = getEffectiveBackend(cliBackend);
if (args.includes('--help') || args.includes('-h')) {
const verbose = remainingArgs.includes('--verbose') || remainingArgs.includes('-v');
const command = remainingArgs[0];
if (remainingArgs.includes('--help') || remainingArgs.includes('-h')) {
await showHelp();
return;
}
if (command === 'create') {
await handleCreate(args.slice(1));
await handleCreate(remainingArgs.slice(1));
return;
}
@@ -928,7 +983,7 @@ export async function handleCliproxyCommand(args: string[]): Promise<void> {
}
if (command === 'remove' || command === 'delete' || command === 'rm') {
await handleRemove(args.slice(1));
await handleRemove(remainingArgs.slice(1));
return;
}
@@ -949,17 +1004,17 @@ export async function handleCliproxyCommand(args: string[]): Promise<void> {
// Quota management commands
if (command === 'default') {
await handleSetDefault(args.slice(1));
await handleSetDefault(remainingArgs.slice(1));
return;
}
if (command === 'pause') {
await handlePauseAccount(args.slice(1));
await handlePauseAccount(remainingArgs.slice(1));
return;
}
if (command === 'resume') {
await handleResumeAccount(args.slice(1));
await handleResumeAccount(remainingArgs.slice(1));
return;
}
@@ -968,9 +1023,9 @@ export async function handleCliproxyCommand(args: string[]): Promise<void> {
return;
}
const installIdx = args.indexOf('--install');
const installIdx = remainingArgs.indexOf('--install');
if (installIdx !== -1) {
let version = args[installIdx + 1];
let version = remainingArgs[installIdx + 1];
if (!version || version.startsWith('-')) {
console.error(fail('Missing version argument for --install'));
console.error(' Usage: ccs cliproxy --install <version>');
@@ -983,10 +1038,10 @@ export async function handleCliproxyCommand(args: string[]): Promise<void> {
return;
}
if (args.includes('--latest') || args.includes('--update')) {
if (remainingArgs.includes('--latest') || remainingArgs.includes('--update')) {
await handleInstallLatest(verbose);
return;
}
await showStatus(verbose);
await showStatus(verbose, effectiveBackend);
}