lsh-framework
Version: 
A powerful, extensible shell with advanced job management, database persistence, and modern CLI features
501 lines (494 loc) • 17.1 kB
JavaScript
/**
 * LSH CLI Entry Point
 * Supports interactive mode and script execution
 */
import { Command } from 'commander';
import InteractiveShell from './lib/interactive-shell.js';
import ScriptRunner from './lib/script-runner.js';
import { parseShellCommand } from './lib/shell-parser.js';
import selfCommand from './commands/self.js';
import { registerApiCommands } from './commands/api.js';
import { registerZshImportCommands } from './commands/zsh-import.js';
import { registerThemeCommands } from './commands/theme.js';
import { init_daemon } from './services/daemon/daemon.js';
import { init_ishell } from './services/shell/shell.js';
import { init_lib } from './services/lib/lib.js';
import { init_supabase } from './services/supabase/supabase.js';
import { init_cron } from './services/cron/cron.js';
import { init_secrets } from './services/secrets/secrets.js';
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Get version from package.json
function getVersion() {
    try {
        const packageJsonPath = path.join(__dirname, '../package.json');
        const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
        return packageJson.version || '0.5.1';
    }
    catch {
        return '0.5.1';
    }
}
const program = new Command();
program
    .name('lsh')
    .description('LSH - A modern shell with ZSH features and superior job management')
    .version(getVersion());
// Options for main command
program
    .option('-i, --interactive', 'Start interactive shell')
    .option('-c, --command <command>', 'Execute command string')
    .option('-s, --script <file>', 'Execute script file')
    .option('--rc <file>', 'Use custom rc file')
    .option('--zsh-compat', 'Enable ZSH compatibility mode')
    .option('--source-zshrc', 'Source ~/.zshrc configuration')
    .option('--package-manager <manager>', 'Package manager (npm, yarn, brew, apt, yum)')
    .option('-v, --verbose', 'Verbose output')
    .option('-d, --debug', 'Debug mode')
    .action(async (options) => {
    try {
        if (options.command) {
            // Execute single command
            await executeCommand(options.command, options);
        }
        else if (options.script) {
            // Execute script file
            await executeScript(options.script, options);
        }
        else if (options.interactive) {
            // Start interactive shell only if -i or --interactive is specified
            await startInteractiveShell(options);
        }
        else {
            // No arguments - show help
            program.help();
        }
    }
    catch (error) {
        console.error(`Error: ${error.message}`);
        process.exit(1);
    }
});
// Script execution subcommand
program
    .command('script <file>')
    .description('Execute a shell script')
    .option('-a, --args <args...>', 'Script arguments')
    .option('-c, --cwd <dir>', 'Working directory')
    .option('-e, --env <key=value>', 'Environment variables')
    .action(async (file, options) => {
    try {
        await executeScript(file, options);
    }
    catch (error) {
        console.error(`Script error: ${error.message}`);
        process.exit(1);
    }
});
// Configuration subcommand
program
    .command('config')
    .description('Manage LSH configuration')
    .option('--init', 'Initialize configuration')
    .option('--show', 'Show current configuration')
    .option('--validate', 'Validate configuration')
    .action(async (options) => {
    try {
        await handleConfig(options);
    }
    catch (error) {
        console.error(`Config error: ${error.message}`);
        process.exit(1);
    }
});
// ZSH compatibility subcommand
program
    .command('zsh')
    .description('ZSH compatibility commands')
    .option('--migrate', 'Migrate ZSH configuration to LSH')
    .option('--source', 'Source ZSH configuration')
    .option('--check', 'Check ZSH availability')
    .action(async (options) => {
    try {
        await handleZshCompatibility(options);
    }
    catch (error) {
        console.error(`ZSH compatibility error: ${error.message}`);
        process.exit(1);
    }
});
// Self-management commands
program.addCommand(selfCommand);
// Help subcommand
program
    .command('help')
    .description('Show detailed help')
    .action(() => {
    showDetailedHelp();
});
// Register async command modules
(async () => {
    // REPL interactive shell
    await init_ishell(program);
    // Library commands
    await init_lib(program);
    // Supabase commands
    await init_supabase(program);
    // Daemon management commands
    await init_daemon(program);
    // Cron commands
    await init_cron(program);
    // Secrets management commands
    await init_secrets(program);
    // API server commands
    registerApiCommands(program);
    // ZSH import commands
    registerZshImportCommands(program);
    // Theme commands
    registerThemeCommands(program);
    // Parse command line arguments after all commands are registered
    program.parse(process.argv);
})();
/**
 * Start interactive shell
 */
async function startInteractiveShell(options) {
    const shellOptions = {
        verbose: options.verbose,
        debug: options.debug,
    };
    // Only set rcFile if explicitly provided
    if (options.rc) {
        shellOptions.rcFile = options.rc;
    }
    const shell = new InteractiveShell(shellOptions);
    await shell.start();
}
/**
 * Execute single command
 */
async function executeCommand(command, options) {
    const { ShellExecutor } = await import('./lib/shell-executor.js');
    const executor = new ShellExecutor();
    // Load configuration if rc file specified
    if (options.rc) {
        await loadRcFile(executor, options.rc);
    }
    try {
        const ast = parseShellCommand(command);
        const result = await executor.execute(ast);
        if (result.stdout) {
            console.log(result.stdout);
        }
        if (result.stderr) {
            console.error(result.stderr);
        }
        process.exit(result.exitCode);
    }
    catch (error) {
        console.error(`Command error: ${error.message}`);
        process.exit(1);
    }
}
/**
 * Execute script file
 */
async function executeScript(scriptPath, options) {
    if (!fs.existsSync(scriptPath)) {
        console.error(`Script file not found: ${scriptPath}`);
        process.exit(1);
    }
    const runner = new ScriptRunner({
        cwd: options.cwd,
        env: parseEnvOptions(options.env),
    });
    const result = await runner.executeScript(scriptPath, {
        args: options.args || [],
    });
    if (result.output) {
        console.log(result.output);
    }
    if (result.errors) {
        console.error(result.errors);
    }
    process.exit(result.exitCode);
}
/**
 * Handle configuration commands
 */
async function handleConfig(options) {
    const rcFile = path.join(process.env.HOME || '/', '.lshrc');
    if (options.init) {
        await initializeConfig(rcFile);
    }
    else if (options.show) {
        await showConfig(rcFile);
    }
    else if (options.validate) {
        await validateConfig(rcFile);
    }
    else {
        console.log('Configuration management:');
        console.log('  --init     Initialize default configuration');
        console.log('  --show     Show current configuration');
        console.log('  --validate Validate configuration file');
    }
}
/**
 * Initialize configuration file
 */
async function initializeConfig(rcFile) {
    if (fs.existsSync(rcFile)) {
        console.log(`Configuration file already exists: ${rcFile}`);
        return;
    }
    const defaultConfig = `# LSH Configuration File
# This file is executed when LSH starts in interactive mode
# Enable ZSH features
setopt EXTENDED_GLOB
setopt AUTO_CD
setopt SHARE_HISTORY
setopt HIST_IGNORE_DUPS
# Set prompt
export PROMPT='%n@%m:%~$ '
export RPROMPT='%T'
# Set history options
export HISTSIZE=10000
export HISTFILE=~/.lsh_history
# Aliases
alias ll='ls -la'
alias la='ls -A'
alias l='ls -CF'
alias ..='cd ..'
alias ...='cd ../..'
# Functions
greet() {
    echo "Hello from LSH!"
}
# Welcome message
echo "LSH interactive shell loaded. Type 'help' for commands."
`;
    try {
        fs.writeFileSync(rcFile, defaultConfig, 'utf8');
        console.log(`✅ Created configuration file: ${rcFile}`);
    }
    catch (error) {
        console.error(`❌ Failed to create configuration: ${error.message}`);
    }
}
/**
 * Show current configuration
 */
async function showConfig(rcFile) {
    if (!fs.existsSync(rcFile)) {
        console.log(`❌ Configuration file not found: ${rcFile}`);
        console.log('Run "lsh config --init" to create one.');
        return;
    }
    try {
        const content = fs.readFileSync(rcFile, 'utf8');
        console.log(`📄 Configuration file: ${rcFile}`);
        console.log('='.repeat(50));
        console.log(content);
    }
    catch (error) {
        console.error(`❌ Failed to read configuration: ${error.message}`);
    }
}
/**
 * Validate configuration file
 */
async function validateConfig(rcFile) {
    if (!fs.existsSync(rcFile)) {
        console.log(`❌ Configuration file not found: ${rcFile}`);
        return;
    }
    try {
        const content = fs.readFileSync(rcFile, 'utf8');
        const lines = content.split('\n');
        let valid = true;
        const errors = [];
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i].trim();
            if (line.startsWith('#') || line === '') {
                continue;
            }
            try {
                parseShellCommand(line);
            }
            catch (error) {
                valid = false;
                errors.push(`Line ${i + 1}: ${error.message}`);
            }
        }
        if (valid) {
            console.log(`✅ Configuration file is valid: ${rcFile}`);
        }
        else {
            console.log(`❌ Configuration file has errors: ${rcFile}`);
            errors.forEach(error => console.log(`  ${error}`));
        }
    }
    catch (error) {
        console.error(`❌ Failed to validate configuration: ${error.message}`);
    }
}
/**
 * Load rc file
 */
async function loadRcFile(executor, rcFile) {
    if (!fs.existsSync(rcFile)) {
        console.error(`Configuration file not found: ${rcFile}`);
        return;
    }
    try {
        const content = fs.readFileSync(rcFile, 'utf8');
        const lines = content.split('\n');
        for (const line of lines) {
            const trimmed = line.trim();
            if (trimmed.startsWith('#') || trimmed === '') {
                continue;
            }
            try {
                const ast = parseShellCommand(trimmed);
                await executor.execute(ast);
            }
            catch (error) {
                console.error(`Config error: ${error.message}`);
            }
        }
    }
    catch (error) {
        console.error(`Failed to load configuration: ${error.message}`);
    }
}
/**
 * Parse environment variable options
 */
function parseEnvOptions(envOptions) {
    const env = {};
    if (envOptions) {
        for (const option of envOptions) {
            const [key, value] = option.split('=', 2);
            if (key && value !== undefined) {
                env[key] = value;
            }
        }
    }
    return env;
}
/**
 * Handle ZSH compatibility commands
 */
async function handleZshCompatibility(options) {
    const { ShellExecutor } = await import('./lib/shell-executor.js');
    const executor = new ShellExecutor();
    if (options.migrate) {
        const result = await executor.getZshCompatibility().migrateZshConfig();
        console.log(result.message);
        process.exit(result.success ? 0 : 1);
    }
    else if (options.source) {
        const result = await executor.getZshCompatibility().sourceZshConfig();
        console.log(result.message);
        process.exit(result.success ? 0 : 1);
    }
    else if (options.check) {
        const result = await executor.getZshCompatibility().checkZshAvailability();
        if (result.available) {
            console.log(`✅ ZSH is available: version ${result.version}`);
            console.log(`   Path: ${result.path}`);
        }
        else {
            console.log('❌ ZSH is not available on this system');
        }
    }
    else {
        console.log('ZSH Compatibility Commands:');
        console.log('  --migrate  Migrate ZSH configuration to LSH');
        console.log('  --source   Source ZSH configuration');
        console.log('  --check    Check ZSH availability');
    }
}
/**
 * Show detailed help
 */
function showDetailedHelp() {
    console.log('LSH - Modern Shell with ZSH Features');
    console.log('====================================');
    console.log('');
    console.log('Usage:');
    console.log('  lsh                    Show help (default)');
    console.log('  lsh -i                 Start interactive shell');
    console.log('  lsh -c "command"       Execute command string');
    console.log('  lsh -s script.sh       Execute script file');
    console.log('  lsh script.sh          Execute script file');
    console.log('');
    console.log('Options:');
    console.log('  -i, --interactive      Start interactive shell');
    console.log('  -c, --command <cmd>    Execute command string');
    console.log('  -s, --script <file>    Execute script file');
    console.log('  --rc <file>            Use custom rc file');
    console.log('  -v, --verbose          Verbose output');
    console.log('  -d, --debug            Debug mode');
    console.log('  -h, --help             Show help');
    console.log('  -V, --version          Show version');
    console.log('');
    console.log('Subcommands:');
    console.log('  repl                    JavaScript REPL interactive shell');
    console.log('  lib                     Library commands');
    console.log('  supabase                Supabase database management');
    console.log('  script <file>           Execute shell script');
    console.log('  config                  Manage configuration');
    console.log('  zsh                     ZSH compatibility commands');
    console.log('  zsh-import              Import ZSH configs (aliases, functions, exports)');
    console.log('  theme                   Manage themes (import Oh-My-Zsh themes)');
    console.log('  self                    Self-management (update, version)');
    console.log('  daemon                  Daemon management');
    console.log('  daemon job              Job management');
    console.log('  daemon db               Database integration');
    console.log('  cron                    Cron job management');
    console.log('  api                     API server management');
    console.log('  help                    Show detailed help');
    console.log('');
    console.log('Examples:');
    console.log('');
    console.log('  Shell Usage:');
    console.log('    lsh                                  # Show this help');
    console.log('    lsh -i                               # Start interactive shell');
    console.log('    lsh repl                            # Start JavaScript REPL');
    console.log('    lsh -c "echo hello && pwd"          # Execute command');
    console.log('    lsh my-script.sh arg1 arg2          # Execute script');
    console.log('');
    console.log('  Configuration:');
    console.log('    lsh config --init                   # Initialize config');
    console.log('    lsh config --show                   # Show config');
    console.log('    lsh self version                    # Show version');
    console.log('    lsh self update                     # Update to latest');
    console.log('');
    console.log('  Daemon & Job Management:');
    console.log('    lsh daemon start                    # Start daemon');
    console.log('    lsh daemon status                   # Check daemon status');
    console.log('    lsh daemon job list                 # List all jobs');
    console.log('    lsh daemon job create               # Create new job');
    console.log('    lsh daemon job trigger <id>         # Run job immediately');
    console.log('');
    console.log('  API Server:');
    console.log('    lsh api start                       # Start daemon with API');
    console.log('    lsh api key                         # Generate API key');
    console.log('    lsh api test                        # Test API connection');
    console.log('    lsh api example -l python           # Show Python client code');
    console.log('');
    console.log('Features:');
    console.log('  ✅ POSIX Shell Compliance (85-95%)');
    console.log('  ✅ ZSH Features (arrays, globbing, floating point)');
    console.log('  ✅ Advanced Job Management');
    console.log('  ✅ Interactive Mode with History & Completion');
    console.log('  ✅ Configuration via ~/.lshrc');
    console.log('  ✅ Script Execution');
    console.log('  ✅ Command Line Interface');
}