Files
ccs/src/cliproxy/services/binary-service.ts
T
Tam Nhu Tran 4f6e61739c refactor(config): adopt config-loader-facade across the codebase
Issue #1161. Sweeps 127 files to import from
src/config/config-loader-facade.ts instead of unified-config-loader or
utils/config-manager directly.

WRITE callers (32 files): replaced raw saveUnifiedConfig /
mutateUnifiedConfig / updateUnifiedConfig calls with the facade's
cache-coherent wrappers saveConfig / mutateConfig / updateConfig. This
fixes a latent stale-cache window where direct writes through the
underlying loader bypassed the facade's memoization.

READ callers (95 files): mechanical import-path migration only —
function names unchanged because the facade re-exports them. No
behavior change.

Also updated:
- tests/unit/utils/browser/browser-setup.test.ts (DI interface rename)
- src/management/checks/image-analysis-check.ts (dynamic import rename)
- src/web-server/health-service.ts (dynamic require rename)
- src/ccs.ts (path prefix fix from sweep script)

After sweep: zero raw write callers remain outside src/config/. Direct
imports of config-manager remain only for symbols not in the facade
(getConfigPath, getCcsDirSource, etc). Behavior unchanged; full suite
passes 1824/1824.

Out of scope: switching loadOrCreateUnifiedConfig() callers to
getCachedConfig() — needs per-callsite cache-safety analysis. Tracked
as follow-up.

Refs #1161
2026-05-03 01:42:53 -04:00

217 lines
6.1 KiB
TypeScript

/**
* CLIProxy Binary Service
*
* Handles CLIProxyAPI binary version management:
* - Get current version status
* - Install specific versions
* - Install latest version
* - Version pinning
*/
import {
getInstalledCliproxyVersion,
installCliproxyVersion,
fetchLatestCliproxyVersion,
isCLIProxyInstalled,
getCLIProxyPath,
getPinnedVersion,
savePinnedVersion,
clearPinnedVersion,
isVersionPinned,
resolveLocalBackend,
} from '../binary-manager';
import { BACKEND_CONFIG, DEFAULT_BACKEND } from '../binary/platform-detector';
import { CLIProxyBackend } from '../types';
import { loadOrCreateUnifiedConfig } from '../../config/config-loader-facade';
/** Binary status result */
export interface BinaryStatusResult {
installed: boolean;
currentVersion: string | null;
pinnedVersion: string | null;
binaryPath: string;
fallbackVersion: string;
backend: CLIProxyBackend;
}
/** Install result */
export interface InstallResult {
success: boolean;
version: string;
error?: string;
wasPinned?: boolean;
}
/** Latest version check result */
export interface LatestVersionResult {
success: boolean;
latestVersion?: string;
currentVersion?: string;
updateAvailable?: boolean;
error?: string;
}
/**
* Get current binary status for a specific backend
*/
export function getBinaryStatus(backend?: CLIProxyBackend): BinaryStatusResult {
const configuredBackend =
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
const effectiveBackend = resolveLocalBackend(configuredBackend, { notifyOnPlus: true });
const backendConfig = BACKEND_CONFIG[effectiveBackend];
return {
installed: isCLIProxyInstalled(effectiveBackend),
currentVersion: getInstalledCliproxyVersion(effectiveBackend),
pinnedVersion: getPinnedVersion(effectiveBackend),
binaryPath: getCLIProxyPath(effectiveBackend),
fallbackVersion: backendConfig.fallbackVersion,
backend: effectiveBackend,
};
}
/**
* Check for latest version
*/
export async function checkLatestVersion(backend?: CLIProxyBackend): Promise<LatestVersionResult> {
const configuredBackend =
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
const effectiveBackend = resolveLocalBackend(configuredBackend, { notifyOnPlus: true });
try {
// Use checkCliproxyUpdate which is backend-aware (uses correct GitHub repo)
const { checkCliproxyUpdate } = await import('../binary-manager');
const updateResult = await checkCliproxyUpdate(effectiveBackend);
const latestVersion = updateResult.latestVersion;
const currentVersion = getInstalledCliproxyVersion(effectiveBackend);
const updateAvailable = latestVersion !== currentVersion;
return {
success: true,
latestVersion,
currentVersion: currentVersion || undefined,
updateAvailable,
};
} catch (error) {
return {
success: false,
error: (error as Error).message,
};
}
}
/**
* Validate version format (supports X.Y.Z or X.Y.Z-N suffix)
*/
export function isValidVersionFormat(version: string): boolean {
return /^\d+\.\d+\.\d+(-\d+)?$/.test(version);
}
/**
* Install a specific version and pin it
*/
export async function installVersion(
version: string,
verbose = false,
backend?: CLIProxyBackend
): Promise<InstallResult> {
if (!isValidVersionFormat(version)) {
return {
success: false,
version,
error: 'Invalid version format. Expected format: X.Y.Z (e.g., 6.5.53)',
};
}
const configuredBackend =
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
const effectiveBackend = resolveLocalBackend(configuredBackend, { notifyOnPlus: true });
try {
await installCliproxyVersion(version, verbose, effectiveBackend);
savePinnedVersion(version, effectiveBackend);
return {
success: true,
version,
wasPinned: true,
};
} catch (error) {
return {
success: false,
version,
error: (error as Error).message,
};
}
}
/**
* Install latest version and clear any pin
*/
export async function installLatest(
verbose = false,
backend?: CLIProxyBackend
): Promise<InstallResult> {
const configuredBackend =
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
const effectiveBackend = resolveLocalBackend(configuredBackend, { notifyOnPlus: true });
try {
const latestVersion = await fetchLatestCliproxyVersion(effectiveBackend);
const currentVersion = getInstalledCliproxyVersion(effectiveBackend);
const wasPinned = isVersionPinned(effectiveBackend);
if (isCLIProxyInstalled(effectiveBackend) && latestVersion === currentVersion && !wasPinned) {
return {
success: true,
version: latestVersion,
error: `Already running latest version: v${latestVersion}`,
};
}
await installCliproxyVersion(latestVersion, verbose, effectiveBackend);
clearPinnedVersion(effectiveBackend);
return {
success: true,
version: latestVersion,
wasPinned,
};
} catch (error) {
return {
success: false,
version: '',
error: (error as Error).message,
};
}
}
/**
* Check if a version is pinned
*/
export function isPinned(backend?: CLIProxyBackend): boolean {
const configuredBackend =
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
const effectiveBackend = resolveLocalBackend(configuredBackend, { notifyOnPlus: true });
return isVersionPinned(effectiveBackend);
}
/**
* Get pinned version if any
*/
export function getPinned(backend?: CLIProxyBackend): string | null {
const configuredBackend =
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
const effectiveBackend = resolveLocalBackend(configuredBackend, { notifyOnPlus: true });
return getPinnedVersion(effectiveBackend);
}
/**
* Clear version pin
*/
export function clearPin(backend?: CLIProxyBackend): void {
const configuredBackend =
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
const effectiveBackend = resolveLocalBackend(configuredBackend, { notifyOnPlus: true });
clearPinnedVersion(effectiveBackend);
}