From 9a687fc03cf704a11e804780185ba24153766fd5 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Mon, 4 May 2026 11:39:47 -0400 Subject: [PATCH] fix: preserve CLIProxy platform arch compatibility --- src/cliproxy/binary/platform-detector.ts | 26 +++++++++++------ .../__tests__/backend-selection.test.js | 28 +++++++++++++++++++ .../__tests__/types-backward-compat.test.ts | 8 +++--- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/cliproxy/binary/platform-detector.ts b/src/cliproxy/binary/platform-detector.ts index 521d72b4..eeb7c9f4 100644 --- a/src/cliproxy/binary/platform-detector.ts +++ b/src/cliproxy/binary/platform-detector.ts @@ -2,7 +2,7 @@ * Platform Detector for CLIProxyAPI Binary Downloads * * Detects OS and architecture to determine correct binary asset. - * Supports 6 platforms: darwin/linux/windows x amd64/aarch64 + * Supports 6 platforms: darwin/linux/windows x amd64/arm64 */ import { @@ -64,9 +64,7 @@ export const CLIPROXY_FAULTY_RANGE = { min: '6.6.81-0', max: '6.6.88-0' }; /** @deprecated Use CLIPROXY_FALLBACK_VERSION instead */ export const CLIPROXY_VERSION = CLIPROXY_FALLBACK_VERSION; -/** - * Platform mapping from Node.js values to CLIProxyAPI naming - */ +/** Platform mapping from Node.js values to CCS public architecture labels. */ const OS_MAP: Record = { darwin: 'darwin', linux: 'linux', @@ -75,11 +73,19 @@ const OS_MAP: Record = { const ARCH_MAP: Record = { x64: 'amd64', + arm64: 'arm64', +}; + +/** CLIProxy release assets use aarch64 while Node.js reports arm64. */ +const RELEASE_ARCH_MAP: Record = { + amd64: 'amd64', arm64: 'aarch64', + aarch64: 'aarch64', }; export function mapNodeArchToReleaseArch(nodeArch: string): SupportedArch | undefined { - return ARCH_MAP[nodeArch]; + const arch = ARCH_MAP[nodeArch]; + return arch ? RELEASE_ARCH_MAP[arch] : undefined; } /** @@ -96,7 +102,8 @@ export function detectPlatform( const nodeArch = process.arch; const os = OS_MAP[nodePlatform]; - const arch = mapNodeArchToReleaseArch(nodeArch); + const arch = ARCH_MAP[nodeArch]; + const releaseArch = mapNodeArchToReleaseArch(nodeArch); if (!os) { throw new Error( @@ -105,7 +112,7 @@ export function detectPlatform( ); } - if (!arch) { + if (!arch || !releaseArch) { throw new Error( `Unsupported CPU architecture: ${nodeArch}\n` + `Supported: x64 (amd64), arm64 (aarch64)` ); @@ -114,7 +121,7 @@ export function detectPlatform( const config = BACKEND_CONFIG[backend]; const ver = version || config.fallbackVersion; const extension: ArchiveExtension = os === 'windows' ? 'zip' : 'tar.gz'; - const binaryName = `${config.binaryPrefix}_${ver}_${os}_${arch}.${extension}`; + const binaryName = `${config.binaryPrefix}_${ver}_${os}_${releaseArch}.${extension}`; return { os, @@ -206,7 +213,8 @@ export function getPlatformDescription(): string { const platform = detectPlatform(); const osName = platform.os === 'darwin' ? 'macOS' : platform.os === 'linux' ? 'Linux' : 'Windows'; - const archName = platform.arch === 'aarch64' ? 'ARM64' : platform.arch; + const archName = + platform.arch === 'arm64' || platform.arch === 'aarch64' ? 'ARM64' : platform.arch; return `${osName} ${archName}`; } catch { return `${process.platform} ${process.arch} (unsupported)`; diff --git a/src/cliproxy/config/__tests__/backend-selection.test.js b/src/cliproxy/config/__tests__/backend-selection.test.js index a8bcb4b3..b4d1ca11 100644 --- a/src/cliproxy/config/__tests__/backend-selection.test.js +++ b/src/cliproxy/config/__tests__/backend-selection.test.js @@ -9,6 +9,21 @@ describe('Backend Selection', () => { const platformDetector = require('../../../../dist/cliproxy/binary/platform-detector'); const types = require('../../../../dist/cliproxy/types'); + function withMockedProcessPlatform(platform, arch, callback) { + const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform'); + const originalArch = Object.getOwnPropertyDescriptor(process, 'arch'); + + Object.defineProperty(process, 'platform', { value: platform, configurable: true }); + Object.defineProperty(process, 'arch', { value: arch, configurable: true }); + + try { + callback(); + } finally { + Object.defineProperty(process, 'platform', originalPlatform); + Object.defineProperty(process, 'arch', originalArch); + } + } + describe('BACKEND_CONFIG', () => { it('has correct configuration for original backend', () => { const config = platformDetector.BACKEND_CONFIG.original; @@ -39,6 +54,19 @@ describe('Backend Selection', () => { assert.strictEqual(platformDetector.mapNodeArchToReleaseArch('x64'), 'amd64'); }); + it('keeps public ARM64 arch compatibility while using aarch64 release assets', () => { + withMockedProcessPlatform('darwin', 'arm64', () => { + const info = platformDetector.detectPlatform('6.10.4', 'original'); + + assert.strictEqual(info.arch, 'arm64'); + assert.strictEqual(info.binaryName, 'CLIProxyAPI_6.10.4_darwin_aarch64.tar.gz'); + assert.strictEqual( + platformDetector.getDownloadUrl('6.10.4', 'original'), + 'https://github.com/router-for-me/CLIProxyAPI/releases/download/v6.10.4/CLIProxyAPI_6.10.4_darwin_aarch64.tar.gz' + ); + }); + }); + it('generates correct binary name for original backend', () => { const info = platformDetector.detectPlatform('6.6.51', 'original'); assert(info.binaryName.startsWith('CLIProxyAPI_6.6.51_')); diff --git a/src/cliproxy/types/__tests__/types-backward-compat.test.ts b/src/cliproxy/types/__tests__/types-backward-compat.test.ts index 1710639a..95952fb7 100644 --- a/src/cliproxy/types/__tests__/types-backward-compat.test.ts +++ b/src/cliproxy/types/__tests__/types-backward-compat.test.ts @@ -34,8 +34,8 @@ import { PLUS_ONLY_PROVIDERS } from '../../types'; describe('types.ts backward compatibility', () => { it('exports all platform types', () => { const os: SupportedOS = 'darwin'; - const arch: SupportedArch = 'aarch64'; - const nodeArch: SupportedArch = 'arm64'; + const arch: SupportedArch = 'arm64'; + const releaseArch: SupportedArch = 'aarch64'; const ext: ArchiveExtension = 'tar.gz'; const info: PlatformInfo = { os, @@ -44,8 +44,8 @@ describe('types.ts backward compatibility', () => { extension: ext, }; expect(info.os).toBe('darwin'); - expect(info.arch).toBe('aarch64'); - expect(nodeArch).toBe('arm64'); + expect(info.arch).toBe('arm64'); + expect(releaseArch).toBe('aarch64'); }); it('exports all binary types', () => {