diff --git a/src/cliproxy/services/variant-config-adapter.ts b/src/cliproxy/services/variant-config-adapter.ts index 1b1b3f15..e8179b51 100644 --- a/src/cliproxy/services/variant-config-adapter.ts +++ b/src/cliproxy/services/variant-config-adapter.ts @@ -1,12 +1,7 @@ -/** - * CLIProxy Variant Config Adapters - * - * Handles reading/writing variant config in both unified and legacy formats. - */ - import * as fs from 'fs'; import { getConfigPath, loadConfigSafe } from '../../utils/config-manager'; import { CLIProxyProvider } from '../types'; +import type { TargetType } from '../../targets/target-adapter'; import { CLIProxyVariantConfig, CompositeVariantConfig, @@ -20,19 +15,15 @@ import { } from '../../config/unified-config-loader'; import { CLIPROXY_DEFAULT_PORT } from '../config-generator'; -/** First port for variant profiles (8318 = default + 1) */ export const VARIANT_PORT_BASE = CLIPROXY_DEFAULT_PORT + 1; - -/** Maximum port offset for variants (100 ports: 8318-8417) */ export const VARIANT_PORT_MAX_OFFSET = 100; - -/** Variant configuration structure */ export interface VariantConfig { provider: string; settings?: string; account?: string; model?: string; port?: number; + target?: TargetType; /** Composite variant fields */ type?: 'composite'; default_tier?: 'opus' | 'sonnet' | 'haiku'; @@ -45,9 +36,6 @@ export interface VariantConfig { hasFallback?: boolean; } -/** - * Check if variant exists in config - */ export function variantExistsInConfig(name: string): boolean { try { if (isUnifiedMode()) { @@ -61,10 +49,6 @@ export function variantExistsInConfig(name: string): boolean { } } -/** - * Get next available port for a new variant. - * Scans existing variants, returns first unused port starting from VARIANT_PORT_BASE. - */ export function getNextAvailablePort(): number { const variants = listVariantsFromConfig(); const usedPorts = new Set(); @@ -89,9 +73,6 @@ export function getNextAvailablePort(): number { ); } -/** - * List variants from config - */ export function listVariantsFromConfig(): Record { try { if (isUnifiedMode()) { @@ -139,6 +120,7 @@ export function listVariantsFromConfig(): Record { provider: defaultTierConfig.provider, settings: composite.settings, port: composite.port, + target: composite.target || 'claude', type: 'composite', default_tier: composite.default_tier, tiers: normalizedTiers, @@ -151,6 +133,7 @@ export function listVariantsFromConfig(): Record { settings: single.settings, account: single.account, port: single.port, + target: single.target || 'claude', }; } } catch (error) { @@ -171,12 +154,14 @@ export function listVariantsFromConfig(): Record { settings: string; account?: string; port?: number; + target?: TargetType; }; result[name] = { provider: v.provider, settings: v.settings, account: v.account, port: v.port, + target: v.target || 'claude', }; } return result; @@ -185,9 +170,6 @@ export function listVariantsFromConfig(): Record { } } -/** - * Save composite variant to unified config - */ export function saveCompositeVariantUnified(name: string, config: CompositeVariantConfig): void { const unifiedConfig = loadOrCreateUnifiedConfig(); @@ -206,15 +188,13 @@ export function saveCompositeVariantUnified(name: string, config: CompositeVaria saveUnifiedConfig(unifiedConfig); } -/** - * Save variant to unified config - */ export function saveVariantUnified( name: string, provider: CLIProxyProvider, settingsPath: string, account?: string, - port?: number + port?: number, + target: TargetType = 'claude' ): void { const config = loadOrCreateUnifiedConfig(); @@ -234,20 +214,19 @@ export function saveVariantUnified( account, settings: settingsPath, port, + ...(target !== 'claude' && { target }), }; saveUnifiedConfig(config); } -/** - * Save variant to legacy JSON config - */ export function saveVariantLegacy( name: string, provider: string, settingsPath: string, account?: string, - port?: number + port?: number, + target: TargetType = 'claude' ): void { const configPath = getConfigPath(); @@ -262,7 +241,13 @@ export function saveVariantLegacy( config.cliproxy = {}; } - const variantConfig: { provider: string; settings: string; account?: string; port?: number } = { + const variantConfig: { + provider: string; + settings: string; + account?: string; + port?: number; + target?: TargetType; + } = { provider, settings: settingsPath, }; @@ -272,6 +257,9 @@ export function saveVariantLegacy( if (port) { variantConfig.port = port; } + if (target !== 'claude') { + variantConfig.target = target; + } config.cliproxy[name] = variantConfig; const tempPath = configPath + '.tmp'; @@ -279,9 +267,6 @@ export function saveVariantLegacy( fs.renameSync(tempPath, configPath); } -/** - * Remove variant from unified config - */ export function removeVariantFromUnifiedConfig(name: string): VariantConfig | null { const config = loadOrCreateUnifiedConfig(); @@ -299,6 +284,7 @@ export function removeVariantFromUnifiedConfig(name: string): VariantConfig | nu provider: composite.tiers[composite.default_tier].provider, settings: composite.settings, port: composite.port, + target: composite.target || 'claude', type: 'composite', default_tier: composite.default_tier, tiers: composite.tiers, @@ -309,12 +295,10 @@ export function removeVariantFromUnifiedConfig(name: string): VariantConfig | nu provider: singleVariant.provider, settings: singleVariant.settings, port: singleVariant.port, + target: singleVariant.target || 'claude', }; } -/** - * Remove variant from legacy JSON config - */ export function removeVariantFromLegacyConfig(name: string): VariantConfig | null { const configPath = getConfigPath(); @@ -329,7 +313,12 @@ export function removeVariantFromLegacyConfig(name: string): VariantConfig | nul return null; } - const variant = config.cliproxy[name] as { provider: string; settings: string; port?: number }; + const variant = config.cliproxy[name] as { + provider: string; + settings: string; + port?: number; + target?: TargetType; + }; delete config.cliproxy[name]; if (Object.keys(config.cliproxy).length === 0) { diff --git a/src/cliproxy/services/variant-service.ts b/src/cliproxy/services/variant-service.ts index a3471c13..fbb917a6 100644 --- a/src/cliproxy/services/variant-service.ts +++ b/src/cliproxy/services/variant-service.ts @@ -10,6 +10,7 @@ import * as path from 'path'; import { CLIProxyProfileName } from '../../auth/profile-detector'; import { CLIProxyProvider, CLIProxyBackend, PLUS_ONLY_PROVIDERS } from '../types'; import { CompositeTierConfig, CompositeVariantConfig } from '../../config/unified-config-types'; +import type { TargetType } from '../../targets/target-adapter'; import { isReservedName, isWindowsReservedName } from '../../config/reserved-names'; import { loadOrCreateUnifiedConfig } from '../../config/unified-config-loader'; import { DEFAULT_BACKEND } from '../platform-detector'; @@ -110,7 +111,8 @@ export function createVariant( name: string, provider: CLIProxyProfileName, model: string, - account?: string + account?: string, + target: TargetType = 'claude' ): VariantOperationResult { try { // Validate provider/backend compatibility (block kiro/ghcp on original backend) @@ -131,17 +133,25 @@ export function createVariant( provider as CLIProxyProvider, getRelativeSettingsPath(provider, name), account, - port + port, + target ); } else { settingsPath = createSettingsFile(name, provider, model, port); - saveVariantLegacy(name, provider, `~/.ccs/${path.basename(settingsPath)}`, account, port); + saveVariantLegacy( + name, + provider, + `~/.ccs/${path.basename(settingsPath)}`, + account, + port, + target + ); } return { success: true, settingsPath, - variant: { provider, model, account, port }, + variant: { provider, model, account, port, target }, }; } catch (error) { return { @@ -208,6 +218,7 @@ export interface UpdateVariantOptions { provider?: CLIProxyProfileName; account?: string; model?: string; + target?: TargetType; } /** @@ -233,6 +244,8 @@ export function updateVariant(name: string, updates: UpdateVariantOptions): Vari const providerChanged = updates.provider !== undefined && updates.provider !== existing.provider; + const existingTarget = existing.target || 'claude'; + const targetChanged = updates.target !== undefined && updates.target !== existingTarget; const hasModelUpdate = updates.model !== undefined && updates.model.trim().length > 0; if (providerChanged && !hasModelUpdate) { @@ -257,8 +270,8 @@ export function updateVariant(name: string, updates: UpdateVariantOptions): Vari } } - // Update config entry if provider or account changed - if (updates.provider !== undefined || updates.account !== undefined) { + // Update config entry if provider/account/target changed + if (updates.provider !== undefined || updates.account !== undefined || targetChanged) { const newProvider = updates.provider ?? existing.provider; // Validate provider/backend compatibility on provider change @@ -269,6 +282,7 @@ export function updateVariant(name: string, updates: UpdateVariantOptions): Vari } } const newAccount = updates.account !== undefined ? updates.account : existing.account; + const newTarget = updates.target ?? existingTarget; if (isUnifiedMode()) { saveVariantUnified( @@ -276,7 +290,8 @@ export function updateVariant(name: string, updates: UpdateVariantOptions): Vari newProvider as CLIProxyProvider, existing.settings || '', newAccount || undefined, - existing.port + existing.port, + newTarget ); } else { saveVariantLegacy( @@ -284,7 +299,8 @@ export function updateVariant(name: string, updates: UpdateVariantOptions): Vari newProvider, existing.settings || '', newAccount || undefined, - existing.port + existing.port, + newTarget ); } } @@ -297,6 +313,7 @@ export function updateVariant(name: string, updates: UpdateVariantOptions): Vari account: updates.account !== undefined ? updates.account : existing.account, port: existing.port, settings: existing.settings, + target: updates.target ?? existingTarget, }, }; } catch (error) { @@ -308,6 +325,7 @@ export function updateVariant(name: string, updates: UpdateVariantOptions): Vari export interface CreateCompositeVariantOptions { name: string; defaultTier: 'opus' | 'sonnet' | 'haiku'; + target?: TargetType; tiers: { opus: CompositeTierConfig; sonnet: CompositeTierConfig; @@ -329,7 +347,7 @@ export function createCompositeVariant( } try { - const { name, defaultTier, tiers } = options; + const { name, defaultTier, tiers, target = 'claude' } = options; const validationError = validateCompositeTiers(tiers, { defaultTier, @@ -361,6 +379,7 @@ export function createCompositeVariant( tiers, settings: settingsPath, port, + ...(target !== 'claude' && { target }), }; saveCompositeVariantUnified(name, compositeConfig); @@ -373,6 +392,7 @@ export function createCompositeVariant( default_tier: defaultTier, tiers, port, + target, }, }; } catch (error) { @@ -384,6 +404,7 @@ export function createCompositeVariant( export interface UpdateCompositeVariantOptions { defaultTier?: 'opus' | 'sonnet' | 'haiku'; tiers?: Partial>; + target?: TargetType; } /** @@ -418,6 +439,8 @@ export function updateCompositeVariant( }; const newDefaultTier = updates.defaultTier ?? existing.default_tier ?? 'sonnet'; + const existingTarget = existing.target || 'claude'; + const newTarget = updates.target ?? existingTarget; const validationError = validateCompositeTiers(mergedTiers, { defaultTier: newDefaultTier, requireAllTiers: true, @@ -455,6 +478,7 @@ export function updateCompositeVariant( tiers: mergedTiers, settings: settingsRef, port: existing.port, + ...(newTarget !== 'claude' && { target: newTarget }), }; saveCompositeVariantUnified(name, compositeConfig); @@ -468,6 +492,7 @@ export function updateCompositeVariant( tiers: mergedTiers, port: existing.port, settings: settingsRef, + target: newTarget, }, }; } catch (error) {