const { execSync, spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const os = require('os');
const https = require('https');
const BIN_DIR = path.join(__dirname, 'bin');
const PACKAGE_VERSION = require('./package.json').version;
const PLATFORM_MAP = {
'darwin-x64': { target: 'x86_64-apple-darwin', archive: 'tar.gz' },
'darwin-arm64': { target: 'aarch64-apple-darwin', archive: 'tar.gz' },
'linux-x64': { target: 'x86_64-unknown-linux-gnu', archive: 'tar.gz' },
'linux-arm64': { target: 'aarch64-unknown-linux-gnu', archive: 'tar.gz' },
'win32-x64': { target: 'x86_64-pc-windows-msvc', archive: 'zip' },
'win32-arm64': { target: 'aarch64-pc-windows-msvc', archive: 'zip' },
};
function getPlatform() {
const platform = os.platform();
const arch = os.arch();
const key = `${platform}-${arch}`;
return PLATFORM_MAP[key] || null;
}
function checkCommand(cmd) {
try {
execSync(`${cmd} --version`, { stdio: 'ignore' });
return true;
} catch {
return false;
}
}
function isMatrixcodeInstalled() {
try {
const result = spawnSync('matrixcode', ['--version'], { stdio: 'ignore' });
return result.status === 0;
} catch {
return false;
}
}
function installViaCargo() {
console.log('[matrixcode] Installing via cargo install matrixcode...');
try {
execSync('cargo install matrixcode', { stdio: 'inherit' });
console.log('[matrixcode] ✓ Installed successfully via cargo');
return true;
} catch (error) {
console.error('[matrixcode] ✗ Failed to install via cargo:', error.message);
return false;
}
}
function downloadFile(url, dest) {
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(dest);
const request = (urlString) => {
https.get(urlString, (response) => {
if (response.statusCode === 302 || response.statusCode === 301) {
request(response.headers.location);
return;
}
if (response.statusCode !== 200) {
reject(new Error(`HTTP ${response.statusCode}`));
return;
}
response.pipe(file);
file.on('finish', () => {
file.close();
resolve();
});
}).on('error', (err) => {
fs.unlink(dest, () => {});
reject(err);
});
};
request(url);
});
}
async function downloadBinary() {
const platformInfo = getPlatform();
if (!platformInfo) {
console.error(`[matrixcode] Unsupported platform: ${os.platform()}-${os.arch()}`);
console.log('[matrixcode] Falling back to cargo install...');
return installViaCargo();
}
const { target, archive } = platformInfo;
const archiveName = `matrixcode-${target}.${archive}`;
const downloadUrl = `https://github.com/bigfish1913/matrixcode/releases/download/v${PACKAGE_VERSION}/${archiveName}`;
console.log(`[matrixcode] Downloading binary for ${target}...`);
console.log(`[matrixcode] URL: ${downloadUrl}`);
if (!fs.existsSync(BIN_DIR)) {
fs.mkdirSync(BIN_DIR, { recursive: true });
}
const archivePath = path.join(BIN_DIR, archiveName);
const binaryName = os.platform() === 'win32' ? 'matrixcode.exe' : 'matrixcode';
const targetPath = path.join(BIN_DIR, binaryName);
try {
await downloadFile(downloadUrl, archivePath);
console.log('[matrixcode] ✓ Download complete, extracting...');
} catch (error) {
console.error(`[matrixcode] ✗ Failed to download: ${error.message}`);
console.log('[matrixcode] Falling back to cargo install...');
return installViaCargo();
}
try {
if (archive === 'tar.gz') {
execSync(`tar -xzf "${archivePath}" -C "${BIN_DIR}"`, { stdio: 'inherit' });
} else if (archive === 'zip') {
execSync(`powershell -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${BIN_DIR}' -Force"`, { stdio: 'inherit' });
}
if (os.platform() !== 'win32') {
fs.chmodSync(targetPath, 0o755);
}
fs.unlinkSync(archivePath);
console.log('[matrixcode] ✓ Binary installed successfully');
return true;
} catch (error) {
console.error(`[matrixcode] ✗ Failed to extract: ${error.message}`);
try { fs.unlinkSync(archivePath); } catch {}
console.log('[matrixcode] Falling back to cargo install...');
return installViaCargo();
}
}
function createWrapperScript() {
const ext = os.platform() === 'win32' ? '.cmd' : '';
const wrapperPath = path.join(BIN_DIR, `matrixcode${ext}`);
if (!fs.existsSync(BIN_DIR)) {
fs.mkdirSync(BIN_DIR, { recursive: true });
}
if (os.platform() === 'win32') {
const content = `@echo off
matrixcode %*
`;
fs.writeFileSync(wrapperPath, content);
} else {
const content = `#!/bin/sh
exec matrixcode "$@"
`;
fs.writeFileSync(wrapperPath, content, { mode: 0o755 });
}
console.log('[matrixcode] ✓ Wrapper script created');
}
async function main() {
console.log(`[matrixcode] v${PACKAGE_VERSION}`);
console.log('');
if (isMatrixcodeInstalled()) {
console.log('[matrixcode] ✓ matrixcode is already installed');
createWrapperScript();
return;
}
console.log('[matrixcode] matrixcode not found, installing...');
console.log('');
if (checkCommand('cargo')) {
console.log('[matrixcode] Found cargo, installing via cargo...');
if (installViaCargo()) {
createWrapperScript();
return;
}
}
if (await downloadBinary()) {
return;
}
console.error('');
console.error('[matrixcode] ✗ Automatic installation failed');
console.error('');
console.error('Please install manually using one of these methods:');
console.error('');
console.error(' # Option 1: Using cargo (recommended)');
console.error(' cargo install matrixcode');
console.error('');
console.error(' # Option 2: Download from GitHub Releases');
console.error(' https://github.com/bigfish1913/matrixcode/releases');
console.error('');
process.exit(1);
}
main().catch(err => {
console.error('[matrixcode] Installation error:', err.message);
process.exit(1);
});