mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-03 04:17:54 +00:00
fix(cliproxy): make backend switching work with version pins and status
- Add backend param to isCLIProxyInstalled(), getCLIProxyPath(), getInstalledCliproxyVersion(), installCliproxyVersion() - Update getBinaryStatus() to pass backend to all helper functions - Add getBackendLabel() helper for dynamic CLI messages - Replace hardcoded "CLIProxy Plus" strings with dynamic labels - Pass --backend flag through install/update command handlers - Import CLIProxyBackend type from types.ts instead of redefining Setting `cliproxy.backend: original` in config.yaml now correctly uses the original backend for version pins and binary operations.
This commit is contained in:
@@ -26,9 +26,10 @@ import {
|
|||||||
getVersionPinPath,
|
getVersionPinPath,
|
||||||
readInstalledVersion,
|
readInstalledVersion,
|
||||||
ensureBinary,
|
ensureBinary,
|
||||||
|
migrateVersionPin,
|
||||||
} from './binary';
|
} from './binary';
|
||||||
|
|
||||||
type CLIProxyBackend = 'original' | 'plus';
|
import type { CLIProxyBackend } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get backend from config or default to 'plus'
|
* Get backend from config or default to 'plus'
|
||||||
@@ -111,7 +112,11 @@ export class BinaryManager {
|
|||||||
/** Convenience function respecting version pin */
|
/** Convenience function respecting version pin */
|
||||||
export async function ensureCLIProxyBinary(verbose = false): Promise<string> {
|
export async function ensureCLIProxyBinary(verbose = false): Promise<string> {
|
||||||
const backend = getConfiguredBackend();
|
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 (pinnedVersion) {
|
||||||
if (verbose) console.error(`[cliproxy] Using pinned version: ${pinnedVersion}`);
|
if (verbose) console.error(`[cliproxy] Using pinned version: ${pinnedVersion}`);
|
||||||
return new BinaryManager(
|
return new BinaryManager(
|
||||||
@@ -127,27 +132,34 @@ export async function ensureCLIProxyBinary(verbose = false): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Check if CLIProxyAPI binary is installed */
|
/** Check if CLIProxyAPI binary is installed */
|
||||||
export function isCLIProxyInstalled(): boolean {
|
export function isCLIProxyInstalled(backend?: CLIProxyBackend): boolean {
|
||||||
const backend = getConfiguredBackend();
|
const effectiveBackend = backend ?? getConfiguredBackend();
|
||||||
return new BinaryManager({}, backend).isBinaryInstalled();
|
return new BinaryManager({}, effectiveBackend).isBinaryInstalled();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get CLIProxyAPI binary path (may not exist) */
|
/** Get CLIProxyAPI binary path (may not exist) */
|
||||||
export function getCLIProxyPath(): string {
|
export function getCLIProxyPath(backend?: CLIProxyBackend): string {
|
||||||
const backend = getConfiguredBackend();
|
const effectiveBackend = backend ?? getConfiguredBackend();
|
||||||
return new BinaryManager({}, backend).getBinaryPath();
|
return new BinaryManager({}, effectiveBackend).getBinaryPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get installed CLIProxyAPI version from .version file */
|
/** Get installed CLIProxyAPI version from .version file */
|
||||||
export function getInstalledCliproxyVersion(): string {
|
export function getInstalledCliproxyVersion(backend?: CLIProxyBackend): string {
|
||||||
const backend = getConfiguredBackend();
|
const effectiveBackend = backend ?? getConfiguredBackend();
|
||||||
return readInstalledVersion(getBackendBinDir(backend), BACKEND_CONFIG[backend].fallbackVersion);
|
return readInstalledVersion(
|
||||||
|
getBackendBinDir(effectiveBackend),
|
||||||
|
BACKEND_CONFIG[effectiveBackend].fallbackVersion
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Install a specific version of CLIProxyAPI */
|
/** Install a specific version of CLIProxyAPI */
|
||||||
export async function installCliproxyVersion(version: string, verbose = false): Promise<void> {
|
export async function installCliproxyVersion(
|
||||||
const backend = getConfiguredBackend();
|
version: string,
|
||||||
const manager = new BinaryManager({ version, verbose, forceVersion: true }, backend);
|
verbose = false,
|
||||||
|
backend?: CLIProxyBackend
|
||||||
|
): Promise<void> {
|
||||||
|
const effectiveBackend = backend ?? getConfiguredBackend();
|
||||||
|
const manager = new BinaryManager({ version, verbose, forceVersion: true }, effectiveBackend);
|
||||||
|
|
||||||
// Check if proxy is running and stop it first
|
// Check if proxy is running and stop it first
|
||||||
if (isProxyRunning()) {
|
if (isProxyRunning()) {
|
||||||
@@ -165,8 +177,11 @@ export async function installCliproxyVersion(version: string, verbose = false):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (manager.isBinaryInstalled()) {
|
if (manager.isBinaryInstalled()) {
|
||||||
|
const label = effectiveBackend === 'plus' ? 'CLIProxy Plus' : 'CLIProxy';
|
||||||
if (verbose)
|
if (verbose)
|
||||||
console.log(info(`Removing existing CLIProxy Plus v${getInstalledCliproxyVersion()}`));
|
console.log(
|
||||||
|
info(`Removing existing ${label} v${getInstalledCliproxyVersion(effectiveBackend)}`)
|
||||||
|
);
|
||||||
manager.deleteBinary();
|
manager.deleteBinary();
|
||||||
}
|
}
|
||||||
await manager.ensureBinary();
|
await manager.ensureBinary();
|
||||||
@@ -223,6 +238,7 @@ export {
|
|||||||
savePinnedVersion,
|
savePinnedVersion,
|
||||||
clearPinnedVersion,
|
clearPinnedVersion,
|
||||||
isVersionPinned,
|
isVersionPinned,
|
||||||
|
migrateVersionPin,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BinaryManager;
|
export default BinaryManager;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export {
|
|||||||
savePinnedVersion,
|
savePinnedVersion,
|
||||||
clearPinnedVersion,
|
clearPinnedVersion,
|
||||||
isVersionPinned,
|
isVersionPinned,
|
||||||
|
migrateVersionPin,
|
||||||
} from './version-cache';
|
} from './version-cache';
|
||||||
|
|
||||||
// Version Checker
|
// Version Checker
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import {
|
|||||||
VERSION_PIN_FILE,
|
VERSION_PIN_FILE,
|
||||||
VersionListCache,
|
VersionListCache,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import { DEFAULT_BACKEND } from '../platform-detector';
|
||||||
|
import type { CLIProxyBackend } from '../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get path to version cache file
|
* 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 {
|
export function getVersionPinPath(backend: CLIProxyBackend = DEFAULT_BACKEND): string {
|
||||||
return path.join(getBinDir(), VERSION_PIN_FILE);
|
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 {
|
export function getPinnedVersion(backend: CLIProxyBackend = DEFAULT_BACKEND): string | null {
|
||||||
const pinPath = getVersionPinPath();
|
const pinPath = getVersionPinPath(backend);
|
||||||
if (!fs.existsSync(pinPath)) {
|
if (!fs.existsSync(pinPath)) {
|
||||||
return null;
|
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 {
|
export function savePinnedVersion(
|
||||||
const pinPath = getVersionPinPath();
|
version: string,
|
||||||
|
backend: CLIProxyBackend = DEFAULT_BACKEND
|
||||||
|
): void {
|
||||||
|
const pinPath = getVersionPinPath(backend);
|
||||||
try {
|
try {
|
||||||
fs.mkdirSync(path.dirname(pinPath), { recursive: true });
|
fs.mkdirSync(path.dirname(pinPath), { recursive: true });
|
||||||
fs.writeFileSync(pinPath, version, 'utf8');
|
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 {
|
export function clearPinnedVersion(backend: CLIProxyBackend = DEFAULT_BACKEND): void {
|
||||||
const pinPath = getVersionPinPath();
|
const pinPath = getVersionPinPath(backend);
|
||||||
if (fs.existsSync(pinPath)) {
|
if (fs.existsSync(pinPath)) {
|
||||||
try {
|
try {
|
||||||
fs.unlinkSync(pinPath);
|
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 {
|
export function isVersionPinned(backend: CLIProxyBackend = DEFAULT_BACKEND): boolean {
|
||||||
return getPinnedVersion() !== null;
|
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 ====================
|
// ==================== Version List Cache ====================
|
||||||
|
|||||||
@@ -58,10 +58,10 @@ export function getBinaryStatus(backend?: CLIProxyBackend): BinaryStatusResult {
|
|||||||
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
|
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
|
||||||
const backendConfig = BACKEND_CONFIG[effectiveBackend];
|
const backendConfig = BACKEND_CONFIG[effectiveBackend];
|
||||||
return {
|
return {
|
||||||
installed: isCLIProxyInstalled(),
|
installed: isCLIProxyInstalled(effectiveBackend),
|
||||||
currentVersion: getInstalledCliproxyVersion(),
|
currentVersion: getInstalledCliproxyVersion(effectiveBackend),
|
||||||
pinnedVersion: getPinnedVersion(),
|
pinnedVersion: getPinnedVersion(effectiveBackend),
|
||||||
binaryPath: getCLIProxyPath(),
|
binaryPath: getCLIProxyPath(effectiveBackend),
|
||||||
fallbackVersion: backendConfig.fallbackVersion,
|
fallbackVersion: backendConfig.fallbackVersion,
|
||||||
backend: effectiveBackend,
|
backend: effectiveBackend,
|
||||||
};
|
};
|
||||||
@@ -100,7 +100,11 @@ export function isValidVersionFormat(version: string): boolean {
|
|||||||
/**
|
/**
|
||||||
* Install a specific version and pin it
|
* Install a specific version and pin it
|
||||||
*/
|
*/
|
||||||
export async function installVersion(version: string, verbose = false): Promise<InstallResult> {
|
export async function installVersion(
|
||||||
|
version: string,
|
||||||
|
verbose = false,
|
||||||
|
backend?: CLIProxyBackend
|
||||||
|
): Promise<InstallResult> {
|
||||||
if (!isValidVersionFormat(version)) {
|
if (!isValidVersionFormat(version)) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -109,9 +113,12 @@ export async function installVersion(version: string, verbose = false): Promise<
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const effectiveBackend =
|
||||||
|
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await installCliproxyVersion(version, verbose);
|
await installCliproxyVersion(version, verbose, effectiveBackend);
|
||||||
savePinnedVersion(version);
|
savePinnedVersion(version, effectiveBackend);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
@@ -130,13 +137,19 @@ export async function installVersion(version: string, verbose = false): Promise<
|
|||||||
/**
|
/**
|
||||||
* Install latest version and clear any pin
|
* Install latest version and clear any pin
|
||||||
*/
|
*/
|
||||||
export async function installLatest(verbose = false): Promise<InstallResult> {
|
export async function installLatest(
|
||||||
|
verbose = false,
|
||||||
|
backend?: CLIProxyBackend
|
||||||
|
): Promise<InstallResult> {
|
||||||
|
const effectiveBackend =
|
||||||
|
backend ?? loadOrCreateUnifiedConfig().cliproxy?.backend ?? DEFAULT_BACKEND;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const latestVersion = await fetchLatestCliproxyVersion();
|
const latestVersion = await fetchLatestCliproxyVersion();
|
||||||
const currentVersion = getInstalledCliproxyVersion();
|
const currentVersion = getInstalledCliproxyVersion(effectiveBackend);
|
||||||
const wasPinned = isVersionPinned();
|
const wasPinned = isVersionPinned(effectiveBackend);
|
||||||
|
|
||||||
if (isCLIProxyInstalled() && latestVersion === currentVersion && !wasPinned) {
|
if (isCLIProxyInstalled(effectiveBackend) && latestVersion === currentVersion && !wasPinned) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
version: latestVersion,
|
version: latestVersion,
|
||||||
@@ -144,8 +157,8 @@ export async function installLatest(verbose = false): Promise<InstallResult> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
await installCliproxyVersion(latestVersion, verbose);
|
await installCliproxyVersion(latestVersion, verbose, effectiveBackend);
|
||||||
clearPinnedVersion();
|
clearPinnedVersion(effectiveBackend);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -111,6 +111,13 @@ function getEffectiveBackend(cliBackend?: CLIProxyBackend): CLIProxyBackend {
|
|||||||
return config.cliproxy?.backend ?? DEFAULT_BACKEND;
|
return config.cliproxy?.backend ?? DEFAULT_BACKEND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get display label for backend
|
||||||
|
*/
|
||||||
|
function getBackendLabel(backend: CLIProxyBackend): string {
|
||||||
|
return backend === 'plus' ? 'CLIProxy Plus' : 'CLIProxy';
|
||||||
|
}
|
||||||
|
|
||||||
interface CliproxyProfileArgs {
|
interface CliproxyProfileArgs {
|
||||||
name?: string;
|
name?: string;
|
||||||
provider?: CLIProxyProfileName;
|
provider?: CLIProxyProfileName;
|
||||||
@@ -152,8 +159,10 @@ function formatModelOption(model: ModelEntry): string {
|
|||||||
|
|
||||||
async function handleCreate(args: string[]): Promise<void> {
|
async function handleCreate(args: string[]): Promise<void> {
|
||||||
await initUI();
|
await initUI();
|
||||||
|
const { backend } = parseBackendArg(args);
|
||||||
|
const effectiveBackend = getEffectiveBackend(backend);
|
||||||
const parsedArgs = parseProfileArgs(args);
|
const parsedArgs = parseProfileArgs(args);
|
||||||
console.log(header('Create CLIProxy Plus Variant'));
|
console.log(header(`Create ${getBackendLabel(effectiveBackend)} Variant`));
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
// Step 1: Profile name
|
// Step 1: Profile name
|
||||||
@@ -292,7 +301,7 @@ async function handleCreate(args: string[]): Promise<void> {
|
|||||||
|
|
||||||
// Create variant
|
// Create variant
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log(info('Creating CLIProxy Plus variant...'));
|
console.log(info(`Creating ${getBackendLabel(effectiveBackend)} variant...`));
|
||||||
const result = createVariant(name, provider, model, account);
|
const result = createVariant(name, provider, model, account);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
@@ -530,14 +539,19 @@ async function showStatus(verbose: boolean, backend: CLIProxyBackend): Promise<v
|
|||||||
console.log('');
|
console.log('');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleInstallVersion(version: string, verbose: boolean): Promise<void> {
|
async function handleInstallVersion(
|
||||||
console.log(info(`Installing CLIProxy Plus v${version}...`));
|
version: string,
|
||||||
|
verbose: boolean,
|
||||||
|
backend: CLIProxyBackend
|
||||||
|
): Promise<void> {
|
||||||
|
const label = getBackendLabel(backend);
|
||||||
|
console.log(info(`Installing ${label} v${version}...`));
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
const result = await installVersion(version, verbose);
|
const result = await installVersion(version, verbose, backend);
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
console.error('');
|
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(` ${result.error}`);
|
||||||
console.error('');
|
console.error('');
|
||||||
console.error('Possible causes:');
|
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(' 3. GitHub API rate limiting');
|
||||||
console.error('');
|
console.error('');
|
||||||
console.error('Check available versions at:');
|
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);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log(ok(`CLIProxy Plus v${version} installed (pinned)`));
|
console.log(ok(`${label} v${version} installed (pinned)`));
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log(dim('This version will be used until you run:'));
|
console.log(dim('This version will be used until you run:'));
|
||||||
console.log(
|
console.log(
|
||||||
@@ -560,10 +574,11 @@ async function handleInstallVersion(version: string, verbose: boolean): Promise<
|
|||||||
console.log('');
|
console.log('');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleInstallLatest(verbose: boolean): Promise<void> {
|
async function handleInstallLatest(verbose: boolean, backend: CLIProxyBackend): Promise<void> {
|
||||||
console.log(info('Fetching latest CLIProxy Plus version...'));
|
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) {
|
if (!result.success) {
|
||||||
console.error(fail(`Failed to install latest version: ${result.error}`));
|
console.error(fail(`Failed to install latest version: ${result.error}`));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -575,7 +590,7 @@ async function handleInstallLatest(verbose: boolean): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log('');
|
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(dim('Auto-update is now enabled.'));
|
||||||
console.log('');
|
console.log('');
|
||||||
}
|
}
|
||||||
@@ -1036,12 +1051,12 @@ export async function handleCliproxyCommand(args: string[]): Promise<void> {
|
|||||||
}
|
}
|
||||||
// Strip leading 'v' prefix and whitespace (user may type " v6.6.80-0 ")
|
// Strip leading 'v' prefix and whitespace (user may type " v6.6.80-0 ")
|
||||||
version = version.trim().replace(/^v/, '');
|
version = version.trim().replace(/^v/, '');
|
||||||
await handleInstallVersion(version, verbose);
|
await handleInstallVersion(version, verbose, effectiveBackend);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remainingArgs.includes('--latest') || remainingArgs.includes('--update')) {
|
if (remainingArgs.includes('--latest') || remainingArgs.includes('--update')) {
|
||||||
await handleInstallLatest(verbose);
|
await handleInstallLatest(verbose, effectiveBackend);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user