Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
This commit is contained in:
408
examples/ruvLLM/esp32-flash/npm/bin/cli.js
Normal file
408
examples/ruvLLM/esp32-flash/npm/bin/cli.js
Normal file
@@ -0,0 +1,408 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* RuvLLM ESP32 CLI
|
||||
*
|
||||
* Cross-platform installation and flashing tool for RuvLLM on ESP32
|
||||
*/
|
||||
|
||||
const { spawn, execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
const VERSION = '0.3.0';
|
||||
const SUPPORTED_TARGETS = ['esp32', 'esp32s2', 'esp32s3', 'esp32c3', 'esp32c6'];
|
||||
|
||||
// Colors for terminal output
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
bright: '\x1b[1m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
red: '\x1b[31m',
|
||||
cyan: '\x1b[36m'
|
||||
};
|
||||
|
||||
function log(msg, color = 'reset') {
|
||||
console.log(`${colors[color]}${msg}${colors.reset}`);
|
||||
}
|
||||
|
||||
function logStep(msg) {
|
||||
console.log(`${colors.cyan}▶${colors.reset} ${msg}`);
|
||||
}
|
||||
|
||||
function logSuccess(msg) {
|
||||
console.log(`${colors.green}✓${colors.reset} ${msg}`);
|
||||
}
|
||||
|
||||
function logError(msg) {
|
||||
console.error(`${colors.red}✗${colors.reset} ${msg}`);
|
||||
}
|
||||
|
||||
function showHelp() {
|
||||
console.log(`
|
||||
${colors.bright}RuvLLM ESP32 v${VERSION}${colors.reset}
|
||||
Full-featured LLM inference engine for ESP32
|
||||
|
||||
${colors.yellow}USAGE:${colors.reset}
|
||||
npx ruvllm-esp32 <command> [options]
|
||||
|
||||
${colors.yellow}COMMANDS:${colors.reset}
|
||||
install Install ESP32 toolchain (espup, espflash)
|
||||
build Build the firmware
|
||||
flash [port] Flash to ESP32 (auto-detect or specify port)
|
||||
monitor [port] Monitor serial output
|
||||
config Interactive configuration
|
||||
cluster Setup multi-chip cluster
|
||||
info Show system information
|
||||
|
||||
${colors.yellow}OPTIONS:${colors.reset}
|
||||
--target, -t ESP32 variant: esp32, esp32s2, esp32s3, esp32c3, esp32c6
|
||||
--port, -p Serial port (e.g., COM3, /dev/ttyUSB0)
|
||||
--release Build in release mode
|
||||
--features Cargo features: federation, full
|
||||
--help, -h Show this help
|
||||
--version, -v Show version
|
||||
|
||||
${colors.yellow}EXAMPLES:${colors.reset}
|
||||
npx ruvllm-esp32 install
|
||||
npx ruvllm-esp32 build --target esp32s3 --release
|
||||
npx ruvllm-esp32 flash --port COM6
|
||||
npx ruvllm-esp32 flash /dev/ttyUSB0
|
||||
npx ruvllm-esp32 cluster --chips 5
|
||||
|
||||
${colors.yellow}FEATURES:${colors.reset}
|
||||
- INT8/Binary quantized inference (~20KB RAM)
|
||||
- Product quantization (8-32x compression)
|
||||
- MicroLoRA on-device adaptation
|
||||
- HNSW vector search (1000+ vectors)
|
||||
- Semantic memory with RAG
|
||||
- Multi-chip federation (pipeline/tensor parallel)
|
||||
- Speculative decoding (2-4x speedup)
|
||||
`);
|
||||
}
|
||||
|
||||
function detectPlatform() {
|
||||
const platform = os.platform();
|
||||
const arch = os.arch();
|
||||
return { platform, arch };
|
||||
}
|
||||
|
||||
function detectPort() {
|
||||
const { platform } = detectPlatform();
|
||||
|
||||
try {
|
||||
if (platform === 'win32') {
|
||||
// Windows: Use PowerShell for better COM port detection
|
||||
try {
|
||||
const result = execSync(
|
||||
'powershell -Command "[System.IO.Ports.SerialPort]::GetPortNames() | Sort-Object { [int]($_ -replace \'COM\', \'\') }"',
|
||||
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
|
||||
);
|
||||
const ports = result.trim().split('\n').filter(p => p.match(/COM\d+/));
|
||||
if (ports.length > 0) {
|
||||
return ports[0].trim();
|
||||
}
|
||||
} catch {
|
||||
// Fallback to wmic
|
||||
const result = execSync('wmic path Win32_SerialPort get DeviceID 2>nul', { encoding: 'utf8' });
|
||||
const ports = result.split('\n').filter(line => line.includes('COM')).map(line => line.trim());
|
||||
if (ports.length > 0) return ports[0];
|
||||
}
|
||||
return 'COM3';
|
||||
} else if (platform === 'darwin') {
|
||||
// macOS
|
||||
const files = fs.readdirSync('/dev').filter(f =>
|
||||
f.startsWith('cu.usbserial') ||
|
||||
f.startsWith('cu.SLAB') ||
|
||||
f.startsWith('cu.wchusbserial') ||
|
||||
f.startsWith('cu.usbmodem')
|
||||
);
|
||||
return files[0] ? `/dev/${files[0]}` : '/dev/cu.usbserial-0001';
|
||||
} else {
|
||||
// Linux
|
||||
const files = fs.readdirSync('/dev').filter(f => f.startsWith('ttyUSB') || f.startsWith('ttyACM'));
|
||||
return files[0] ? `/dev/${files[0]}` : '/dev/ttyUSB0';
|
||||
}
|
||||
} catch (e) {
|
||||
return platform === 'win32' ? 'COM3' : '/dev/ttyUSB0';
|
||||
}
|
||||
}
|
||||
|
||||
function checkToolchain() {
|
||||
try {
|
||||
execSync('espup --version', { stdio: 'pipe' });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function installToolchain() {
|
||||
logStep('Installing ESP32 toolchain...');
|
||||
|
||||
const { platform } = detectPlatform();
|
||||
|
||||
try {
|
||||
if (platform === 'win32') {
|
||||
// Windows: Check if we have the PowerShell setup script
|
||||
const scriptsDir = path.join(__dirname, '..', 'scripts', 'windows');
|
||||
const setupScript = path.join(scriptsDir, 'setup.ps1');
|
||||
|
||||
if (fs.existsSync(setupScript)) {
|
||||
logStep('Running Windows setup script...');
|
||||
execSync(`powershell -ExecutionPolicy Bypass -File "${setupScript}"`, { stdio: 'inherit' });
|
||||
} else {
|
||||
// Fallback: manual installation
|
||||
logStep('Installing espup...');
|
||||
|
||||
// Download espup for Windows
|
||||
const espupUrl = 'https://github.com/esp-rs/espup/releases/latest/download/espup-x86_64-pc-windows-msvc.exe';
|
||||
const espupPath = path.join(os.tmpdir(), 'espup.exe');
|
||||
|
||||
execSync(`powershell -Command "Invoke-WebRequest -Uri '${espupUrl}' -OutFile '${espupPath}'"`, { stdio: 'inherit' });
|
||||
|
||||
logStep('Running espup install...');
|
||||
execSync(`"${espupPath}" install`, { stdio: 'inherit' });
|
||||
|
||||
// Install espflash
|
||||
logStep('Installing espflash...');
|
||||
execSync('cargo install espflash ldproxy', { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
logSuccess('Toolchain installed successfully!');
|
||||
log('\nTo use the toolchain, run:', 'yellow');
|
||||
log(' . .\\scripts\\windows\\env.ps1', 'cyan');
|
||||
|
||||
} else {
|
||||
// Linux/macOS
|
||||
logStep('Installing espup...');
|
||||
const arch = os.arch() === 'arm64' ? 'aarch64' : 'x86_64';
|
||||
const binary = platform === 'darwin'
|
||||
? `espup-${arch}-apple-darwin`
|
||||
: `espup-${arch}-unknown-linux-gnu`;
|
||||
|
||||
execSync(`curl -L https://github.com/esp-rs/espup/releases/latest/download/${binary} -o /tmp/espup && chmod +x /tmp/espup && /tmp/espup install`, { stdio: 'inherit' });
|
||||
|
||||
// Install espflash
|
||||
logStep('Installing espflash...');
|
||||
execSync('cargo install espflash ldproxy', { stdio: 'inherit' });
|
||||
|
||||
logSuccess('Toolchain installed successfully!');
|
||||
log('\nPlease restart your terminal or run:', 'yellow');
|
||||
log(' source $HOME/export-esp.sh', 'cyan');
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
logError(`Installation failed: ${e.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function build(options = {}) {
|
||||
const target = options.target || 'esp32';
|
||||
const release = options.release !== false; // Default to release
|
||||
const features = options.features || '';
|
||||
const { platform } = detectPlatform();
|
||||
|
||||
logStep(`Building for ${target}${release ? ' (release)' : ''}...`);
|
||||
|
||||
const targetMap = {
|
||||
'esp32': 'xtensa-esp32-espidf',
|
||||
'esp32s2': 'xtensa-esp32s2-espidf',
|
||||
'esp32s3': 'xtensa-esp32s3-espidf',
|
||||
'esp32c3': 'riscv32imc-esp-espidf',
|
||||
'esp32c6': 'riscv32imac-esp-espidf'
|
||||
};
|
||||
|
||||
const rustTarget = targetMap[target] || targetMap['esp32'];
|
||||
|
||||
try {
|
||||
if (platform === 'win32') {
|
||||
// Windows: Use PowerShell build script if available
|
||||
const scriptsDir = path.join(__dirname, '..', 'scripts', 'windows');
|
||||
const buildScript = path.join(scriptsDir, 'build.ps1');
|
||||
|
||||
if (fs.existsSync(buildScript)) {
|
||||
let psArgs = `-ExecutionPolicy Bypass -File "${buildScript}" -Target "${rustTarget}"`;
|
||||
if (release) psArgs += ' -Release';
|
||||
if (features) psArgs += ` -Features "${features}"`;
|
||||
|
||||
execSync(`powershell ${psArgs}`, { stdio: 'inherit', cwd: process.cwd() });
|
||||
} else {
|
||||
// Fallback to direct cargo
|
||||
let cmd = `cargo build --target ${rustTarget}`;
|
||||
if (release) cmd += ' --release';
|
||||
if (features) cmd += ` --features ${features}`;
|
||||
execSync(cmd, { stdio: 'inherit', cwd: process.cwd() });
|
||||
}
|
||||
} else {
|
||||
// Linux/macOS
|
||||
let cmd = `cargo build --target ${rustTarget}`;
|
||||
if (release) cmd += ' --release';
|
||||
if (features) cmd += ` --features ${features}`;
|
||||
execSync(cmd, { stdio: 'inherit', cwd: process.cwd() });
|
||||
}
|
||||
|
||||
logSuccess('Build completed!');
|
||||
return true;
|
||||
} catch (e) {
|
||||
logError(`Build failed: ${e.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function flash(port, options = {}) {
|
||||
const actualPort = port || detectPort();
|
||||
const target = options.target || 'esp32';
|
||||
const { platform } = detectPlatform();
|
||||
|
||||
logStep(`Flashing to ${actualPort}...`);
|
||||
|
||||
const targetMap = {
|
||||
'esp32': 'xtensa-esp32-espidf',
|
||||
'esp32s2': 'xtensa-esp32s2-espidf',
|
||||
'esp32s3': 'xtensa-esp32s3-espidf',
|
||||
'esp32c3': 'riscv32imc-esp-espidf',
|
||||
'esp32c6': 'riscv32imac-esp-espidf'
|
||||
};
|
||||
const rustTarget = targetMap[target] || targetMap['esp32'];
|
||||
|
||||
try {
|
||||
if (platform === 'win32') {
|
||||
// Windows: Use PowerShell flash script if available
|
||||
const scriptsDir = path.join(__dirname, '..', 'scripts', 'windows');
|
||||
const flashScript = path.join(scriptsDir, 'flash.ps1');
|
||||
|
||||
if (fs.existsSync(flashScript)) {
|
||||
const psArgs = `-ExecutionPolicy Bypass -File "${flashScript}" -Port "${actualPort}" -Target "${rustTarget}"`;
|
||||
execSync(`powershell ${psArgs}`, { stdio: 'inherit', cwd: process.cwd() });
|
||||
} else {
|
||||
// Fallback
|
||||
const binary = `target\\${rustTarget}\\release\\ruvllm-esp32`;
|
||||
execSync(`espflash flash --monitor --port ${actualPort} ${binary}`, { stdio: 'inherit' });
|
||||
}
|
||||
} else {
|
||||
// Linux/macOS
|
||||
const binary = `target/${rustTarget}/release/ruvllm-esp32`;
|
||||
execSync(`espflash flash --monitor --port ${actualPort} ${binary}`, { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
logSuccess('Flash completed!');
|
||||
return true;
|
||||
} catch (e) {
|
||||
logError(`Flash failed: ${e.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function monitor(port) {
|
||||
const actualPort = port || detectPort();
|
||||
logStep(`Monitoring ${actualPort}...`);
|
||||
|
||||
try {
|
||||
execSync(`espflash monitor --port ${actualPort}`, { stdio: 'inherit' });
|
||||
} catch (e) {
|
||||
// Monitor exits normally with Ctrl+C
|
||||
}
|
||||
}
|
||||
|
||||
function showInfo() {
|
||||
const { platform, arch } = detectPlatform();
|
||||
const hasToolchain = checkToolchain();
|
||||
|
||||
console.log(`
|
||||
${colors.bright}RuvLLM ESP32 System Information${colors.reset}
|
||||
${'─'.repeat(40)}
|
||||
Version: ${VERSION}
|
||||
Platform: ${platform}
|
||||
Architecture: ${arch}
|
||||
Toolchain: ${hasToolchain ? `${colors.green}Installed${colors.reset}` : `${colors.red}Not installed${colors.reset}`}
|
||||
Detected Port: ${detectPort()}
|
||||
|
||||
${colors.yellow}Supported Targets:${colors.reset}
|
||||
${SUPPORTED_TARGETS.join(', ')}
|
||||
|
||||
${colors.yellow}Features:${colors.reset}
|
||||
- Binary quantization (32x compression)
|
||||
- Product quantization (8-32x)
|
||||
- Sparse attention patterns
|
||||
- MicroLoRA adaptation
|
||||
- HNSW vector index
|
||||
- Semantic memory
|
||||
- RAG retrieval
|
||||
- Anomaly detection
|
||||
- Pipeline parallelism
|
||||
- Tensor parallelism
|
||||
- Speculative decoding
|
||||
`);
|
||||
}
|
||||
|
||||
// Parse arguments
|
||||
const args = process.argv.slice(2);
|
||||
const command = args[0];
|
||||
|
||||
const options = {
|
||||
target: 'esp32',
|
||||
port: null,
|
||||
release: false,
|
||||
features: ''
|
||||
};
|
||||
|
||||
for (let i = 1; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg === '--target' || arg === '-t') {
|
||||
options.target = args[++i];
|
||||
} else if (arg === '--port' || arg === '-p') {
|
||||
options.port = args[++i];
|
||||
} else if (arg === '--release') {
|
||||
options.release = true;
|
||||
} else if (arg === '--features') {
|
||||
options.features = args[++i];
|
||||
} else if (arg === '--help' || arg === '-h') {
|
||||
showHelp();
|
||||
process.exit(0);
|
||||
} else if (arg === '--version' || arg === '-v') {
|
||||
console.log(VERSION);
|
||||
process.exit(0);
|
||||
} else if (!arg.startsWith('-')) {
|
||||
// Positional argument (likely port)
|
||||
if (!options.port) options.port = arg;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute command
|
||||
async function main() {
|
||||
switch (command) {
|
||||
case 'install':
|
||||
await installToolchain();
|
||||
break;
|
||||
case 'build':
|
||||
await build(options);
|
||||
break;
|
||||
case 'flash':
|
||||
await flash(options.port, options);
|
||||
break;
|
||||
case 'monitor':
|
||||
await monitor(options.port);
|
||||
break;
|
||||
case 'info':
|
||||
showInfo();
|
||||
break;
|
||||
case 'help':
|
||||
case undefined:
|
||||
showHelp();
|
||||
break;
|
||||
default:
|
||||
logError(`Unknown command: ${command}`);
|
||||
showHelp();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(e => {
|
||||
logError(e.message);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user