diff --git a/src/cliproxy/binary/lifecycle.ts b/src/cliproxy/binary/lifecycle.ts index effbfda5..51867a4e 100644 --- a/src/cliproxy/binary/lifecycle.ts +++ b/src/cliproxy/binary/lifecycle.ts @@ -7,7 +7,7 @@ import * as fs from 'fs'; import { BinaryManagerConfig } from '../types'; import { checkForUpdates, fetchLatestVersion, isNewerVersion } from './version-checker'; import { downloadAndInstall, deleteBinary, getBinaryPath } from './installer'; -import { info } from '../../utils/ui'; +import { info, warn } from '../../utils/ui'; import { isCliproxyRunning } from '../stats-fetcher'; import { CLIPROXY_DEFAULT_PORT } from '../config-generator'; import { CLIPROXY_MAX_STABLE_VERSION } from '../platform-detector'; @@ -18,10 +18,22 @@ function log(message: string, verbose: boolean): void { } /** - * Clamp version to max stable if newer versions are unstable + * Check if version is above max stable (known unstable) */ -function clampToMaxStable(version: string, verbose: boolean): string { - if (isNewerVersion(version, CLIPROXY_MAX_STABLE_VERSION)) { +function isAboveMaxStable(version: string): boolean { + return isNewerVersion(version, CLIPROXY_MAX_STABLE_VERSION); +} + +/** + * Clamp version to max stable if newer versions are unstable + * Returns max stable version if input is empty/invalid + */ +function clampToMaxStable(version: string | undefined, verbose: boolean): string { + if (!version) { + log(`Empty version, using max stable ${CLIPROXY_MAX_STABLE_VERSION}`, verbose); + return CLIPROXY_MAX_STABLE_VERSION; + } + if (isAboveMaxStable(version)) { log(`Clamping ${version} to max stable ${CLIPROXY_MAX_STABLE_VERSION}`, verbose); return CLIPROXY_MAX_STABLE_VERSION; } @@ -31,17 +43,32 @@ function clampToMaxStable(version: string, verbose: boolean): string { /** Handle auto-update when binary exists */ async function handleAutoUpdate(config: BinaryManagerConfig, verbose: boolean): Promise { const updateResult = await checkForUpdates(config.binPath, config.version, verbose); + const currentVersion = updateResult.currentVersion; + const latestVersion = updateResult.latestVersion; + + // Check if user is on known unstable version - inform but don't force downgrade + if (isAboveMaxStable(currentVersion)) { + console.log( + warn( + `CLIProxy Plus v${currentVersion} has known stability issues. ` + + `Stable version: v${CLIPROXY_MAX_STABLE_VERSION}` + ) + ); + console.log(info('Run "ccs cliproxy install 80" to downgrade, or wait for upstream fix')); + } + if (!updateResult.hasUpdate) return; // Clamp to max stable version - const targetVersion = clampToMaxStable(updateResult.latestVersion, verbose); - if (!isNewerVersion(targetVersion, updateResult.currentVersion)) { - log(`Already at max stable version ${updateResult.currentVersion}`, verbose); + const targetVersion = clampToMaxStable(latestVersion, verbose); + if (!isNewerVersion(targetVersion, currentVersion)) { + log(`Already at max stable version ${currentVersion}`, verbose); return; } const proxyRunning = await isCliproxyRunning(CLIPROXY_DEFAULT_PORT); - const updateMsg = `CLIProxy Plus update available: v${updateResult.currentVersion} -> v${targetVersion}`; + const latestNote = isAboveMaxStable(latestVersion) ? ` (latest v${latestVersion} unstable)` : ''; + const updateMsg = `CLIProxy Plus update: v${currentVersion} -> v${targetVersion}${latestNote}`; if (proxyRunning) { console.log(info(updateMsg)); @@ -95,7 +122,10 @@ export async function ensureBinary(config: BinaryManagerConfig): Promise config.version = targetVersion; } } catch { - log(`Using pinned version: ${config.version}`, verbose); + // API failed - use fallback but still clamp to max stable + const fallbackVersion = clampToMaxStable(config.version, verbose); + config.version = fallbackVersion; + log(`Using fallback version: ${fallbackVersion}`, verbose); } } else { log(`Force version mode: using specified version ${config.version}`, verbose);