@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
782 lines (781 loc) • 29.8 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnvironmentValidator = void 0;
exports.getEnvironmentValidator = getEnvironmentValidator;
exports.validateEnvironment = validateEnvironment;
exports.setupEnvironment = setupEnvironment;
const events_1 = require("events");
const os = __importStar(require("os"));
const child_process_1 = require("child_process");
const util_1 = require("util");
const semver = __importStar(require("semver"));
const execAsync = (0, util_1.promisify)(child_process_1.exec);
class EnvironmentValidator extends events_1.EventEmitter {
constructor() {
super();
this.requirements = new Map();
this.validationCache = new Map();
this.cacheTimeout = 5 * 60 * 1000; // 5 minutes
this.defaultOptions = {
autoFix: true,
interactive: false,
verbose: false,
skipOptional: false,
parallel: true,
timeout: 30000,
retryAttempts: 3
};
this.initializeRequirements();
}
initializeRequirements() {
// Core requirements
this.addRequirement({
name: 'Node.js',
type: 'runtime',
required: true,
minVersion: '16.0.0',
checkCommand: 'node --version',
installUrl: 'https://nodejs.org/',
validator: async () => {
try {
const version = (0, child_process_1.execSync)('node --version', { encoding: 'utf8' }).trim();
return semver.gte(version.replace('v', ''), '16.0.0');
}
catch {
return false;
}
}
});
this.addRequirement({
name: 'npm',
type: 'tool',
required: true,
minVersion: '8.0.0',
checkCommand: 'npm --version',
installCommand: 'npm install -g npm@latest',
autoInstall: true
});
this.addRequirement({
name: 'Git',
type: 'tool',
required: true,
minVersion: '2.25.0',
checkCommand: 'git --version',
installUrl: 'https://git-scm.com/downloads',
validator: async () => {
try {
const output = (0, child_process_1.execSync)('git --version', { encoding: 'utf8' });
const version = output.match(/(\d+\.\d+\.\d+)/)?.[1];
return version ? semver.gte(version, '2.25.0') : false;
}
catch {
return false;
}
}
});
// Optional but recommended tools
this.addRequirement({
name: 'Docker',
type: 'tool',
required: false,
checkCommand: 'docker --version',
installUrl: 'https://docs.docker.com/get-docker/'
});
this.addRequirement({
name: 'Yarn',
type: 'package',
required: false,
minVersion: '1.22.0',
checkCommand: 'yarn --version',
installCommand: 'npm install -g yarn',
autoInstall: true
});
this.addRequirement({
name: 'pnpm',
type: 'package',
required: false,
minVersion: '7.0.0',
checkCommand: 'pnpm --version',
installCommand: 'npm install -g pnpm',
autoInstall: true
});
// System requirements
this.addRequirement({
name: 'Disk Space',
type: 'system',
required: true,
validator: async () => {
const diskInfo = await this.getDiskInfo();
return diskInfo.free > 1024 * 1024 * 1024; // 1GB free space
}
});
this.addRequirement({
name: 'Memory',
type: 'system',
required: true,
validator: async () => {
const memInfo = this.getMemoryInfo();
return memInfo.free > 512 * 1024 * 1024; // 512MB free memory
}
});
this.addRequirement({
name: 'Internet Connection',
type: 'network',
required: true,
validator: async () => {
return await this.checkInternetConnection();
}
});
// Platform-specific requirements
if (process.platform === 'win32') {
this.addRequirement({
name: 'Windows Build Tools',
type: 'tool',
required: false,
platforms: ['win32'],
checkCommand: 'npm list -g windows-build-tools',
installCommand: 'npm install -g windows-build-tools',
autoInstall: true
});
}
if (process.platform === 'darwin') {
this.addRequirement({
name: 'Xcode Command Line Tools',
type: 'tool',
required: false,
platforms: ['darwin'],
checkCommand: 'xcode-select -p',
installCommand: 'xcode-select --install'
});
}
}
addRequirement(requirement) {
// Check platform compatibility
if (requirement.platforms && !requirement.platforms.includes(process.platform)) {
return;
}
this.requirements.set(requirement.name, requirement);
this.emit('requirement:added', requirement);
}
removeRequirement(name) {
const removed = this.requirements.delete(name);
if (removed) {
this.emit('requirement:removed', name);
}
return removed;
}
async validate(options = {}) {
this.emit('validation:start');
const opts = { ...this.defaultOptions, ...options };
const issues = [];
const warnings = [];
const recommendations = [];
// Gather system info
this.systemInfo = await this.gatherSystemInfo();
// Validate each requirement
const validationPromises = Array.from(this.requirements.values()).map(req => this.validateRequirement(req, opts));
const results = opts.parallel
? await Promise.all(validationPromises)
: await this.validateSequentially(validationPromises);
// Process results
for (const result of results) {
if (result.issues) {
issues.push(...result.issues);
}
if (result.warnings) {
warnings.push(...result.warnings);
}
if (result.recommendations) {
recommendations.push(...result.recommendations);
}
}
// Add system-specific warnings
const systemWarnings = this.analyzeSystemHealth();
warnings.push(...systemWarnings);
// Add general recommendations
const generalRecommendations = this.generateRecommendations(this.systemInfo);
recommendations.push(...generalRecommendations);
const validationResult = {
valid: issues.filter(i => i.severity === 'error').length === 0,
issues,
warnings,
systemInfo: this.systemInfo,
recommendations: [...new Set(recommendations)], // Remove duplicates
autoFixAvailable: issues.some(i => i.autoFixable)
};
this.emit('validation:complete', validationResult);
return validationResult;
}
async validateRequirement(requirement, options) {
// Check cache
const cacheKey = requirement.name;
const cached = this.validationCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.result ? {} : {
issues: [{
requirement: requirement.name,
type: 'missing',
severity: requirement.required ? 'error' : 'warning',
message: `${requirement.name} is not available`,
autoFixable: !!requirement.autoInstall && !!requirement.installCommand
}]
};
}
const issues = [];
const warnings = [];
const recommendations = [];
try {
let isValid = false;
// Custom validator takes precedence
if (requirement.validator) {
isValid = await requirement.validator();
}
else if (requirement.checkCommand) {
isValid = await this.checkCommand(requirement);
}
if (!isValid) {
issues.push({
requirement: requirement.name,
type: 'missing',
severity: requirement.required ? 'error' : 'warning',
message: `${requirement.name} is not installed or not meeting requirements`,
fixCommand: requirement.installCommand,
fixUrl: requirement.installUrl,
autoFixable: !!requirement.autoInstall && !!requirement.installCommand
});
if (!requirement.required) {
recommendations.push(`Consider installing ${requirement.name} for better functionality`);
}
}
// Cache result
this.validationCache.set(cacheKey, { result: isValid, timestamp: Date.now() });
}
catch (error) {
issues.push({
requirement: requirement.name,
type: 'missing',
severity: requirement.required ? 'error' : 'warning',
message: `Failed to check ${requirement.name}: ${error.message}`,
autoFixable: false
});
}
return { issues, warnings, recommendations };
}
async checkCommand(requirement) {
if (!requirement.checkCommand)
return false;
try {
const output = (0, child_process_1.execSync)(requirement.checkCommand, { encoding: 'utf8' }).trim();
// Version check if specified
if (requirement.minVersion || requirement.maxVersion) {
const version = this.extractVersion(output);
if (!version)
return false;
if (requirement.minVersion && !semver.gte(version, requirement.minVersion)) {
return false;
}
if (requirement.maxVersion && !semver.lte(version, requirement.maxVersion)) {
return false;
}
}
return true;
}
catch {
return false;
}
}
extractVersion(output) {
// Common version patterns
const patterns = [
/(\d+\.\d+\.\d+)/, // 1.2.3
/v(\d+\.\d+\.\d+)/, // v1.2.3
/(\d+\.\d+)/, // 1.2
/version (\d+\.\d+\.\d+)/i
];
for (const pattern of patterns) {
const match = output.match(pattern);
if (match) {
return match[1];
}
}
return null;
}
async validateSequentially(promises) {
const results = [];
for (const promise of promises) {
results.push(await promise);
}
return results;
}
async setup(options = {}) {
this.emit('setup:start');
const opts = { ...this.defaultOptions, ...options };
const startTime = Date.now();
const fixed = [];
const failed = [];
const skipped = [];
const warnings = [];
const recoveryActions = [];
// First validate
const validation = await this.validate(opts);
if (validation.valid && validation.issues.length === 0) {
return {
success: true,
fixed,
failed,
skipped,
warnings,
duration: Date.now() - startTime
};
}
// Process issues
for (const issue of validation.issues) {
if (!issue.autoFixable || !opts.autoFix) {
if (issue.severity === 'error') {
failed.push(issue.requirement);
// Add recovery action
if (issue.fixCommand || issue.fixUrl) {
recoveryActions.push({
name: `Fix ${issue.requirement}`,
command: issue.fixCommand || `Visit ${issue.fixUrl}`,
description: issue.message,
priority: 'high',
automated: !!issue.fixCommand
});
}
}
else {
warnings.push(issue.message);
}
continue;
}
// Skip optional requirements if requested
if (opts.skipOptional && issue.severity === 'warning') {
skipped.push(issue.requirement);
continue;
}
// Attempt auto-fix
const requirement = this.requirements.get(issue.requirement);
if (requirement && requirement.installCommand) {
const fixResult = await this.attemptFix(requirement, opts);
if (fixResult.success) {
fixed.push(issue.requirement);
}
else {
failed.push(issue.requirement);
warnings.push(fixResult.error || 'Unknown error during fix');
// Add recovery action
recoveryActions.push({
name: `Manually fix ${issue.requirement}`,
command: requirement.installCommand,
description: `Failed to auto-install: ${fixResult.error}`,
priority: requirement.required ? 'high' : 'medium',
automated: false
});
}
}
}
const result = {
success: failed.length === 0,
fixed,
failed,
skipped,
warnings,
duration: Date.now() - startTime,
recoveryActions: recoveryActions.length > 0 ? recoveryActions : undefined
};
this.emit('setup:complete', result);
return result;
}
async attemptFix(requirement, options) {
if (!requirement.installCommand) {
return { success: false, error: 'No install command available' };
}
this.emit('fix:start', requirement.name);
let attempts = 0;
let lastError;
while (attempts < options.retryAttempts) {
attempts++;
try {
// Execute install command
if (options.verbose) {
console.log(`Attempting to install ${requirement.name} (attempt ${attempts}/${options.retryAttempts})...`);
}
await execAsync(requirement.installCommand, {
timeout: options.timeout,
env: { ...process.env, FORCE_COLOR: '0' }
});
// Verify installation
const isValid = requirement.validator
? await requirement.validator()
: requirement.checkCommand
? await this.checkCommand(requirement)
: true;
if (isValid) {
this.emit('fix:success', requirement.name);
return { success: true };
}
lastError = 'Installation verification failed';
}
catch (error) {
lastError = error.message || 'Unknown error';
if (options.verbose) {
console.error(`Failed to install ${requirement.name}: ${lastError}`);
}
// Wait before retry
if (attempts < options.retryAttempts) {
await this.delay(1000 * attempts); // Exponential backoff
}
}
}
this.emit('fix:failed', requirement.name, lastError);
return { success: false, error: lastError };
}
async gatherSystemInfo() {
const cpus = os.cpus();
const memory = this.getMemoryInfo();
const disk = await this.getDiskInfo();
const network = await this.getNetworkInfo();
return {
platform: process.platform,
arch: process.arch,
nodeVersion: process.version,
npmVersion: await this.getNpmVersion(),
osVersion: os.release(),
shell: process.env.SHELL || 'unknown',
memory,
disk,
cpu: {
model: cpus[0]?.model || 'unknown',
cores: cpus.length,
speed: cpus[0]?.speed || 0
},
network
};
}
getMemoryInfo() {
const total = os.totalmem();
const free = os.freemem();
return {
total,
free,
used: total - free
};
}
async getDiskInfo() {
// This is a simplified implementation
// In production, you'd use a library like 'node-disk-info'
try {
if (process.platform === 'win32') {
const output = (0, child_process_1.execSync)('wmic logicaldisk get size,freespace', { encoding: 'utf8' });
// Parse Windows output
const lines = output.trim().split('\n').slice(1);
let totalSize = 0, freeSpace = 0;
for (const line of lines) {
const parts = line.trim().split(/\s+/);
if (parts.length >= 2) {
freeSpace += parseInt(parts[0]) || 0;
totalSize += parseInt(parts[1]) || 0;
}
}
return {
total: totalSize,
free: freeSpace,
used: totalSize - freeSpace
};
}
else {
const output = (0, child_process_1.execSync)('df -k /', { encoding: 'utf8' });
const lines = output.trim().split('\n');
const data = lines[1].split(/\s+/);
return {
total: parseInt(data[1]) * 1024,
free: parseInt(data[3]) * 1024,
used: parseInt(data[2]) * 1024
};
}
}
catch {
return { total: 0, free: 0, used: 0 };
}
}
async getNetworkInfo() {
const online = await this.checkInternetConnection();
const proxy = process.env.HTTP_PROXY || process.env.HTTPS_PROXY;
return {
online,
proxy,
vpn: await this.detectVPN()
};
}
async getNpmVersion() {
try {
return (0, child_process_1.execSync)('npm --version', { encoding: 'utf8' }).trim();
}
catch {
return 'unknown';
}
}
async checkInternetConnection() {
try {
// Try to resolve a reliable domain
const dns = require('dns').promises;
await dns.resolve4('github.com');
return true;
}
catch {
return false;
}
}
async detectVPN() {
// Simple VPN detection - can be enhanced
try {
const interfaces = os.networkInterfaces();
for (const [name, addresses] of Object.entries(interfaces)) {
if (name.toLowerCase().includes('vpn') ||
name.toLowerCase().includes('tun') ||
name.toLowerCase().includes('tap')) {
return true;
}
}
return false;
}
catch {
return false;
}
}
analyzeSystemHealth() {
const warnings = [];
if (!this.systemInfo)
return warnings;
// Memory warnings
const memoryUsagePercent = (this.systemInfo.memory.used / this.systemInfo.memory.total) * 100;
if (memoryUsagePercent > 90) {
warnings.push({
message: 'System memory usage is very high (>90%)',
type: 'performance',
recommendation: 'Close unnecessary applications to free up memory'
});
}
// Disk warnings
const diskUsagePercent = (this.systemInfo.disk.used / this.systemInfo.disk.total) * 100;
if (diskUsagePercent > 90) {
warnings.push({
message: 'Disk usage is very high (>90%)',
type: 'performance',
recommendation: 'Free up disk space for optimal performance'
});
}
// Network warnings
if (!this.systemInfo.network.online) {
warnings.push({
message: 'No internet connection detected',
type: 'compatibility',
recommendation: 'Some features may not work without internet access'
});
}
if (this.systemInfo.network.proxy) {
warnings.push({
message: 'HTTP proxy detected',
type: 'compatibility',
recommendation: 'Ensure proxy settings are correctly configured for package managers'
});
}
// Node.js version warnings
const nodeVersion = semver.clean(this.systemInfo.nodeVersion);
if (nodeVersion && semver.lt(nodeVersion, '18.0.0')) {
warnings.push({
message: `Node.js ${nodeVersion} is outdated`,
type: 'deprecated',
recommendation: 'Consider upgrading to Node.js 18 or later for better performance and features'
});
}
return warnings;
}
generateRecommendations(systemInfo) {
const recommendations = [];
// Performance recommendations
if (systemInfo.cpu.cores < 4) {
recommendations.push('Consider upgrading to a system with more CPU cores for better build performance');
}
if (systemInfo.memory.total < 8 * 1024 * 1024 * 1024) { // Less than 8GB
recommendations.push('Systems with 8GB+ RAM provide better development experience');
}
// Tool recommendations
const installedManagers = [];
for (const [name, req] of this.requirements) {
if (req.type === 'package' && this.validationCache.get(name)?.result) {
installedManagers.push(name);
}
}
if (installedManagers.length === 0) {
recommendations.push('Install pnpm or yarn for faster package installation');
}
// Platform-specific recommendations
if (systemInfo.platform === 'win32') {
recommendations.push('Consider using Windows Terminal for better CLI experience');
recommendations.push('Enable Developer Mode in Windows settings for symbolic link support');
}
if (systemInfo.platform === 'darwin') {
recommendations.push('Install Homebrew for easier tool management');
}
return recommendations;
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async generateReport(validation) {
const result = validation || await this.validate();
const lines = [];
lines.push('# Environment Validation Report');
lines.push(`Generated: ${new Date().toISOString()}`);
lines.push('');
// Summary
lines.push('## Summary');
lines.push(`Status: ${result.valid ? '✅ VALID' : '❌ INVALID'}`);
lines.push(`Issues: ${result.issues.length} (${result.issues.filter(i => i.severity === 'error').length} errors)`);
lines.push(`Warnings: ${result.warnings.length}`);
lines.push('');
// System Information
lines.push('## System Information');
lines.push(`- Platform: ${result.systemInfo.platform} (${result.systemInfo.arch})`);
lines.push(`- OS Version: ${result.systemInfo.osVersion}`);
lines.push(`- Node.js: ${result.systemInfo.nodeVersion}`);
lines.push(`- npm: ${result.systemInfo.npmVersion}`);
lines.push(`- CPU: ${result.systemInfo.cpu.model} (${result.systemInfo.cpu.cores} cores)`);
lines.push(`- Memory: ${this.formatBytes(result.systemInfo.memory.total)} total, ${this.formatBytes(result.systemInfo.memory.free)} free`);
lines.push(`- Disk: ${this.formatBytes(result.systemInfo.disk.total)} total, ${this.formatBytes(result.systemInfo.disk.free)} free`);
lines.push(`- Network: ${result.systemInfo.network.online ? 'Online' : 'Offline'}`);
lines.push('');
// Issues
if (result.issues.length > 0) {
lines.push('## Issues');
for (const issue of result.issues) {
const icon = issue.severity === 'error' ? '❌' : '⚠️';
lines.push(`${icon} **${issue.requirement}**: ${issue.message}`);
if (issue.fixCommand) {
lines.push(` Fix: \`${issue.fixCommand}\``);
}
if (issue.fixUrl) {
lines.push(` More info: ${issue.fixUrl}`);
}
}
lines.push('');
}
// Warnings
if (result.warnings.length > 0) {
lines.push('## Warnings');
for (const warning of result.warnings) {
lines.push(`⚠️ ${warning.message}`);
if (warning.recommendation) {
lines.push(` → ${warning.recommendation}`);
}
}
lines.push('');
}
// Recommendations
if (result.recommendations.length > 0) {
lines.push('## Recommendations');
for (const rec of result.recommendations) {
lines.push(`- ${rec}`);
}
lines.push('');
}
// Auto-fix availability
if (result.autoFixAvailable) {
lines.push('## Auto-fix Available');
lines.push('Run `re-shell env setup --auto-fix` to automatically resolve fixable issues.');
lines.push('');
}
return lines.join('\n');
}
formatBytes(bytes) {
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(1)} ${units[unitIndex]}`;
}
// Public methods for requirement management
getRequirements() {
return Array.from(this.requirements.values());
}
getRequirement(name) {
return this.requirements.get(name);
}
clearCache() {
this.validationCache.clear();
this.systemInfo = undefined;
}
exportRequirements() {
const requirements = this.getRequirements();
return JSON.stringify(requirements, null, 2);
}
importRequirements(json) {
try {
const requirements = JSON.parse(json);
if (Array.isArray(requirements)) {
this.requirements.clear();
for (const req of requirements) {
this.addRequirement(req);
}
}
}
catch (error) {
throw new Error(`Failed to import requirements: ${error.message}`);
}
}
}
exports.EnvironmentValidator = EnvironmentValidator;
// Global instance
let globalValidator = null;
function getEnvironmentValidator() {
if (!globalValidator) {
globalValidator = new EnvironmentValidator();
}
return globalValidator;
}
async function validateEnvironment(options) {
const validator = getEnvironmentValidator();
return validator.validate(options);
}
async function setupEnvironment(options) {
const validator = getEnvironmentValidator();
return validator.setup(options);
}