351 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			351 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import * as plugins from './moxytool.plugins.ts';
 | 
						||
import * as paths from './moxytool.paths.ts';
 | 
						||
import { logger } from './moxytool.logging.ts';
 | 
						||
import { ScriptIndex } from './moxytool.classes.scriptindex.ts';
 | 
						||
import { ScriptRunner } from './moxytool.classes.scriptrunner.ts';
 | 
						||
 | 
						||
export const runCli = async () => {
 | 
						||
  const smartshellInstance = new plugins.smartshell.Smartshell({
 | 
						||
    executor: 'bash',
 | 
						||
  });
 | 
						||
 | 
						||
  const smartcliInstance = new plugins.smartcli.Smartcli();
 | 
						||
 | 
						||
  // Initialize script index and check if refresh is needed
 | 
						||
  const scriptIndex = new ScriptIndex();
 | 
						||
 | 
						||
  // Silently check and refresh index in the background if needed
 | 
						||
  (async () => {
 | 
						||
    try {
 | 
						||
      await scriptIndex.loadCache();
 | 
						||
      if (await scriptIndex.needsRefresh()) {
 | 
						||
        // Don't block CLI startup, refresh in background
 | 
						||
        scriptIndex.fetchIndex().catch(() => {
 | 
						||
          // Silently fail, will use cached data
 | 
						||
        });
 | 
						||
      }
 | 
						||
    } catch {
 | 
						||
      // Silently fail on index errors
 | 
						||
    }
 | 
						||
  })();
 | 
						||
 | 
						||
  // Standard command (no arguments)
 | 
						||
  smartcliInstance.standardCommand().subscribe(async () => {
 | 
						||
    logger.log('info', 'MOXYTOOL - Proxmox Administration Tool');
 | 
						||
    logger.log('info', '');
 | 
						||
    logger.log('info', 'Available commands:');
 | 
						||
    logger.log('info', '* vgpu-setup - Install and configure Proxmox vGPU support');
 | 
						||
    logger.log('info', '* scripts    - Manage Proxmox community scripts');
 | 
						||
    logger.log('info', '');
 | 
						||
    logger.log('info', 'Usage: moxytool <command> [options]');
 | 
						||
  });
 | 
						||
 | 
						||
  // vGPU setup command
 | 
						||
  smartcliInstance.addCommand('vgpu-setup').subscribe(async (argvArg) => {
 | 
						||
    logger.log('ok', 'Starting Proxmox vGPU setup process');
 | 
						||
    logger.log('info', 'This will install vGPU support for NVIDIA GPUs on Proxmox');
 | 
						||
    logger.log('info', '');
 | 
						||
 | 
						||
    // Check for arguments
 | 
						||
    const step = argvArg.step;
 | 
						||
    const url = argvArg.url;
 | 
						||
    const file = argvArg.file;
 | 
						||
    const debug = argvArg.debug;
 | 
						||
 | 
						||
    // Check if running on Proxmox
 | 
						||
    try {
 | 
						||
      const result = await smartshellInstance.exec('which pveversion');
 | 
						||
      if (result.exitCode !== 0) {
 | 
						||
        logger.log('error', 'This system does not appear to be running Proxmox');
 | 
						||
        logger.log('error', 'Please run this command on a Proxmox host');
 | 
						||
        Deno.exit(1);
 | 
						||
      }
 | 
						||
    } catch (e) {
 | 
						||
      logger.log('error', 'Failed to verify Proxmox installation');
 | 
						||
      logger.log('error', 'Please ensure you are running this on a Proxmox host');
 | 
						||
      Deno.exit(1);
 | 
						||
    }
 | 
						||
 | 
						||
    // Create temporary directory
 | 
						||
    const tmpDir = '/tmp/moxytool-vgpu-setup';
 | 
						||
    await smartshellInstance.exec(`mkdir -p ${tmpDir}`);
 | 
						||
 | 
						||
    try {
 | 
						||
      // Step 1: Clone and run the installer script
 | 
						||
      logger.log('info', 'Step 1: Downloading Proxmox vGPU installer (supports v9)...');
 | 
						||
      const cloneResult = await smartshellInstance.exec(
 | 
						||
        `cd ${tmpDir} && git clone https://github.com/anomixer/proxmox-vgpu-installer.git`,
 | 
						||
      );
 | 
						||
 | 
						||
      if (cloneResult.exitCode !== 0) {
 | 
						||
        logger.log('error', 'Failed to clone the installer repository');
 | 
						||
        logger.log('error', cloneResult.stderr || 'Unknown error');
 | 
						||
        Deno.exit(1);
 | 
						||
      }
 | 
						||
 | 
						||
      logger.log('ok', 'Installer downloaded successfully');
 | 
						||
      logger.log('info', '');
 | 
						||
 | 
						||
      // Build command with arguments
 | 
						||
      let command = 'bash proxmox-installer.sh';
 | 
						||
      if (step) {
 | 
						||
        command += ` --step ${step}`;
 | 
						||
      }
 | 
						||
      if (url) {
 | 
						||
        command += ` --url ${url}`;
 | 
						||
      }
 | 
						||
      if (file) {
 | 
						||
        command += ` --file ${file}`;
 | 
						||
      }
 | 
						||
      if (debug) {
 | 
						||
        command += ' --debug';
 | 
						||
      }
 | 
						||
 | 
						||
      logger.log('info', 'Running installer script...');
 | 
						||
      logger.log('info', 'Please follow the interactive prompts');
 | 
						||
      logger.log('info', '');
 | 
						||
 | 
						||
      // Run the installer script interactively
 | 
						||
      const installResult = await smartshellInstance.exec(
 | 
						||
        `cd ${tmpDir}/proxmox-vgpu-installer && ${command}`,
 | 
						||
      );
 | 
						||
 | 
						||
      if (installResult.exitCode !== 0) {
 | 
						||
        logger.log('error', 'Installer script failed');
 | 
						||
        logger.log('error', installResult.stderr || 'Unknown error');
 | 
						||
        Deno.exit(1);
 | 
						||
      }
 | 
						||
 | 
						||
      logger.log('ok', 'vGPU setup process completed');
 | 
						||
      logger.log('info', '');
 | 
						||
      logger.log('info', 'Next steps:');
 | 
						||
      logger.log('info', '1. If prompted, reboot your system');
 | 
						||
      logger.log('info', '2. After reboot, run this command again to continue setup');
 | 
						||
      logger.log('info', '3. Verify installation with: mdevctl types');
 | 
						||
      logger.log('info', '4. Configure your VMs with vGPU in the Proxmox web UI');
 | 
						||
    } catch (error) {
 | 
						||
      logger.log('error', `Setup failed: ${error instanceof Error ? error.message : String(error)}`);
 | 
						||
      Deno.exit(1);
 | 
						||
    }
 | 
						||
  });
 | 
						||
 | 
						||
  // Scripts management commands
 | 
						||
  smartcliInstance.addCommand('scripts').subscribe(async (argvArg) => {
 | 
						||
    const subcommand = argvArg._[0];
 | 
						||
 | 
						||
    if (!subcommand) {
 | 
						||
      logger.log('info', 'MOXYTOOL Scripts - Proxmox Community Scripts Management');
 | 
						||
      logger.log('info', '');
 | 
						||
      logger.log('info', 'Available subcommands:');
 | 
						||
      logger.log('info', '* list              - List all available scripts');
 | 
						||
      logger.log('info', '* search <query>    - Search for scripts by name or description');
 | 
						||
      logger.log('info', '* info <slug>       - Show detailed information about a script');
 | 
						||
      logger.log('info', '* run <slug>        - Execute a script');
 | 
						||
      logger.log('info', '* refresh           - Force refresh the script index');
 | 
						||
      logger.log('info', '');
 | 
						||
      logger.log('info', 'Usage: moxytool scripts <subcommand> [options]');
 | 
						||
      return;
 | 
						||
    }
 | 
						||
 | 
						||
    // Ensure index is loaded
 | 
						||
    await scriptIndex.loadCache();
 | 
						||
 | 
						||
    switch (subcommand) {
 | 
						||
      case 'list': {
 | 
						||
        const scripts = scriptIndex.getAll();
 | 
						||
 | 
						||
        if (scripts.length === 0) {
 | 
						||
          logger.log('warn', 'No scripts found. Run "moxytool scripts refresh" to fetch the index.');
 | 
						||
          return;
 | 
						||
        }
 | 
						||
 | 
						||
        const stats = scriptIndex.getStats();
 | 
						||
        logger.log('info', `Available Scripts (${stats.count} total, indexed ${stats.age})`);
 | 
						||
        logger.log('info', '');
 | 
						||
 | 
						||
        // Group by type
 | 
						||
        const containers = scripts.filter(s => s.type === 'ct');
 | 
						||
        const vms = scripts.filter(s => s.type === 'vm');
 | 
						||
 | 
						||
        if (containers.length > 0) {
 | 
						||
          logger.log('info', 'Containers (LXC):');
 | 
						||
          containers.forEach(script => {
 | 
						||
            logger.log('info', `  • ${script.slug.padEnd(25)} - ${script.name}`);
 | 
						||
          });
 | 
						||
          logger.log('info', '');
 | 
						||
        }
 | 
						||
 | 
						||
        if (vms.length > 0) {
 | 
						||
          logger.log('info', 'Virtual Machines:');
 | 
						||
          vms.forEach(script => {
 | 
						||
            logger.log('info', `  • ${script.slug.padEnd(25)} - ${script.name}`);
 | 
						||
          });
 | 
						||
        }
 | 
						||
 | 
						||
        logger.log('info', '');
 | 
						||
        logger.log('info', 'Use "moxytool scripts info <slug>" for more details');
 | 
						||
        logger.log('info', 'Use "moxytool scripts run <slug>" to install');
 | 
						||
        break;
 | 
						||
      }
 | 
						||
 | 
						||
      case 'search': {
 | 
						||
        const query = argvArg._[1];
 | 
						||
 | 
						||
        if (!query) {
 | 
						||
          logger.log('error', 'Please provide a search query');
 | 
						||
          logger.log('info', 'Usage: moxytool scripts search <query>');
 | 
						||
          return;
 | 
						||
        }
 | 
						||
 | 
						||
        const results = scriptIndex.search(query as string);
 | 
						||
 | 
						||
        if (results.length === 0) {
 | 
						||
          logger.log('warn', `No scripts found matching "${query}"`);
 | 
						||
          return;
 | 
						||
        }
 | 
						||
 | 
						||
        logger.log('info', `Found ${results.length} script(s) matching "${query}":`);
 | 
						||
        logger.log('info', '');
 | 
						||
 | 
						||
        results.forEach(script => {
 | 
						||
          logger.log('info', `${script.slug} (${script.type})`);
 | 
						||
          logger.log('info', `  ${script.name}`);
 | 
						||
          logger.log('info', `  ${script.description.substring(0, 80)}...`);
 | 
						||
          logger.log('info', '');
 | 
						||
        });
 | 
						||
 | 
						||
        logger.log('info', 'Use "moxytool scripts info <slug>" for more details');
 | 
						||
        break;
 | 
						||
      }
 | 
						||
 | 
						||
      case 'info': {
 | 
						||
        const slug = argvArg._[1];
 | 
						||
 | 
						||
        if (!slug) {
 | 
						||
          logger.log('error', 'Please provide a script slug');
 | 
						||
          logger.log('info', 'Usage: moxytool scripts info <slug>');
 | 
						||
          return;
 | 
						||
        }
 | 
						||
 | 
						||
        const script = scriptIndex.getBySlug(slug as string);
 | 
						||
 | 
						||
        if (!script) {
 | 
						||
          logger.log('error', `Script "${slug}" not found`);
 | 
						||
          logger.log('info', 'Use "moxytool scripts search <query>" to find scripts');
 | 
						||
          return;
 | 
						||
        }
 | 
						||
 | 
						||
        logger.log('info', '═'.repeat(60));
 | 
						||
        logger.log('info', `${script.name}`);
 | 
						||
        logger.log('info', '═'.repeat(60));
 | 
						||
        logger.log('info', '');
 | 
						||
        logger.log('info', `Slug: ${script.slug}`);
 | 
						||
        logger.log('info', `Type: ${script.type === 'ct' ? 'Container (LXC)' : 'Virtual Machine'}`);
 | 
						||
        logger.log('info', '');
 | 
						||
        logger.log('info', 'Description:');
 | 
						||
        logger.log('info', script.description);
 | 
						||
        logger.log('info', '');
 | 
						||
 | 
						||
        if (script.install_methods && script.install_methods[0]?.resources) {
 | 
						||
          const res = script.install_methods[0].resources;
 | 
						||
          logger.log('info', 'Resource Requirements:');
 | 
						||
          if (res.cpu) logger.log('info', `  CPU: ${res.cpu} cores`);
 | 
						||
          if (res.ram) logger.log('info', `  RAM: ${res.ram} MB`);
 | 
						||
          if (res.hdd) logger.log('info', `  Disk: ${res.hdd} GB`);
 | 
						||
          if (res.os) logger.log('info', `  OS: ${res.os} ${res.version || ''}`);
 | 
						||
          logger.log('info', '');
 | 
						||
        }
 | 
						||
 | 
						||
        if (script.interface_port) {
 | 
						||
          logger.log('info', `Web Interface: http://<your-ip>:${script.interface_port}`);
 | 
						||
          logger.log('info', '');
 | 
						||
        }
 | 
						||
 | 
						||
        if (script.default_credentials) {
 | 
						||
          logger.log('info', 'Default Credentials:');
 | 
						||
          if (script.default_credentials.username) {
 | 
						||
            logger.log('info', `  Username: ${script.default_credentials.username}`);
 | 
						||
          }
 | 
						||
          if (script.default_credentials.password) {
 | 
						||
            logger.log('info', `  Password: ${script.default_credentials.password}`);
 | 
						||
          }
 | 
						||
          logger.log('info', '');
 | 
						||
        }
 | 
						||
 | 
						||
        if (script.notes && script.notes.length > 0) {
 | 
						||
          logger.log('info', 'Important Notes:');
 | 
						||
          script.notes.forEach(note => {
 | 
						||
            const prefix = note.type === 'warning' ? '⚠️  ' : 'ℹ️  ';
 | 
						||
            logger.log('warn', `${prefix}${note.content}`);
 | 
						||
          });
 | 
						||
          logger.log('info', '');
 | 
						||
        }
 | 
						||
 | 
						||
        if (script.documentation) {
 | 
						||
          logger.log('info', `Documentation: ${script.documentation}`);
 | 
						||
        }
 | 
						||
        if (script.website) {
 | 
						||
          logger.log('info', `Website: ${script.website}`);
 | 
						||
        }
 | 
						||
 | 
						||
        logger.log('info', '');
 | 
						||
        logger.log('info', `To install: sudo moxytool scripts run ${script.slug}`);
 | 
						||
        logger.log('info', '═'.repeat(60));
 | 
						||
        break;
 | 
						||
      }
 | 
						||
 | 
						||
      case 'run': {
 | 
						||
        const slug = argvArg._[1];
 | 
						||
 | 
						||
        if (!slug) {
 | 
						||
          logger.log('error', 'Please provide a script slug');
 | 
						||
          logger.log('info', 'Usage: sudo moxytool scripts run <slug>');
 | 
						||
          return;
 | 
						||
        }
 | 
						||
 | 
						||
        const script = scriptIndex.getBySlug(slug as string);
 | 
						||
 | 
						||
        if (!script) {
 | 
						||
          logger.log('error', `Script "${slug}" not found`);
 | 
						||
          logger.log('info', 'Use "moxytool scripts search <query>" to find scripts');
 | 
						||
          return;
 | 
						||
        }
 | 
						||
 | 
						||
        // Validate Proxmox host
 | 
						||
        const runner = new ScriptRunner();
 | 
						||
        const isProxmox = await runner.validateProxmoxHost();
 | 
						||
 | 
						||
        if (!isProxmox) {
 | 
						||
          logger.log('error', 'This system does not appear to be running Proxmox');
 | 
						||
          logger.log('error', 'Community scripts can only be run on Proxmox hosts');
 | 
						||
          Deno.exit(1);
 | 
						||
        }
 | 
						||
 | 
						||
        // Execute the script
 | 
						||
        const exitCode = await runner.execute(script);
 | 
						||
        Deno.exit(exitCode);
 | 
						||
      }
 | 
						||
 | 
						||
      case 'refresh': {
 | 
						||
        logger.log('info', 'Refreshing script index...');
 | 
						||
 | 
						||
        try {
 | 
						||
          await scriptIndex.fetchIndex();
 | 
						||
          const stats = scriptIndex.getStats();
 | 
						||
          logger.log('success', `Index refreshed: ${stats.count} scripts cached`);
 | 
						||
        } catch (error) {
 | 
						||
          logger.log('error', `Failed to refresh index: ${error}`);
 | 
						||
          Deno.exit(1);
 | 
						||
        }
 | 
						||
        break;
 | 
						||
      }
 | 
						||
 | 
						||
      default:
 | 
						||
        logger.log('error', `Unknown subcommand: ${subcommand}`);
 | 
						||
        logger.log('info', 'Run "moxytool scripts" to see available subcommands');
 | 
						||
    }
 | 
						||
  });
 | 
						||
 | 
						||
  smartcliInstance.startParse();
 | 
						||
};
 |