diff --git a/src/cliproxy/binary-manager.ts b/src/cliproxy/binary-manager.ts index 9ae8088f..84ec01e0 100644 --- a/src/cliproxy/binary-manager.ts +++ b/src/cliproxy/binary-manager.ts @@ -26,9 +26,10 @@ import { getVersionPinPath, readInstalledVersion, ensureBinary, + migrateVersionPin, } from './binary'; -type CLIProxyBackend = 'original' | 'plus'; +import type { CLIProxyBackend } from './types'; /** * Get backend from config or default to 'plus' @@ -111,7 +112,11 @@ export class BinaryManager { /** Convenience function respecting version pin */ export async function ensureCLIProxyBinary(verbose = false): Promise { const backend = getConfiguredBackend(); - const pinnedVersion = getPinnedVersion(); + + // Migrate old shared pin to backend-specific location (one-time migration) + migrateVersionPin(backend); + + const pinnedVersion = getPinnedVersion(backend); if (pinnedVersion) { if (verbose) console.error(`[cliproxy] Using pinned version: ${pinnedVersion}`); return new BinaryManager( @@ -127,27 +132,34 @@ export async function ensureCLIProxyBinary(verbose = false): Promise { } /** Check if CLIProxyAPI binary is installed */ -export function isCLIProxyInstalled(): boolean { - const backend = getConfiguredBackend(); - return new BinaryManager({}, backend).isBinaryInstalled(); +export function isCLIProxyInstalled(backend?: CLIProxyBackend): boolean { + const effectiveBackend = backend ?? getConfiguredBackend(); + return new BinaryManager({}, effectiveBackend).isBinaryInstalled(); } /** Get CLIProxyAPI binary path (may not exist) */ -export function getCLIProxyPath(): string { - const backend = getConfiguredBackend(); - return new BinaryManager({}, backend).getBinaryPath(); +export function getCLIProxyPath(backend?: CLIProxyBackend): string { + const effectiveBackend = backend ?? getConfiguredBackend(); + return new BinaryManager({}, effectiveBackend).getBinaryPath(); } /** Get installed CLIProxyAPI version from .version file */ -export function getInstalledCliproxyVersion(): string { - const backend = getConfiguredBackend(); - return readInstalledVersion(getBackendBinDir(backend), BACKEND_CONFIG[backend].fallbackVersion); +export function getInstalledCliproxyVersion(backend?: CLIProxyBackend): string { + const effectiveBackend = backend ?? getConfiguredBackend(); + return readInstalledVersion( + getBackendBinDir(effectiveBackend), + BACKEND_CONFIG[effectiveBackend].fallbackVersion + ); } /** Install a specific version of CLIProxyAPI */ -export async function installCliproxyVersion(version: string, verbose = false): Promise { - const backend = getConfiguredBackend(); - const manager = new BinaryManager({ version, verbose, forceVersion: true }, backend); +export async function installCliproxyVersion( + version: string, + verbose = false, + backend?: CLIProxyBackend +): Promise { + const effectiveBackend = backend ?? getConfiguredBackend(); + const manager = new BinaryManager({ version, verbose, forceVersion: true }, effectiveBackend); // Check if proxy is running and stop it first if (isProxyRunning()) { @@ -165,8 +177,11 @@ export async function installCliproxyVersion(version: string, verbose = false): } if (manager.isBinaryInstalled()) { + const label = effectiveBackend === 'plus' ? 'CLIProxy Plus' : 'CLIProxy'; if (verbose) - console.log(info(`Removing existing CLIProxy Plus v${getInstalledCliproxyVersion()}`)); + console.log( + info(`Removing existing ${label} v${getInstalledCliproxyVersion(effectiveBackend)}`) + ); manager.deleteBinary(); } await manager.ensureBinary(); @@ -223,6 +238,7 @@ export { savePinnedVersion, clearPinnedVersion, isVersionPinned, + migrateVersionPin, }; export default BinaryManager; diff --git a/src/cliproxy/binary/index.ts b/src/cliproxy/binary/index.ts index e0336258..23614d93 100644 --- a/src/cliproxy/binary/index.ts +++ b/src/cliproxy/binary/index.ts @@ -25,6 +25,7 @@ export { savePinnedVersion, clearPinnedVersion, isVersionPinned, + migrateVersionPin, } from './version-cache'; // Version Checker diff --git a/src/cliproxy/binary/version-cache.ts b/src/cliproxy/binary/version-cache.ts index 63a19a56..7d120afc 100644 --- a/src/cliproxy/binary/version-cache.ts +++ b/src/cliproxy/binary/version-cache.ts @@ -12,6 +12,8 @@ import { VERSION_PIN_FILE, VersionListCache, } from './types'; +import { DEFAULT_BACKEND } from '../platform-detector'; +import type { CLIProxyBackend } from '../types'; /** * Get path to version cache file @@ -21,10 +23,10 @@ export function getVersionCachePath(): string { } /** - * Get path to version pin file + * Get path to version pin file (backend-specific) */ -export function getVersionPinPath(): string { - return path.join(getBinDir(), VERSION_PIN_FILE); +export function getVersionPinPath(backend: CLIProxyBackend = DEFAULT_BACKEND): string { + return path.join(getBinDir(), backend, VERSION_PIN_FILE); } /** @@ -98,10 +100,10 @@ export function writeInstalledVersion(binPath: string, version: string): void { } /** - * Get pinned version if one exists + * Get pinned version if one exists (backend-specific) */ -export function getPinnedVersion(): string | null { - const pinPath = getVersionPinPath(); +export function getPinnedVersion(backend: CLIProxyBackend = DEFAULT_BACKEND): string | null { + const pinPath = getVersionPinPath(backend); if (!fs.existsSync(pinPath)) { return null; } @@ -113,10 +115,13 @@ export function getPinnedVersion(): string | null { } /** - * Save pinned version to persist user's explicit choice + * Save pinned version to persist user's explicit choice (backend-specific) */ -export function savePinnedVersion(version: string): void { - const pinPath = getVersionPinPath(); +export function savePinnedVersion( + version: string, + backend: CLIProxyBackend = DEFAULT_BACKEND +): void { + const pinPath = getVersionPinPath(backend); try { fs.mkdirSync(path.dirname(pinPath), { recursive: true }); fs.writeFileSync(pinPath, version, 'utf8'); @@ -126,10 +131,10 @@ export function savePinnedVersion(version: string): void { } /** - * Clear pinned version (unpin) + * Clear pinned version (unpin) - backend-specific */ -export function clearPinnedVersion(): void { - const pinPath = getVersionPinPath(); +export function clearPinnedVersion(backend: CLIProxyBackend = DEFAULT_BACKEND): void { + const pinPath = getVersionPinPath(backend); if (fs.existsSync(pinPath)) { try { fs.unlinkSync(pinPath); @@ -140,10 +145,32 @@ export function clearPinnedVersion(): void { } /** - * Check if a version is currently pinned + * Check if a version is currently pinned (backend-specific) */ -export function isVersionPinned(): boolean { - return getPinnedVersion() !== null; +export function isVersionPinned(backend: CLIProxyBackend = DEFAULT_BACKEND): boolean { + return getPinnedVersion(backend) !== null; +} + +/** + * Migrate old shared version pin to backend-specific location. + * Called once on first run after update. + */ +export function migrateVersionPin(backend: CLIProxyBackend): void { + const oldPinPath = path.join(getBinDir(), VERSION_PIN_FILE); + if (!fs.existsSync(oldPinPath)) return; + + try { + const oldVersion = fs.readFileSync(oldPinPath, 'utf8').trim(); + if (!oldVersion) return; + + // Save to new backend-specific location + savePinnedVersion(oldVersion, backend); + + // Delete old shared file + fs.unlinkSync(oldPinPath); + } catch { + // Silent fail - not critical + } } // ==================== Version List Cache ==================== diff --git a/src/cliproxy/services/binary-service.ts b/src/cliproxy/services/binary-service.ts index 7a2458f9..7202c3cb 100644 --- a/src/cliproxy/services/binary-service.ts +++ b/src/cliproxy/services/binary-service.ts @@ -58,10 +58,10 @@ export function getBinaryStatus(backend?: CLIProxyBackend): BinaryStatusResult { backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND; const backendConfig = BACKEND_CONFIG[effectiveBackend]; return { - installed: isCLIProxyInstalled(), - currentVersion: getInstalledCliproxyVersion(), - pinnedVersion: getPinnedVersion(), - binaryPath: getCLIProxyPath(), + installed: isCLIProxyInstalled(effectiveBackend), + currentVersion: getInstalledCliproxyVersion(effectiveBackend), + pinnedVersion: getPinnedVersion(effectiveBackend), + binaryPath: getCLIProxyPath(effectiveBackend), fallbackVersion: backendConfig.fallbackVersion, backend: effectiveBackend, }; @@ -100,7 +100,11 @@ export function isValidVersionFormat(version: string): boolean { /** * Install a specific version and pin it */ -export async function installVersion(version: string, verbose = false): Promise { +export async function installVersion( + version: string, + verbose = false, + backend?: CLIProxyBackend +): Promise { if (!isValidVersionFormat(version)) { return { success: false, @@ -109,9 +113,12 @@ export async function installVersion(version: string, verbose = false): Promise< }; } + const effectiveBackend = + backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND; + try { - await installCliproxyVersion(version, verbose); - savePinnedVersion(version); + await installCliproxyVersion(version, verbose, effectiveBackend); + savePinnedVersion(version, effectiveBackend); return { success: true, @@ -130,13 +137,19 @@ export async function installVersion(version: string, verbose = false): Promise< /** * Install latest version and clear any pin */ -export async function installLatest(verbose = false): Promise { +export async function installLatest( + verbose = false, + backend?: CLIProxyBackend +): Promise { + const effectiveBackend = + backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND; + try { const latestVersion = await fetchLatestCliproxyVersion(); - const currentVersion = getInstalledCliproxyVersion(); - const wasPinned = isVersionPinned(); + const currentVersion = getInstalledCliproxyVersion(effectiveBackend); + const wasPinned = isVersionPinned(effectiveBackend); - if (isCLIProxyInstalled() && latestVersion === currentVersion && !wasPinned) { + if (isCLIProxyInstalled(effectiveBackend) && latestVersion === currentVersion && !wasPinned) { return { success: true, version: latestVersion, @@ -144,8 +157,8 @@ export async function installLatest(verbose = false): Promise { }; } - await installCliproxyVersion(latestVersion, verbose); - clearPinnedVersion(); + await installCliproxyVersion(latestVersion, verbose, effectiveBackend); + clearPinnedVersion(effectiveBackend); return { success: true, diff --git a/src/commands/cliproxy-command.ts b/src/commands/cliproxy-command.ts index 30360803..6a08c32d 100644 --- a/src/commands/cliproxy-command.ts +++ b/src/commands/cliproxy-command.ts @@ -111,6 +111,13 @@ function getEffectiveBackend(cliBackend?: CLIProxyBackend): CLIProxyBackend { return config.cliproxy?.backend ?? DEFAULT_BACKEND; } +/** + * Get display label for backend + */ +function getBackendLabel(backend: CLIProxyBackend): string { + return backend === 'plus' ? 'CLIProxy Plus' : 'CLIProxy'; +} + interface CliproxyProfileArgs { name?: string; provider?: CLIProxyProfileName; @@ -152,8 +159,10 @@ function formatModelOption(model: ModelEntry): string { async function handleCreate(args: string[]): Promise { await initUI(); + const { backend } = parseBackendArg(args); + const effectiveBackend = getEffectiveBackend(backend); const parsedArgs = parseProfileArgs(args); - console.log(header('Create CLIProxy Plus Variant')); + console.log(header(`Create ${getBackendLabel(effectiveBackend)} Variant`)); console.log(''); // Step 1: Profile name @@ -292,7 +301,7 @@ async function handleCreate(args: string[]): Promise { // Create variant console.log(''); - console.log(info('Creating CLIProxy Plus variant...')); + console.log(info(`Creating ${getBackendLabel(effectiveBackend)} variant...`)); const result = createVariant(name, provider, model, account); if (!result.success) { @@ -530,14 +539,19 @@ async function showStatus(verbose: boolean, backend: CLIProxyBackend): Promise { - console.log(info(`Installing CLIProxy Plus v${version}...`)); +async function handleInstallVersion( + version: string, + verbose: boolean, + backend: CLIProxyBackend +): Promise { + const label = getBackendLabel(backend); + console.log(info(`Installing ${label} v${version}...`)); console.log(''); - const result = await installVersion(version, verbose); + const result = await installVersion(version, verbose, backend); if (!result.success) { console.error(''); - console.error(fail(`Failed to install CLIProxy Plus v${version}`)); + console.error(fail(`Failed to install ${label} v${version}`)); console.error(` ${result.error}`); console.error(''); console.error('Possible causes:'); @@ -546,12 +560,12 @@ async function handleInstallVersion(version: string, verbose: boolean): Promise< console.error(' 3. GitHub API rate limiting'); console.error(''); console.error('Check available versions at:'); - console.error(' https://github.com/router-for-me/CLIProxyAPIPlus/releases'); + console.error(` https://github.com/${BACKEND_CONFIG[backend].repo}/releases`); process.exit(1); } console.log(''); - console.log(ok(`CLIProxy Plus v${version} installed (pinned)`)); + console.log(ok(`${label} v${version} installed (pinned)`)); console.log(''); console.log(dim('This version will be used until you run:')); console.log( @@ -560,10 +574,11 @@ async function handleInstallVersion(version: string, verbose: boolean): Promise< console.log(''); } -async function handleInstallLatest(verbose: boolean): Promise { - console.log(info('Fetching latest CLIProxy Plus version...')); +async function handleInstallLatest(verbose: boolean, backend: CLIProxyBackend): Promise { + const label = getBackendLabel(backend); + console.log(info(`Fetching latest ${label} version...`)); - const result = await installLatest(verbose); + const result = await installLatest(verbose, backend); if (!result.success) { console.error(fail(`Failed to install latest version: ${result.error}`)); process.exit(1); @@ -575,7 +590,7 @@ async function handleInstallLatest(verbose: boolean): Promise { } console.log(''); - console.log(ok(`CLIProxy Plus updated to v${result.version}`)); + console.log(ok(`${label} updated to v${result.version}`)); console.log(dim('Auto-update is now enabled.')); console.log(''); } @@ -1036,12 +1051,12 @@ export async function handleCliproxyCommand(args: string[]): Promise { } // Strip leading 'v' prefix and whitespace (user may type " v6.6.80-0 ") version = version.trim().replace(/^v/, ''); - await handleInstallVersion(version, verbose); + await handleInstallVersion(version, verbose, effectiveBackend); return; } if (remainingArgs.includes('--latest') || remainingArgs.includes('--update')) { - await handleInstallLatest(verbose); + await handleInstallLatest(verbose, effectiveBackend); return; }