113 lines
3.6 KiB
TypeScript
113 lines
3.6 KiB
TypeScript
import * as plugins from '../../plugins.js';
|
|
import { logger } from '../../core/utils/logger.js';
|
|
|
|
/**
|
|
* Locates the RustProxy binary using a priority-ordered search strategy:
|
|
* 1. SMARTPROXY_RUST_BINARY environment variable
|
|
* 2. Platform-specific optional npm package
|
|
* 3. Local development build at ./rust/target/release/rustproxy
|
|
* 4. System PATH
|
|
*/
|
|
export class RustBinaryLocator {
|
|
private cachedPath: string | null = null;
|
|
|
|
/**
|
|
* Find the RustProxy binary path.
|
|
* Returns null if no binary is available.
|
|
*/
|
|
public async findBinary(): Promise<string | null> {
|
|
if (this.cachedPath !== null) {
|
|
return this.cachedPath;
|
|
}
|
|
|
|
const path = await this.searchBinary();
|
|
this.cachedPath = path;
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* Clear the cached binary path (e.g., after a failed launch).
|
|
*/
|
|
public clearCache(): void {
|
|
this.cachedPath = null;
|
|
}
|
|
|
|
private async searchBinary(): Promise<string | null> {
|
|
// 1. Environment variable override
|
|
const envPath = process.env.SMARTPROXY_RUST_BINARY;
|
|
if (envPath) {
|
|
if (await this.isExecutable(envPath)) {
|
|
logger.log('info', `RustProxy binary found via SMARTPROXY_RUST_BINARY: ${envPath}`, { component: 'rust-locator' });
|
|
return envPath;
|
|
}
|
|
logger.log('warn', `SMARTPROXY_RUST_BINARY set but not executable: ${envPath}`, { component: 'rust-locator' });
|
|
}
|
|
|
|
// 2. Platform-specific optional npm package
|
|
const platformBinary = await this.findPlatformPackageBinary();
|
|
if (platformBinary) {
|
|
logger.log('info', `RustProxy binary found in platform package: ${platformBinary}`, { component: 'rust-locator' });
|
|
return platformBinary;
|
|
}
|
|
|
|
// 3. Local development build
|
|
const localPaths = [
|
|
plugins.path.resolve(process.cwd(), 'rust/target/release/rustproxy'),
|
|
plugins.path.resolve(process.cwd(), 'rust/target/debug/rustproxy'),
|
|
];
|
|
for (const localPath of localPaths) {
|
|
if (await this.isExecutable(localPath)) {
|
|
logger.log('info', `RustProxy binary found at local path: ${localPath}`, { component: 'rust-locator' });
|
|
return localPath;
|
|
}
|
|
}
|
|
|
|
// 4. System PATH
|
|
const systemPath = await this.findInPath('rustproxy');
|
|
if (systemPath) {
|
|
logger.log('info', `RustProxy binary found in system PATH: ${systemPath}`, { component: 'rust-locator' });
|
|
return systemPath;
|
|
}
|
|
|
|
logger.log('error', 'No RustProxy binary found. Set SMARTPROXY_RUST_BINARY, install the platform package, or build with: cd rust && cargo build --release', { component: 'rust-locator' });
|
|
return null;
|
|
}
|
|
|
|
private async findPlatformPackageBinary(): Promise<string | null> {
|
|
const platform = process.platform;
|
|
const arch = process.arch;
|
|
const packageName = `@push.rocks/smartproxy-${platform}-${arch}`;
|
|
|
|
try {
|
|
// Try to resolve the platform-specific package
|
|
const packagePath = require.resolve(`${packageName}/rustproxy`);
|
|
if (await this.isExecutable(packagePath)) {
|
|
return packagePath;
|
|
}
|
|
} catch {
|
|
// Package not installed - expected for development
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private async isExecutable(filePath: string): Promise<boolean> {
|
|
try {
|
|
await plugins.fs.promises.access(filePath, plugins.fs.constants.X_OK);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private async findInPath(binaryName: string): Promise<string | null> {
|
|
const pathDirs = (process.env.PATH || '').split(plugins.path.delimiter);
|
|
for (const dir of pathDirs) {
|
|
const fullPath = plugins.path.join(dir, binaryName);
|
|
if (await this.isExecutable(fullPath)) {
|
|
return fullPath;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|