From 8e6b67bf99204f4fe187ba59e739491d4a2ab193 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 12 Feb 2026 12:56:57 +0700 Subject: [PATCH] fix(cliproxy): fix edit dialog empty model and composite guidance - P2: Edit dialog no longer seeds empty model string Changed `variant.model || ''` to `variant.model ?? undefined` Added payload filtering to exclude undefined/empty values before API call Prevents unintended model overwrites when user only changes other fields - P3: Composite --config guidance now uses variant name Added profileName to ExecutorConfig interface Error message now shows correct variant name instead of default tier provider e.g., "ccs cliproxy edit my-mix" instead of "ccs cliproxy edit gemini" --- src/ccs.ts | 1 + src/cliproxy/executor/index.ts | 3 ++- src/cliproxy/types.ts | 2 ++ ui/src/components/cliproxy/cliproxy-edit-dialog.tsx | 10 +++++++--- 4 files changed, 12 insertions(+), 4 deletions(-) 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);