diff --git a/src/ccs.ts b/src/ccs.ts index 1d5a7414..f5cb7486 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -611,6 +611,7 @@ async function main(): Promise { isComposite: profileInfo.isComposite, compositeTiers: profileInfo.compositeTiers, compositeDefaultTier: profileInfo.compositeDefaultTier, + profileName: profileInfo.name, }); } else if (profileInfo.type === 'copilot') { // COPILOT FLOW: GitHub Copilot subscription via copilot-api proxy diff --git a/src/cliproxy/executor/index.ts b/src/cliproxy/executor/index.ts index c5dbde93..0c6630e4 100644 --- a/src/cliproxy/executor/index.ts +++ b/src/cliproxy/executor/index.ts @@ -434,10 +434,11 @@ export async function execClaudeWithCLIProxy( if (forceConfig && supportsModelConfig(provider)) { // Block --config for composite variants (per-tier models in config.yaml) if (cfg.isComposite) { + const variantName = cfg.profileName || provider; console.log( warn('Composite variants use per-tier config. Edit config.yaml to change tier models.') ); - console.error(` Use "ccs cliproxy edit ${provider}" to modify composite variants`); + console.error(` Use "ccs cliproxy edit ${variantName}" to modify composite variants`); process.exit(1); } else { await configureProviderModel(provider, true, cfg.customSettingsPath); diff --git a/src/cliproxy/types.ts b/src/cliproxy/types.ts index 06c83dc1..8db9142c 100644 --- a/src/cliproxy/types.ts +++ b/src/cliproxy/types.ts @@ -193,6 +193,8 @@ export interface ExecutorConfig { }; /** Composite variant: which tier is the default */ compositeDefaultTier?: 'opus' | 'sonnet' | 'haiku'; + /** Original profile/variant name (e.g., "my-mix" for composite variants) */ + profileName?: string; } /** diff --git a/ui/src/components/cliproxy/cliproxy-edit-dialog.tsx b/ui/src/components/cliproxy/cliproxy-edit-dialog.tsx index 9ac4a329..3197ebe1 100644 --- a/ui/src/components/cliproxy/cliproxy-edit-dialog.tsx +++ b/ui/src/components/cliproxy/cliproxy-edit-dialog.tsx @@ -90,16 +90,20 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit } else { singleForm.reset({ provider: variant.provider, - model: variant.model || '', - account: variant.account || '', + model: variant.model ?? undefined, + account: variant.account ?? undefined, }); } }, [variant, isComposite, singleForm, compositeForm]); const onSubmitSingle = async (data: SingleProviderFormData) => { if (!variant) return; + // Filter out undefined values - backend interprets undefined as "no change" + const payload = Object.fromEntries( + Object.entries(data).filter(([, v]) => v !== undefined && v !== '') + ) as SingleProviderFormData; try { - await updateMutation.mutateAsync({ name: variant.name, data }); + await updateMutation.mutateAsync({ name: variant.name, data: payload }); onOpenChange(false); } catch (error) { console.error('Failed to update variant:', error);