seb-cli-tool
Version:
SEB CLI - Smart Embedded Board Configuration Tool - Cloud-First MCU Management
243 lines (215 loc) ⢠8.3 kB
JavaScript
#!/usr/bin/env node
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
// Import update checker
let UpdateChecker;
try {
UpdateChecker = require('../scripts/update-checker.js');
} catch (error) {
// Update checker not available, continue without it
UpdateChecker = null;
}
// Get the directory where this script is located
const scriptDir = path.dirname(require.main.filename);
const packageDir = path.dirname(scriptDir);
// Check if we're running from a packaged binary or development
const isWindows = process.platform === 'win32';
const isBinaryPackaged = fs.existsSync(path.join(packageDir, 'dist', isWindows ? 'seb.exe' : 'seb'));
const isDevelopment = fs.existsSync(path.join(packageDir, 'seb', 'cli', 'main.py'));
let pythonCommand;
let pythonArgs;
// Python command detection with virtual environment support
function findPythonCommand() {
const { execSync } = require('child_process');
const isWindows = process.platform === 'win32';
// First check for SEB configuration file
const userHome = process.env.HOME || process.env.USERPROFILE;
const sebConfigPath = path.join(userHome, '.seb', 'config.json');
if (fs.existsSync(sebConfigPath)) {
try {
const config = JSON.parse(fs.readFileSync(sebConfigPath, 'utf8'));
if (config.python_path && fs.existsSync(config.python_path)) {
try {
execSync(`"${config.python_path}" --version`, { stdio: 'ignore' });
return config.python_path;
} catch (e) {
// Continue to other options
}
}
} catch (e) {
// Continue to other options
}
}
// Check for SEB virtual environment in package directory
const sebVenvPath = path.join(packageDir, 'seb_env');
if (fs.existsSync(sebVenvPath)) {
const venvPython = path.join(sebVenvPath, isWindows ? 'Scripts' : 'bin', isWindows ? 'python.exe' : 'python');
const venvPython3 = path.join(sebVenvPath, isWindows ? 'Scripts' : 'bin', isWindows ? 'python3.exe' : 'python3');
try {
execSync(`"${venvPython}" --version`, { stdio: 'ignore' });
return venvPython;
} catch (e) {
try {
execSync(`"${venvPython3}" --version`, { stdio: 'ignore' });
return venvPython3;
} catch (e) {
// Continue to other options
}
}
}
// Check if we're in a virtual environment
if (process.env.VIRTUAL_ENV) {
const venvPython = path.join(process.env.VIRTUAL_ENV, isWindows ? 'Scripts' : 'bin', isWindows ? 'python.exe' : 'python');
try {
execSync(`"${venvPython}" --version`, { stdio: 'ignore' });
return venvPython;
} catch (e) {
// Fallback to venv python3
const venvPython3 = path.join(process.env.VIRTUAL_ENV, isWindows ? 'Scripts' : 'bin', isWindows ? 'python3.exe' : 'python3');
try {
execSync(`"${venvPython3}" --version`, { stdio: 'ignore' });
return venvPython3;
} catch (e) {
// Continue to system Python
}
}
}
// Check for global SEB virtual environment
const globalSebVenvPath = path.join(userHome, '.seb', 'venv');
const globalSebVenvPython = path.join(globalSebVenvPath, isWindows ? 'Scripts' : 'bin', isWindows ? 'python.exe' : 'python');
const globalSebVenvPython3 = path.join(globalSebVenvPath, isWindows ? 'Scripts' : 'bin', isWindows ? 'python3.exe' : 'python3');
try {
execSync(`"${globalSebVenvPython}" --version`, { stdio: 'ignore' });
return globalSebVenvPython;
} catch (e) {
try {
execSync(`"${globalSebVenvPython3}" --version`, { stdio: 'ignore' });
return globalSebVenvPython3;
} catch (e) {
// Continue to system Python
}
}
// Try system Python with Windows-specific paths
if (isWindows) {
// Try common Windows Python paths
const pythonPaths = [
'python',
'python3',
'py',
path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Python', 'Python312', 'python.exe'),
path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Python', 'Python311', 'python.exe'),
path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Python', 'Python310', 'python.exe'),
path.join(process.env.PROGRAMFILES || '', 'Python312', 'python.exe'),
path.join(process.env.PROGRAMFILES || '', 'Python311', 'python.exe'),
path.join(process.env.PROGRAMFILES || '', 'Python310', 'python.exe'),
path.join(process.env.PROGRAMFILES || '', 'Python', 'Python312', 'python.exe'),
path.join(process.env.PROGRAMFILES || '', 'Python', 'Python311', 'python.exe'),
path.join(process.env.PROGRAMFILES || '', 'Python', 'Python310', 'python.exe'),
];
for (const pythonPath of pythonPaths) {
try {
execSync(`"${pythonPath}" --version`, { stdio: 'ignore' });
return pythonPath;
} catch (e) {
// Continue to next path
}
}
} else {
// Unix-like systems
try {
execSync('python3 --version', { stdio: 'ignore' });
return 'python3';
} catch (e) {
try {
execSync('python --version', { stdio: 'ignore' });
return 'python';
} catch (e) {
// Continue to null
}
}
}
return null;
}
// Priority order: Platform-specific binary > Virtual environment > Development
if (isBinaryPackaged) {
// Use packaged binary (source code protected) - PRIORITY 1
const binaryName = isWindows ? 'seb.exe' : 'seb';
pythonCommand = path.join(packageDir, 'dist', binaryName);
pythonArgs = process.argv.slice(2);
} else if (isDevelopment) {
// Use Python module in development - PRIORITY 2
pythonCommand = findPythonCommand() || 'python3';
pythonArgs = ['-m', 'seb.cli.main', ...process.argv.slice(2)];
} else {
// Fallback: try to find Python and install if needed - PRIORITY 3
pythonCommand = findPythonCommand() || 'python3';
pythonArgs = ['-m', 'seb.cli.main', ...process.argv.slice(2)];
}
// Enhanced error handling
function handleError(error) {
if (error.code === 'ENOENT') {
console.error('ā Error: Python not found or SEB CLI not properly installed.');
console.error('');
console.error('š§ For Windows users:');
console.error(' 1. Make sure Python 3.10+ is installed from https://www.python.org/downloads/');
console.error(' 2. During installation, check "Add Python to PATH"');
console.error(' 3. Restart your terminal/PowerShell');
console.error(' 4. Try running: seb --version');
console.error('');
console.error('š” Alternative solutions:');
console.error(' ⢠Run: npm install -g seb-cli-tool (reinstall)');
console.error(' ⢠Check if Python is in PATH: python --version');
console.error(' ⢠Try: py --version (Windows Python launcher)');
process.exit(1);
} else {
console.error('ā Error:', error.message);
process.exit(1);
}
}
// Enhanced output handling
function handleOutput(data) {
process.stdout.write(data);
}
function handleErrorOutput(data) {
process.stderr.write(data);
}
// Check for updates in background (non-blocking)
if (UpdateChecker && process.argv.length > 2 && !process.argv.includes('--no-update-check')) {
const checker = new UpdateChecker();
checker.checkForUpdates().then(result => {
if (result.hasUpdate) {
console.log(`\nš Update available: ${result.currentVersion} ā ${result.latestVersion}`);
console.log(`š Changelog: ${result.changelog}`);
console.log(`š” Run 'npm update -g @rootaccess/seb-cli' to update\n`);
}
}).catch(() => {
// Silent fail for update checks
});
}
// Main execution
try {
const sebProcess = spawn(pythonCommand, pythonArgs, {
stdio: ['inherit', 'pipe', 'pipe'],
cwd: process.cwd(),
env: { ...process.env, SEB_CLI_MODE: 'npm' }
});
// Handle output
sebProcess.stdout.on('data', handleOutput);
sebProcess.stderr.on('data', handleErrorOutput);
// Handle process completion
sebProcess.on('close', (code) => {
process.exit(code);
});
// Handle process errors
sebProcess.on('error', handleError);
// Handle process termination
process.on('SIGINT', () => {
sebProcess.kill('SIGINT');
});
process.on('SIGTERM', () => {
sebProcess.kill('SIGTERM');
});
} catch (error) {
handleError(error);
}