@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
1,327 lines (1,314 loc) • 50.6 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.TroubleshootingGuideGenerator = void 0;
const events_1 = require("events");
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const child_process_1 = require("child_process");
const os = __importStar(require("os"));
class TroubleshootingGuideGenerator extends events_1.EventEmitter {
constructor(config = {}) {
super();
this.diagnostics = new Map();
this.guides = new Map();
this.customChecks = new Map();
this.config = {
outputPath: './troubleshooting',
includeAutoDiagnostics: true,
includeSystemInfo: true,
includeNetworkTests: true,
includeFileSystemChecks: true,
includeEnvironmentChecks: true,
includeDependencyAnalysis: true,
includePerformanceTests: true,
includeSecurityChecks: true,
generateInteractiveGuide: true,
exportFormats: ['html', 'markdown'],
...config
};
this.initializeBuiltInDiagnostics();
this.initializeBuiltInGuides();
if (config.customChecks) {
config.customChecks.forEach(check => {
this.customChecks.set(check.id, check);
});
}
}
async generateTroubleshootingGuides() {
this.emit('generation:start');
try {
// Create output directory
await fs.ensureDir(this.config.outputPath);
// Run diagnostics if enabled
let report;
if (this.config.includeAutoDiagnostics) {
this.emit('diagnostics:start');
report = await this.runDiagnostics();
this.emit('diagnostics:complete', report);
}
// Generate guides for each category
const categories = this.getUniqueCategories();
for (const category of categories) {
this.emit('category:start', category);
await this.generateCategoryGuides(category);
this.emit('category:complete', category);
}
// Generate index and navigation
await this.generateIndex();
await this.generateNavigation();
// Export in requested formats
for (const format of this.config.exportFormats || []) {
this.emit('export:start', format);
await this.exportGuides(format, report);
this.emit('export:complete', format);
}
// Generate interactive guide if enabled
if (this.config.generateInteractiveGuide) {
await this.generateInteractiveGuide();
}
const result = {
guidesGenerated: this.guides.size,
diagnosticsCreated: this.diagnostics.size,
autoFixesConfigured: this.countAutoFixes(),
outputPath: this.config.outputPath,
formats: this.config.exportFormats || [],
report
};
this.emit('generation:complete', result);
return result;
}
catch (error) {
this.emit('generation:error', error);
throw error;
}
}
initializeBuiltInDiagnostics() {
// System diagnostics
this.addDiagnostic({
id: 'node-version',
name: 'Node.js Version Check',
category: 'system',
description: 'Checks if Node.js version meets requirements',
run: async () => {
const version = process.version;
const major = parseInt(version.split('.')[0].substring(1));
if (major < 16) {
return {
status: 'fail',
message: `Node.js ${version} is below minimum required version 16.x`,
suggestion: 'Update Node.js to version 16.x or higher'
};
}
return {
status: 'pass',
message: `Node.js ${version} meets requirements`
};
}
});
// Network diagnostics
this.addDiagnostic({
id: 'network-connectivity',
name: 'Network Connectivity Check',
category: 'network',
description: 'Checks internet connectivity',
run: async () => {
try {
(0, child_process_1.execSync)('ping -c 1 google.com', { stdio: 'ignore' });
return {
status: 'pass',
message: 'Network connectivity is working'
};
}
catch {
return {
status: 'fail',
message: 'No internet connectivity detected',
suggestion: 'Check your network connection and firewall settings'
};
}
}
});
// File system diagnostics
this.addDiagnostic({
id: 'disk-space',
name: 'Disk Space Check',
category: 'filesystem',
description: 'Checks available disk space',
run: async () => {
const diskInfo = await this.getDiskInfo();
if (diskInfo.percentage > 90) {
return {
status: 'warning',
message: `Disk usage is at ${diskInfo.percentage}%`,
suggestion: 'Consider freeing up disk space',
details: diskInfo
};
}
return {
status: 'pass',
message: `Sufficient disk space available (${diskInfo.free} GB free)`
};
}
});
// Environment diagnostics
this.addDiagnostic({
id: 'env-path',
name: 'PATH Environment Check',
category: 'environment',
description: 'Checks if required tools are in PATH',
run: async () => {
const requiredTools = ['node', 'npm', 'git'];
const missing = [];
for (const tool of requiredTools) {
try {
(0, child_process_1.execSync)(`which ${tool}`, { stdio: 'ignore' });
}
catch {
missing.push(tool);
}
}
if (missing.length > 0) {
return {
status: 'fail',
message: `Missing tools in PATH: ${missing.join(', ')}`,
suggestion: 'Install missing tools or add them to your PATH'
};
}
return {
status: 'pass',
message: 'All required tools found in PATH'
};
}
});
// Dependency diagnostics
this.addDiagnostic({
id: 'npm-registry',
name: 'NPM Registry Access',
category: 'dependencies',
description: 'Checks access to NPM registry',
run: async () => {
try {
(0, child_process_1.execSync)('npm ping', { stdio: 'ignore' });
return {
status: 'pass',
message: 'NPM registry is accessible'
};
}
catch {
return {
status: 'fail',
message: 'Cannot access NPM registry',
suggestion: 'Check network settings or NPM configuration'
};
}
}
});
// Performance diagnostics
this.addDiagnostic({
id: 'cpu-usage',
name: 'CPU Usage Check',
category: 'performance',
description: 'Checks current CPU usage',
run: async () => {
const loadAvg = os.loadavg()[0];
const cpuCount = os.cpus().length;
const usage = (loadAvg / cpuCount) * 100;
if (usage > 80) {
return {
status: 'warning',
message: `High CPU usage detected (${usage.toFixed(1)}%)`,
suggestion: 'Close unnecessary applications to improve performance'
};
}
return {
status: 'pass',
message: `CPU usage is normal (${usage.toFixed(1)}%)`
};
}
});
// Security diagnostics
this.addDiagnostic({
id: 'npm-audit',
name: 'Security Vulnerability Check',
category: 'security',
description: 'Checks for known vulnerabilities',
run: async () => {
try {
const result = (0, child_process_1.execSync)('npm audit --json', { encoding: 'utf8' });
const audit = JSON.parse(result);
if (audit.metadata.vulnerabilities.total > 0) {
return {
status: 'warning',
message: `Found ${audit.metadata.vulnerabilities.total} vulnerabilities`,
suggestion: 'Run "npm audit fix" to resolve vulnerabilities',
details: audit.metadata.vulnerabilities
};
}
return {
status: 'pass',
message: 'No known vulnerabilities found'
};
}
catch {
return {
status: 'info',
message: 'Unable to check for vulnerabilities',
suggestion: 'Ensure npm audit is available'
};
}
},
autoFix: async () => {
try {
(0, child_process_1.execSync)('npm audit fix');
return true;
}
catch {
return false;
}
}
});
// Configuration diagnostics
this.addDiagnostic({
id: 'config-validation',
name: 'Configuration File Validation',
category: 'configuration',
description: 'Validates Re-Shell configuration files',
run: async () => {
const configPaths = [
path.join(process.cwd(), 're-shell.config.json'),
path.join(process.cwd(), '.re-shell', 'config.yaml')
];
for (const configPath of configPaths) {
if (await fs.pathExists(configPath)) {
try {
const content = await fs.readFile(configPath, 'utf8');
JSON.parse(content); // Basic validation
return {
status: 'pass',
message: 'Configuration files are valid'
};
}
catch (error) {
return {
status: 'fail',
message: `Invalid configuration file: ${configPath}`,
suggestion: 'Check configuration file syntax',
details: error
};
}
}
}
return {
status: 'info',
message: 'No configuration files found',
suggestion: 'Run "re-shell init" to create configuration'
};
}
});
}
initializeBuiltInGuides() {
// Installation issues
this.addGuide({
id: 'installation-issues',
title: 'Installation Issues',
description: 'Common problems during Re-Shell installation',
symptoms: [
'Installation fails with permission errors',
'Dependencies cannot be resolved',
'Post-install scripts fail'
],
causes: [
{
id: 'permissions',
description: 'Insufficient permissions for global installation',
probability: 'high',
checkCommand: 'npm config get prefix',
diagnosticId: 'env-path'
},
{
id: 'network',
description: 'Network connectivity issues',
probability: 'medium',
diagnosticId: 'network-connectivity'
}
],
solutions: [
{
id: 'use-npx',
description: 'Use npx instead of global installation',
steps: [
{
order: 1,
description: 'Run Re-Shell using npx',
command: 'npx @re-shell/cli init my-project'
}
],
effectiveness: 'high',
riskLevel: 'low',
timeEstimate: '1 minute'
},
{
id: 'fix-permissions',
description: 'Fix npm permissions',
steps: [
{
order: 1,
description: 'Change npm prefix to user directory',
command: 'npm config set prefix ~/.npm-global'
},
{
order: 2,
description: 'Add to PATH in ~/.bashrc or ~/.zshrc',
code: 'export PATH=~/.npm-global/bin:$PATH'
},
{
order: 3,
description: 'Reload shell configuration',
command: 'source ~/.bashrc'
}
],
effectiveness: 'high',
riskLevel: 'low',
timeEstimate: '5 minutes'
}
],
diagnostics: [
this.diagnostics.get('env-path'),
this.diagnostics.get('network-connectivity'),
this.diagnostics.get('npm-registry')
],
tags: ['installation', 'setup', 'permissions'],
difficulty: 'beginner'
});
// Build failures
this.addGuide({
id: 'build-failures',
title: 'Build Failures',
description: 'Troubleshooting build and compilation errors',
symptoms: [
'Build process fails with errors',
'TypeScript compilation errors',
'Module resolution failures'
],
causes: [
{
id: 'missing-deps',
description: 'Missing or incompatible dependencies',
probability: 'high',
diagnosticId: 'npm-audit'
},
{
id: 'node-version',
description: 'Incompatible Node.js version',
probability: 'medium',
diagnosticId: 'node-version'
}
],
solutions: [
{
id: 'clean-install',
description: 'Clean install dependencies',
steps: [
{
order: 1,
description: 'Remove node_modules and lock file',
command: 'rm -rf node_modules package-lock.json'
},
{
order: 2,
description: 'Clear npm cache',
command: 'npm cache clean --force'
},
{
order: 3,
description: 'Install dependencies fresh',
command: 'npm install'
}
],
effectiveness: 'high',
riskLevel: 'low',
timeEstimate: '5-10 minutes'
}
],
diagnostics: [
this.diagnostics.get('node-version'),
this.diagnostics.get('disk-space')
],
tags: ['build', 'compilation', 'typescript'],
difficulty: 'intermediate'
});
// Performance issues
this.addGuide({
id: 'performance-issues',
title: 'Performance Issues',
description: 'Resolving slow performance and high resource usage',
symptoms: [
'Commands run slowly',
'High CPU or memory usage',
'System becomes unresponsive'
],
causes: [
{
id: 'large-workspace',
description: 'Large number of workspace apps',
probability: 'high'
},
{
id: 'insufficient-resources',
description: 'Insufficient system resources',
probability: 'medium',
diagnosticId: 'cpu-usage'
}
],
solutions: [
{
id: 'optimize-workspace',
description: 'Optimize workspace configuration',
steps: [
{
order: 1,
description: 'Enable workspace caching',
command: 're-shell config set cache.enabled true'
},
{
order: 2,
description: 'Increase cache size limit',
command: 're-shell config set cache.maxSize 1GB'
},
{
order: 3,
description: 'Enable parallel processing',
command: 're-shell config set build.parallel true'
}
],
effectiveness: 'high',
riskLevel: 'low',
timeEstimate: '2 minutes'
}
],
diagnostics: [
this.diagnostics.get('cpu-usage'),
this.diagnostics.get('disk-space')
],
tags: ['performance', 'optimization', 'resources'],
difficulty: 'advanced'
});
}
addDiagnostic(diagnostic) {
this.diagnostics.set(diagnostic.id, diagnostic);
}
addGuide(guide) {
this.guides.set(guide.id, guide);
}
async runDiagnostics() {
const results = [];
const recommendations = [];
const autoFixes = [];
// Collect system info
const systemInfo = await this.collectSystemInfo();
// Run all diagnostics
for (const [id, diagnostic] of this.diagnostics) {
this.emit('diagnostic:start', id);
const startTime = Date.now();
try {
const result = await diagnostic.run();
const duration = Date.now() - startTime;
results.push({
checkId: id,
checkName: diagnostic.name,
category: diagnostic.category,
result,
duration,
timestamp: new Date()
});
// Generate recommendations based on results
if (result.status === 'fail' || result.status === 'warning') {
recommendations.push({
id: `rec-${id}`,
priority: result.status === 'fail' ? 'high' : 'medium',
title: diagnostic.name,
description: result.message,
actions: result.suggestion ? [result.suggestion] : [],
impact: 'Resolves ' + diagnostic.name,
effort: 'low'
});
// Check for auto-fix
if (diagnostic.autoFix && result.autoFixAvailable !== false) {
autoFixes.push({
diagnosticId: id,
title: `Auto-fix: ${diagnostic.name}`,
description: result.suggestion || 'Automatic fix available',
command: 'auto-fix command',
riskLevel: 'low',
requiresConfirmation: true
});
}
}
this.emit('diagnostic:complete', id, result);
}
catch (error) {
this.emit('diagnostic:error', id, error);
results.push({
checkId: id,
checkName: diagnostic.name,
category: diagnostic.category,
result: {
status: 'fail',
message: `Diagnostic failed: ${error}`
},
duration: Date.now() - startTime,
timestamp: new Date()
});
}
}
// Run custom checks
for (const [id, check] of this.customChecks) {
const result = await this.runCustomCheck(check);
results.push({
checkId: id,
checkName: check.name,
category: check.category,
result,
duration: 0,
timestamp: new Date()
});
}
// Sort recommendations by priority
recommendations.sort((a, b) => {
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
return priorityOrder[a.priority] - priorityOrder[b.priority];
});
const report = {
timestamp: new Date(),
systemInfo,
diagnosticResults: results,
recommendations,
autoFixesAvailable: autoFixes
};
// Save report
await this.saveReport(report);
return report;
}
async collectSystemInfo() {
const cpus = os.cpus();
const totalMem = os.totalmem();
const freeMem = os.freemem();
const diskInfo = await this.getDiskInfo();
return {
platform: os.platform(),
arch: os.arch(),
nodeVersion: process.version,
npmVersion: this.getNpmVersion(),
cliVersion: this.getCliVersion(),
memory: {
total: totalMem,
free: freeMem,
used: totalMem - freeMem,
percentage: ((totalMem - freeMem) / totalMem) * 100
},
cpu: {
model: cpus[0].model,
cores: cpus.length,
speed: cpus[0].speed,
loadAverage: os.loadavg()
},
network: {
interfaces: this.getNetworkInterfaces(),
connectivity: await this.checkConnectivity(),
dns: this.getDnsServers()
},
disk: diskInfo
};
}
getNpmVersion() {
try {
return (0, child_process_1.execSync)('npm --version', { encoding: 'utf8' }).trim();
}
catch {
return 'unknown';
}
}
getCliVersion() {
try {
const packagePath = path.join(__dirname, '../../package.json');
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
return pkg.version;
}
catch {
return 'unknown';
}
}
async getDiskInfo() {
try {
const platform = os.platform();
let command;
if (platform === 'darwin' || platform === 'linux') {
command = 'df -k /';
}
else if (platform === 'win32') {
command = 'wmic logicaldisk get size,freespace,caption';
}
else {
throw new Error('Unsupported platform');
}
const output = (0, child_process_1.execSync)(command, { encoding: 'utf8' });
// Parse output based on platform
if (platform === 'darwin' || platform === 'linux') {
const lines = output.trim().split('\n');
const data = lines[1].split(/\s+/);
const total = parseInt(data[1]) * 1024;
const used = parseInt(data[2]) * 1024;
const free = parseInt(data[3]) * 1024;
return {
total,
used,
free,
percentage: (used / total) * 100,
path: '/'
};
}
else {
// Windows parsing would go here
return {
total: 0,
used: 0,
free: 0,
percentage: 0,
path: 'C:\\'
};
}
}
catch {
return {
total: 0,
used: 0,
free: 0,
percentage: 0,
path: '/'
};
}
}
getNetworkInterfaces() {
const interfaces = os.networkInterfaces();
const result = [];
for (const [name, addrs] of Object.entries(interfaces)) {
if (addrs) {
for (const addr of addrs) {
result.push({
name,
address: addr.address,
netmask: addr.netmask,
family: addr.family,
internal: addr.internal
});
}
}
}
return result;
}
async checkConnectivity() {
try {
(0, child_process_1.execSync)('ping -c 1 google.com', { stdio: 'ignore' });
return true;
}
catch {
return false;
}
}
getDnsServers() {
try {
if (os.platform() === 'darwin' || os.platform() === 'linux') {
const output = (0, child_process_1.execSync)('cat /etc/resolv.conf', { encoding: 'utf8' });
const servers = output
.split('\n')
.filter(line => line.startsWith('nameserver'))
.map(line => line.split(' ')[1]);
return servers;
}
return [];
}
catch {
return [];
}
}
async runCustomCheck(check) {
try {
if (check.command) {
const output = (0, child_process_1.execSync)(check.command, { encoding: 'utf8' });
if (check.validator) {
return check.validator(output);
}
return {
status: 'info',
message: output.trim()
};
}
else if (check.script) {
// Execute custom script
const result = eval(check.script);
if (check.validator) {
return check.validator(result);
}
return {
status: 'info',
message: String(result)
};
}
return {
status: 'info',
message: 'Check completed'
};
}
catch (error) {
return {
status: 'fail',
message: `Check failed: ${error}`
};
}
}
getUniqueCategories() {
const categories = new Set();
for (const diagnostic of this.diagnostics.values()) {
categories.add(diagnostic.category);
}
for (const check of this.customChecks.values()) {
categories.add(check.category);
}
return Array.from(categories);
}
async generateCategoryGuides(category) {
const categoryPath = path.join(this.config.outputPath, category);
await fs.ensureDir(categoryPath);
// Generate guide for each troubleshooting guide in this category
const categoryGuides = Array.from(this.guides.values()).filter(guide => guide.diagnostics.some(d => d.category === category));
for (const guide of categoryGuides) {
await this.generateGuideFile(guide, categoryPath);
}
// Generate category index
await this.generateCategoryIndex(category, categoryGuides, categoryPath);
}
async generateGuideFile(guide, outputPath) {
const guidePath = path.join(outputPath, `${guide.id}.md`);
let content = `# ${guide.title}\n\n`;
content += `${guide.description}\n\n`;
content += `## Symptoms\n\n`;
for (const symptom of guide.symptoms) {
content += `- ${symptom}\n`;
}
content += '\n';
content += `## Possible Causes\n\n`;
for (const cause of guide.causes) {
content += `### ${cause.description}\n`;
content += `- **Probability**: ${cause.probability}\n`;
if (cause.checkCommand) {
content += `- **Check command**: \`${cause.checkCommand}\`\n`;
}
content += '\n';
}
content += `## Solutions\n\n`;
for (const solution of guide.solutions) {
content += `### ${solution.description}\n`;
content += `- **Effectiveness**: ${solution.effectiveness}\n`;
content += `- **Risk Level**: ${solution.riskLevel}\n`;
content += `- **Time Estimate**: ${solution.timeEstimate}\n`;
if (solution.prerequisites) {
content += `\n**Prerequisites**:\n`;
for (const prereq of solution.prerequisites) {
content += `- ${prereq}\n`;
}
}
content += `\n**Steps**:\n`;
for (const step of solution.steps) {
content += `\n${step.order}. ${step.description}\n`;
if (step.command) {
content += ` \`\`\`bash\n ${step.command}\n \`\`\`\n`;
}
if (step.code) {
content += ` \`\`\`\n ${step.code}\n \`\`\`\n`;
}
if (step.verification) {
content += ` **Verify**: ${step.verification}\n`;
}
}
content += '\n';
}
if (guide.relatedGuides) {
content += `## Related Guides\n\n`;
for (const relatedId of guide.relatedGuides) {
const related = this.guides.get(relatedId);
if (related) {
content += `- [${related.title}](./${relatedId}.md)\n`;
}
}
content += '\n';
}
content += `## Tags\n\n`;
content += guide.tags.map(tag => `\`${tag}\``).join(', ') + '\n\n';
content += `**Difficulty**: ${guide.difficulty}\n`;
await fs.writeFile(guidePath, content);
}
async generateCategoryIndex(category, guides, outputPath) {
const indexPath = path.join(outputPath, 'index.md');
let content = `# ${category.charAt(0).toUpperCase() + category.slice(1)} Troubleshooting\n\n`;
content += `This section contains troubleshooting guides for ${category}-related issues.\n\n`;
content += `## Available Guides\n\n`;
for (const guide of guides) {
content += `### [${guide.title}](./${guide.id}.md)\n`;
content += `${guide.description}\n\n`;
content += `**Symptoms**:\n`;
for (const symptom of guide.symptoms.slice(0, 3)) {
content += `- ${symptom}\n`;
}
if (guide.symptoms.length > 3) {
content += `- ...and ${guide.symptoms.length - 3} more\n`;
}
content += '\n';
}
await fs.writeFile(indexPath, content);
}
async generateIndex() {
const indexPath = path.join(this.config.outputPath, 'index.md');
let content = `# Re-Shell Troubleshooting Guide\n\n`;
content += `Welcome to the Re-Shell troubleshooting guide. This guide helps you diagnose and resolve common issues.\n\n`;
content += `## Quick Diagnostics\n\n`;
content += `Run the following command to perform automatic diagnostics:\n\n`;
content += `\`\`\`bash\nre-shell diagnose\n\`\`\`\n\n`;
content += `## Categories\n\n`;
const categories = this.getUniqueCategories();
for (const category of categories) {
const guides = Array.from(this.guides.values()).filter(g => g.diagnostics.some(d => d.category === category));
content += `- [${category.charAt(0).toUpperCase() + category.slice(1)}](./${category}/index.md) (${guides.length} guides)\n`;
}
content += '\n';
content += `## Common Issues\n\n`;
const commonGuides = Array.from(this.guides.values())
.filter(g => g.difficulty === 'beginner')
.slice(0, 5);
for (const guide of commonGuides) {
content += `- [${guide.title}](./${guide.diagnostics[0].category}/${guide.id}.md)\n`;
}
await fs.writeFile(indexPath, content);
}
async generateNavigation() {
const navPath = path.join(this.config.outputPath, 'navigation.json');
const navigation = {
sections: this.getUniqueCategories().map(category => ({
title: category.charAt(0).toUpperCase() + category.slice(1),
path: `./${category}/index.md`,
items: Array.from(this.guides.values())
.filter(g => g.diagnostics.some(d => d.category === category))
.map(g => ({
title: g.title,
path: `./${category}/${g.id}.md`,
difficulty: g.difficulty
}))
}))
};
await fs.writeJson(navPath, navigation, { spaces: 2 });
}
async exportGuides(format, report) {
switch (format) {
case 'html':
await this.exportToHtml(report);
break;
case 'pdf':
await this.exportToPdf();
break;
case 'markdown':
// Already in markdown format
break;
case 'json':
await this.exportToJson(report);
break;
case 'interactive':
await this.generateInteractiveGuide();
break;
}
}
async exportToHtml(report) {
const htmlPath = path.join(this.config.outputPath, 'html');
await fs.ensureDir(htmlPath);
// Generate main HTML page
const mainHtml = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Re-Shell Troubleshooting Guide</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.sidebar {
position: fixed;
left: 0;
top: 0;
width: 250px;
height: 100vh;
overflow-y: auto;
background: #f5f5f5;
padding: 20px;
}
.content {
margin-left: 290px;
}
.diagnostic-result {
padding: 10px;
margin: 10px 0;
border-radius: 5px;
}
.pass { background: #d4edda; color: #155724; }
.warning { background: #fff3cd; color: #856404; }
.fail { background: #f8d7da; color: #721c24; }
.info { background: #d1ecf1; color: #0c5460; }
pre {
background: #f4f4f4;
padding: 10px;
border-radius: 5px;
overflow-x: auto;
}
code {
background: #f4f4f4;
padding: 2px 4px;
border-radius: 3px;
}
</style>
</head>
<body>
<div class="sidebar">
<h2>Navigation</h2>
<ul>
<li><a href="#diagnostics">Diagnostics Report</a></li>
<li><a href="#guides">Troubleshooting Guides</a></li>
<li><a href="#recommendations">Recommendations</a></li>
</ul>
</div>
<div class="content">
<h1>Re-Shell Troubleshooting Guide</h1>
${report ? this.generateDiagnosticsHtml(report) : ''}
${this.generateGuidesHtml()}
</div>
</body>
</html>`;
await fs.writeFile(path.join(htmlPath, 'index.html'), mainHtml);
}
generateDiagnosticsHtml(report) {
let html = '<section id="diagnostics">\n';
html += '<h2>Diagnostics Report</h2>\n';
html += `<p>Generated: ${report.timestamp.toLocaleString()}</p>\n`;
html += '<h3>System Information</h3>\n';
html += '<ul>\n';
html += `<li>Platform: ${report.systemInfo.platform} (${report.systemInfo.arch})</li>\n`;
html += `<li>Node.js: ${report.systemInfo.nodeVersion}</li>\n`;
html += `<li>NPM: ${report.systemInfo.npmVersion}</li>\n`;
html += `<li>Memory: ${Math.round(report.systemInfo.memory.used / 1024 / 1024 / 1024)}GB / ${Math.round(report.systemInfo.memory.total / 1024 / 1024 / 1024)}GB</li>\n`;
html += `<li>CPU: ${report.systemInfo.cpu.model} (${report.systemInfo.cpu.cores} cores)</li>\n`;
html += '</ul>\n';
html += '<h3>Diagnostic Results</h3>\n';
for (const result of report.diagnosticResults) {
html += `<div class="diagnostic-result ${result.result.status}">\n`;
html += `<h4>${result.checkName}</h4>\n`;
html += `<p>${result.result.message}</p>\n`;
if (result.result.suggestion) {
html += `<p><strong>Suggestion:</strong> ${result.result.suggestion}</p>\n`;
}
html += '</div>\n';
}
html += '</section>\n';
return html;
}
generateGuidesHtml() {
let html = '<section id="guides">\n';
html += '<h2>Troubleshooting Guides</h2>\n';
for (const guide of this.guides.values()) {
html += `<div class="guide">\n`;
html += `<h3>${guide.title}</h3>\n`;
html += `<p>${guide.description}</p>\n`;
html += '<h4>Symptoms</h4>\n';
html += '<ul>\n';
for (const symptom of guide.symptoms) {
html += `<li>${symptom}</li>\n`;
}
html += '</ul>\n';
html += '</div>\n';
}
html += '</section>\n';
return html;
}
async exportToPdf() {
// PDF export would require a library like puppeteer
// For now, we'll create a placeholder
const pdfPath = path.join(this.config.outputPath, 'troubleshooting-guide.pdf');
await fs.writeFile(pdfPath, 'PDF export not implemented yet');
}
async exportToJson(report) {
const jsonPath = path.join(this.config.outputPath, 'troubleshooting.json');
const data = {
generated: new Date().toISOString(),
guides: Array.from(this.guides.values()),
diagnostics: Array.from(this.diagnostics.values()).map(d => ({
id: d.id,
name: d.name,
category: d.category,
description: d.description
})),
report
};
await fs.writeJson(jsonPath, data, { spaces: 2 });
}
async generateInteractiveGuide() {
const interactivePath = path.join(this.config.outputPath, 'interactive');
await fs.ensureDir(interactivePath);
// Generate interactive troubleshooting wizard
const wizardHtml = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Re-Shell Interactive Troubleshooting</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.wizard-step {
display: none;
animation: fadeIn 0.3s;
}
.wizard-step.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.symptom-list {
list-style: none;
padding: 0;
}
.symptom-item {
padding: 10px;
margin: 5px 0;
background: #f5f5f5;
border-radius: 5px;
cursor: pointer;
transition: background 0.2s;
}
.symptom-item:hover {
background: #e0e0e0;
}
.symptom-item.selected {
background: #007bff;
color: white;
}
.diagnostic-running {
padding: 20px;
text-align: center;
background: #f0f8ff;
border-radius: 5px;
}
.solution {
background: #f9f9f9;
padding: 15px;
margin: 10px 0;
border-radius: 5px;
border-left: 4px solid #28a745;
}
button {
background: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background: #0056b3;
}
.spinner {
border: 3px solid #f3f3f3;
border-top: 3px solid #007bff;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<h1>Re-Shell Interactive Troubleshooting</h1>
<div id="step1" class="wizard-step active">
<h2>What issue are you experiencing?</h2>
<ul class="symptom-list">
${this.generateSymptomsList()}
</ul>
<button onclick="nextStep()">Next</button>
</div>
<div id="step2" class="wizard-step">
<h2>Running Diagnostics</h2>
<div class="diagnostic-running">
<div class="spinner"></div>
<p>Analyzing your system...</p>
</div>
</div>
<div id="step3" class="wizard-step">
<h2>Recommended Solutions</h2>
<div id="solutions"></div>
<button onclick="runAutoFix()">Run Auto-Fix</button>
<button onclick="startOver()">Start Over</button>
</div>
<script>
let currentStep = 1;
let selectedSymptoms = [];
function selectSymptom(element, symptom) {
element.classList.toggle('selected');
if (selectedSymptoms.includes(symptom)) {
selectedSymptoms = selectedSymptoms.filter(s => s !== symptom);
} else {
selectedSymptoms.push(symptom);
}
}
function nextStep() {
if (selectedSymptoms.length === 0) {
alert('Please select at least one symptom');
return;
}
document.getElementById('step1').classList.remove('active');
document.getElementById('step2').classList.add('active');
// Simulate diagnostics
setTimeout(() => {
showSolutions();
}, 3000);
}
function showSolutions() {
document.getElementById('step2').classList.remove('active');
document.getElementById('step3').classList.add('active');
// Generate solutions based on symptoms
const solutionsDiv = document.getElementById('solutions');
solutionsDiv.innerHTML = generateSolutions(selectedSymptoms);
}
function generateSolutions(symptoms) {
// This would be dynamically generated based on the selected symptoms
return \`
<div class="solution">
<h3>Clean Install Dependencies</h3>
<p>Remove node_modules and reinstall all dependencies</p>
<pre>rm -rf node_modules package-lock.json
npm install</pre>
</div>
<div class="solution">
<h3>Clear Cache</h3>
<p>Clear npm and Re-Shell cache</p>
<pre>npm cache clean --force
re-shell cache clear</pre>
</div>
\`;
}
function runAutoFix() {
alert('Auto-fix would run here. For safety, please run the commands manually.');
}
function startOver() {
currentStep = 1;
selectedSymptoms = [];
document.querySelectorAll('.wizard-step').forEach(step => {
step.classList.remove('active');
});
document.getElementById('step1').classList.add('active');
document.querySelectorAll('.symptom-item').forEach(item => {
item.classList.remove('selected');
});
}
</script>
</body>
</html>`;
await fs.writeFile(path.join(interactivePath, 'index.html'), wizardHtml);
}
generateSymptomsList() {
const allSymptoms = new Set();
for (const guide of this.guides.values()) {
guide.symptoms.forEach(s => allSymptoms.add(s));
}
return Array.from(allSymptoms)
.map(symptom => `<li class="symptom-item" onclick="selectSymptom(this, '${symptom}')">${symptom}</li>`)
.join('\n');
}
async saveReport(report) {
const reportPath = path.join(this.config.outputPath, 'diagnostic-report.json');
await fs.writeJson(reportPath, report, { spaces: 2 });
report.exportPath = reportPath;
}
countAutoFixes() {
let count = 0;
for (const diagnostic of this.diagnostics.values()) {
if (diagnostic.autoFix) {
count++;
}
}
for (const check of this.customChecks.values()) {
if (check.autoFix) {
count++;
}
}
return count;
}
async runDiagnostic(diagnosticId) {
const diagnostic = this.diagnostics.get(diagnosticId);
if (!diagnostic) {
throw new Error(`Diagnostic ${diagnosticId} not found`);
}
return diagnostic.run();
}
async runAutoFix(diagnosticId) {
const diagnostic = this.diagnostics.get(diagnosticId);
if (!diagnostic || !diagnostic.autoFix) {
throw new Error(`Auto-fix not available for ${diagnosticId}`);
}
return diagnostic.autoFix();
}
async searchG