mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 08:17:11 +00:00
feat(cliproxy): persist target on variants
- add target to variant create/update flows for single and composite modes - persist target in unified config and legacy cliproxy entries - include target in variant list/remove return payloads
This commit is contained in:
@@ -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<number>();
|
||||
@@ -89,9 +73,6 @@ export function getNextAvailablePort(): number {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* List variants from config
|
||||
*/
|
||||
export function listVariantsFromConfig(): Record<string, VariantConfig> {
|
||||
try {
|
||||
if (isUnifiedMode()) {
|
||||
@@ -139,6 +120,7 @@ export function listVariantsFromConfig(): Record<string, VariantConfig> {
|
||||
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<string, VariantConfig> {
|
||||
settings: single.settings,
|
||||
account: single.account,
|
||||
port: single.port,
|
||||
target: single.target || 'claude',
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -171,12 +154,14 @@ export function listVariantsFromConfig(): Record<string, VariantConfig> {
|
||||
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<string, VariantConfig> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
@@ -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<Record<'opus' | 'sonnet' | 'haiku', CompositeTierConfig>>;
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user