From ac574e0bafaf5a7124857e7f38fe3c44f5d67a8a Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Thu, 12 Feb 2026 08:26:03 +0700 Subject: [PATCH] fix(cliproxy): address code review feedback (attempt 2/5) - Add tiers shape validation in variant-routes POST/PUT endpoints - Use process.exit(1) for blocked --config on composite variants --- src/cliproxy/executor/index.ts | 4 +++- src/web-server/routes/variant-routes.ts | 32 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/cliproxy/executor/index.ts b/src/cliproxy/executor/index.ts index 9b5da0ec..0f9502d7 100644 --- a/src/cliproxy/executor/index.ts +++ b/src/cliproxy/executor/index.ts @@ -432,10 +432,12 @@ export async function execClaudeWithCLIProxy( 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`); + process.exit(1); } else { await configureProviderModel(provider, true, cfg.customSettingsPath); + process.exit(0); } - process.exit(0); } // Handle --logout diff --git a/src/web-server/routes/variant-routes.ts b/src/web-server/routes/variant-routes.ts index e1efa7c7..9a96e810 100644 --- a/src/web-server/routes/variant-routes.ts +++ b/src/web-server/routes/variant-routes.ts @@ -75,6 +75,21 @@ router.post('/', (req: Request, res: Response): void => { return; } + // Validate tiers shape + const requiredTiers = ['opus', 'sonnet', 'haiku'] as const; + for (const tier of requiredTiers) { + if ( + !tiers[tier] || + typeof tiers[tier].provider !== 'string' || + typeof tiers[tier].model !== 'string' + ) { + res.status(400).json({ + error: `Invalid tier config for '${tier}': requires 'provider' and 'model' strings`, + }); + return; + } + } + const result = createCompositeVariant({ name, defaultTier: default_tier, tiers }); if (!result.success) { @@ -147,6 +162,23 @@ router.put('/:name', (req: Request, res: Response): void => { return; } + // Validate tiers shape if provided + if (tiers) { + const requiredTiers = ['opus', 'sonnet', 'haiku'] as const; + for (const tier of requiredTiers) { + if ( + !tiers[tier] || + typeof tiers[tier].provider !== 'string' || + typeof tiers[tier].model !== 'string' + ) { + res.status(400).json({ + error: `Invalid tier config for '${tier}': requires 'provider' and 'model' strings`, + }); + return; + } + } + } + const result = updateCompositeVariant(name, { defaultTier: default_tier, tiers }); if (!result.success) {