UNPKG

freshroute-server

Version:

Local development server for FreshRoute extension with API mocking and extended functionality

1,173 lines (986 loc) • 89.2 kB
#!/usr/bin/env node import { Command } from 'commander'; import chalk from 'chalk'; import ora from 'ora'; import open from 'open'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { spawn } from 'child_process'; import fs from 'fs/promises'; import os from 'os'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const packageRoot = join(__dirname, '..'); const program = new Command(); // Package info const packageInfo = JSON.parse( await fs.readFile(join(packageRoot, 'package.json'), 'utf8') ); program .name('freshroute') .description('FreshRoute Development Server CLI') .version(packageInfo.version); // Start server command program .command('start') .description('Start the FreshRoute development server') .option('-p, --port <port>', 'HTTP server port', '3001') .option('-w, --ws-port <port>', 'WebSocket server port', '3002') .option('-d, --dev', 'Start in development mode with auto-restart') .option('-s, --silent', 'Run server in background (daemon mode)') .action(async (options) => { // Check if server is already running const isRunning = await checkServerRunning(options.port); if (isRunning) { console.log(chalk.yellow(`āš ļø FreshRoute server is already running on port ${options.port}`)); console.log(chalk.blue('šŸ’” Use "freshroute stop" to stop the server first')); return; } if (!options.silent) { console.log(chalk.blue.bold('šŸš€ Starting FreshRoute Server...')); } const serverScript = join(packageRoot, 'server.js'); const env = { ...process.env, PORT: options.port, WS_PORT: options.wsPort }; // Determine stdio mode based on silent flag const stdio = options.silent ? 'ignore' : 'inherit'; const detached = options.silent; if (options.dev) { if (!options.silent) { console.log(chalk.yellow('šŸ“¦ Development mode - auto-restart enabled')); } const nodemon = spawn('npx', ['nodemon', serverScript], { cwd: packageRoot, env, stdio, detached }); if (options.silent) { // Save PID for stopping later await savePID(nodemon.pid, options.port); nodemon.unref(); console.log(chalk.green(`āœ… FreshRoute server started in background on port ${options.port}`)); console.log(chalk.blue(`šŸ“‹ Use "freshroute stop" to stop the server`)); } nodemon.on('error', (error) => { if (!options.silent) { console.error(chalk.red('āŒ Failed to start server:'), error.message); } process.exit(1); }); } else { const server = spawn('node', [serverScript], { cwd: packageRoot, env, stdio, detached }); if (options.silent) { // Save PID for stopping later await savePID(server.pid, options.port); server.unref(); console.log(chalk.green(`āœ… FreshRoute server started in background on port ${options.port}`)); console.log(chalk.blue(`šŸ“‹ Use "freshroute stop" to stop the server`)); } server.on('error', (error) => { if (!options.silent) { console.error(chalk.red('āŒ Failed to start server:'), error.message); } process.exit(1); }); } }); // Stop server command program .command('stop') .description('Stop the FreshRoute development server') .option('-p, --port <port>', 'HTTP server port to stop', '3001') .option('--all', 'Stop all running FreshRoute servers') .action(async (options) => { const spinner = ora('Stopping FreshRoute server...').start(); try { if (options.all) { const stopped = await stopAllServers(); if (stopped > 0) { spinner.succeed(chalk.green(`āœ… Stopped ${stopped} FreshRoute server(s)`)); } else { spinner.info(chalk.yellow('ā„¹ļø No running FreshRoute servers found')); } } else { const stopped = await stopServer(options.port); if (stopped) { spinner.succeed(chalk.green(`āœ… FreshRoute server stopped (port ${options.port})`)); } else { spinner.info(chalk.yellow(`ā„¹ļø No FreshRoute server running on port ${options.port}`)); } } } catch (error) { spinner.fail(chalk.red(`āŒ Failed to stop server: ${error.message}`)); process.exit(1); } }); // Status command program .command('status') .description('Check FreshRoute server status') .option('-p, --port <port>', 'HTTP server port to check', '3001') .action(async (options) => { const spinner = ora('Checking server status...').start(); try { const isRunning = await checkServerRunning(options.port); const pidInfo = await getPIDInfo(options.port); if (isRunning) { spinner.succeed(chalk.green(`āœ… FreshRoute server is running on port ${options.port}`)); if (pidInfo) { console.log(chalk.blue(`šŸ“‹ Process ID: ${pidInfo.pid}`)); console.log(chalk.blue(`šŸ“… Started: ${new Date(pidInfo.startTime).toLocaleString()}`)); } console.log(chalk.blue(`🌐 Dashboard: http://localhost:${options.port}`)); console.log(chalk.blue(`šŸ” Health check: http://localhost:${options.port}/health`)); } else { spinner.info(chalk.yellow(`ā„¹ļø No FreshRoute server running on port ${options.port}`)); console.log(chalk.blue(`šŸ’” Start with: freshroute start`)); console.log(chalk.blue(`šŸ’” Start in background: freshroute start --silent`)); } } catch (error) { spinner.fail(chalk.red(`āŒ Failed to check status: ${error.message}`)); process.exit(1); } }); // Install extension command program .command('install-extension') .description('Install the FreshRoute Chrome extension') .option('--open-chrome', 'Open Chrome extensions page after extraction') .action(async (options) => { console.log(chalk.blue.bold('šŸ“¦ Installing FreshRoute Chrome Extension...\n')); console.log(chalk.yellow('ā„¹ļø Important: This extension is designed for DEVELOPMENT use')); console.log(chalk.yellow(' It will be available when using: ') + chalk.green('freshroute open-chrome')); console.log(chalk.yellow(' It will NOT appear in your regular Chrome profile\n')); const spinner = ora('Installing Chrome extension...').start(); try { // Check if pre-built extension exists const extensionPath = join(packageRoot, 'extension'); const extensionExists = await fs.access(extensionPath).then(() => true).catch(() => false); if (!extensionExists) { spinner.fail(chalk.red('āŒ Pre-built extension not found in package')); console.log(chalk.yellow('šŸ’” The extension should be included in the npm package')); console.log(chalk.yellow('šŸ’” Try reinstalling: npm install -g freshroute-server')); return; } const { installExtension } = await import('../scripts/install-extension.js'); await installExtension(); spinner.succeed(chalk.green('āœ… Extension installed successfully!')); console.log(chalk.cyan('\nšŸ”§ How to use the extension:')); console.log('1. Start the server: ' + chalk.green('freshroute start')); console.log('2. Open development Chrome: ' + chalk.green('freshroute open-chrome')); console.log('3. The extension will be automatically loaded in development mode'); console.log(chalk.yellow('\nāš ļø The extension is NOT installed in your regular Chrome profile')); console.log(chalk.yellow(' Use ') + chalk.green('freshroute open-chrome') + chalk.yellow(' to access it')); if (options.openChrome) { console.log(chalk.blue('\n🌐 Opening Chrome development mode...')); // Import and run open-chrome functionality const userHome = os.homedir(); const extensionInstallPath = join(userHome, '.freshroute', 'extension'); const tempDir = join(os.tmpdir(), 'chrome-freshroute-dev'); const profileDir = join(tempDir, 'freshroute-dev'); await fs.mkdir(profileDir, { recursive: true }); const platform = process.platform; let chromeExecutable; switch (platform) { case 'darwin': chromeExecutable = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; break; case 'win32': chromeExecutable = 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'; if (!await fs.access(chromeExecutable).then(() => true).catch(() => false)) { chromeExecutable = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'; } break; default: chromeExecutable = 'google-chrome'; } const chromeFlags = [ `--user-data-dir=${profileDir}`, `--load-extension=${extensionInstallPath}`, '--disable-web-security', '--disable-features=TranslateUI', '--disable-background-timer-throttling', '--disable-renderer-backgrounding', '--disable-backgrounding-occluded-windows', '--disable-background-networking', '--disable-sync', '--disable-default-apps', '--no-first-run', '--no-default-browser-check', '--disable-dev-shm-usage', '--disable-gpu-sandbox', '--disable-software-rasterizer', '--aggressive-cache-discard', '--memory-pressure-off', 'http://localhost:3001/health' ]; try { const chromeProcess = spawn(chromeExecutable, chromeFlags, { detached: true, stdio: 'ignore' }); chromeProcess.unref(); console.log(chalk.green('āœ… Chrome opened with FreshRoute extension loaded!')); } catch (error) { console.log(chalk.red('āŒ Failed to open Chrome automatically')); console.log(chalk.yellow('šŸ’” Use: freshroute open-chrome')); } } } catch (error) { spinner.fail(chalk.red(`āŒ Installation failed: ${error.message}`)); process.exit(1); } }); // Manual installation guide command program .command('install-manual') .description('Show instructions for manually installing extension in regular Chrome') .action(async () => { console.log(chalk.blue.bold('šŸ”§ Manual Installation Guide\n')); // Check if extension is available const userHome = os.homedir(); const extensionInstallPath = join(userHome, '.freshroute', 'extension'); const extensionExists = await fs.access(extensionInstallPath).then(() => true).catch(() => false); if (!extensionExists) { console.log(chalk.red('āŒ Extension not found. Run "freshroute install-extension" first.\n')); return; } console.log(chalk.green('šŸ“ Extension Location:')); console.log(chalk.gray(' ' + extensionInstallPath + '\n')); console.log(chalk.yellow('āš ļø Manual Installation (for regular Chrome profile):')); console.log('1. Open your regular Chrome browser'); console.log('2. Go to: ' + chalk.blue('chrome://extensions/')); console.log('3. Enable "Developer mode" (toggle in top right)'); console.log('4. Click "Load unpacked" button'); console.log('5. Navigate to and select: ' + chalk.green(extensionInstallPath)); console.log('6. The FreshRoute extension will be installed in your regular profile'); console.log(chalk.cyan('\n🚨 Important Notes:')); console.log('• Regular Chrome has CORS restrictions - some features may not work'); console.log('• For development, use: ' + chalk.green('freshroute open-chrome') + ' (recommended)'); console.log('• The development profile has CORS disabled and better settings'); console.log(chalk.blue('\nšŸ”— Quick Actions:')); console.log('• Copy path: ' + chalk.gray('pbcopy <<< "' + extensionInstallPath + '"') + ' (macOS)'); console.log('• Open folder: ' + chalk.gray('open "' + extensionInstallPath + '"') + ' (macOS)'); try { // Try to open the folder automatically if (process.platform === 'darwin') { const { spawn } = await import('child_process'); spawn('open', [extensionInstallPath], { stdio: 'ignore' }); console.log(chalk.green('\nāœ… Extension folder opened in Finder')); } } catch (error) { // Ignore errors for folder opening } }); // Open Chrome with CORS disabled and dev profile program .command('open-chrome') .description('Open Chrome with CORS disabled and development profile') .option('--profile <name>', 'Chrome profile name to use', 'freshroute-dev') .option('--port <port>', 'Server port to connect to', '3001') .option('--url <url>', 'URL to open initially', 'http://localhost:3001/health') .option('--debug', 'Show debug information') .action(async (options) => { const spinner = ora('Opening Chrome with development settings...').start(); try { // Create temporary user data directory const tempDir = join(os.tmpdir(), 'chrome-freshroute-dev'); const profileDir = join(tempDir, options.profile); // Ensure profile directory exists await fs.mkdir(profileDir, { recursive: true }); // Chrome executable paths for different platforms const platform = process.platform; let chromeExecutable; switch (platform) { case 'darwin': // macOS chromeExecutable = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; break; case 'win32': // Windows chromeExecutable = 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'; // Also try alternative paths if (!await fs.access(chromeExecutable).then(() => true).catch(() => false)) { chromeExecutable = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'; } break; case 'linux': // Linux chromeExecutable = 'google-chrome'; break; default: chromeExecutable = 'google-chrome'; } // Verify Chrome executable exists if (platform === 'darwin' || platform === 'win32') { const chromeExists = await fs.access(chromeExecutable).then(() => true).catch(() => false); if (!chromeExists) { spinner.fail(chalk.red('āŒ Chrome executable not found')); console.log(chalk.yellow(`šŸ’” Expected Chrome at: ${chromeExecutable}`)); return; } } spinner.text = 'Starting Chrome with development settings...'; // Chrome flags for development - optimized for performance const chromeFlags = [ `--user-data-dir=${profileDir}`, '--disable-web-security', '--disable-features=TranslateUI', '--disable-background-timer-throttling', '--disable-renderer-backgrounding', '--disable-backgrounding-occluded-windows', '--disable-background-networking', '--disable-sync', '--disable-default-apps', '--disable-extensions-except', '--disable-component-extensions-with-background-pages', '--no-first-run', '--no-default-browser-check', '--disable-dev-shm-usage', '--disable-gpu-sandbox', '--disable-software-rasterizer', '--aggressive-cache-discard', '--memory-pressure-off' ]; // Add URL as last argument chromeFlags.push(options.url); spinner.succeed(chalk.green('🌐 Opening Chrome with development settings...')); if (options.debug) { console.log(chalk.gray('\nšŸ” Debug Information:')); console.log(`Chrome executable: ${chromeExecutable}`); console.log(`Profile directory: ${profileDir}`); console.log(`Chrome flags: ${chromeFlags.slice(0, 10).join(' ')} ...`); } console.log(chalk.cyan('\nšŸ”§ Chrome Development Settings:')); console.log('• CORS disabled for API testing'); console.log('• Separate development profile'); console.log('• Performance optimized for development'); console.log('• Background processes minimized'); console.log('• Extension not auto-loaded (manual loading required)'); console.log(chalk.yellow(`\nšŸ“ Profile: ${profileDir}`)); console.log(chalk.yellow(`🌐 Opening: ${options.url}`)); // Launch Chrome const chromeProcess = spawn(chromeExecutable, chromeFlags, { detached: true, stdio: 'ignore' }); chromeProcess.unref(); console.log(chalk.green('\nāœ… Chrome launched successfully!')); console.log(chalk.cyan('\nšŸ“‹ Chrome opened with development settings:')); console.log('• CORS disabled - API calls will work without CORS issues'); console.log('• Development profile - Settings isolated from your main Chrome'); console.log('• Performance optimized - Faster startup and reduced memory usage'); console.log(chalk.yellow('\nšŸ”§ To use the FreshRoute extension:')); console.log('1. Go to chrome://extensions/ in the opened browser'); console.log('2. Enable "Developer mode" (toggle in top-right)'); console.log('3. Click "Load unpacked" and select the extension folder'); console.log('4. Or run: ' + chalk.green('freshroute install-manual') + ' for detailed instructions'); console.log(chalk.cyan('\nšŸ’” This Chrome instance is separate from your regular Chrome')); console.log('• Your regular Chrome bookmarks/settings are not affected'); console.log('• Extensions and settings are isolated to this development profile'); console.log('• Close this Chrome window when done developing'); } catch (error) { spinner.fail(chalk.red(`āŒ Failed to open Chrome: ${error.message}`)); console.log(chalk.yellow('\nšŸ”§ Troubleshooting:')); console.log('• Make sure Chrome is installed'); console.log('• Check if Chrome executable path is correct'); console.log('• Try running with --debug flag for more info'); if (process.platform === 'darwin') { console.log('• macOS: Chrome should be in /Applications/'); } else if (process.platform === 'win32') { console.log('• Windows: Chrome should be in Program Files'); } else { console.log('• Linux: Install google-chrome package'); } } }); // Extension info command program .command('extension-info') .description('Show information about the bundled Chrome extension') .action(async () => { try { const extensionPath = join(packageRoot, 'extension'); const manifestPath = join(extensionPath, 'manifest.json'); const manifestExists = await fs.access(manifestPath).then(() => true).catch(() => false); if (!manifestExists) { console.log(chalk.yellow('āš ļø Extension not found in package.')); console.log(chalk.yellow('šŸ’” Try reinstalling: npm install -g freshroute-server')); return; } const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8')); // Try to read extension info if available const extensionInfoPath = join(extensionPath, 'extension-info.json'); let extensionInfo = null; try { extensionInfo = JSON.parse(await fs.readFile(extensionInfoPath, 'utf8')); } catch (error) { // Extension info file not found, that's ok } console.log(chalk.blue.bold('\nšŸ“¦ FreshRoute Chrome Extension Info:')); console.log(`Name: ${chalk.green(manifest.name)}`); console.log(`Version: ${chalk.green(manifest.version)}`); console.log(`Description: ${chalk.gray(manifest.description)}`); console.log(`Manifest Version: ${chalk.cyan(manifest.manifest_version)}`); console.log(`Location: ${chalk.gray(extensionPath)}`); if (extensionInfo) { console.log(`Built: ${chalk.gray(new Date(extensionInfo.builtAt).toLocaleString())}`); console.log(`Pre-built: ${chalk.green(extensionInfo.preBuilt ? 'Yes' : 'No')}`); } const stats = await fs.stat(extensionPath); console.log(`Package Date: ${chalk.gray(stats.mtime.toLocaleString())}`); // Check if installed const userHome = os.homedir(); const extensionInstallPath = join(userHome, '.freshroute', 'extension'); const installedExists = await fs.access(extensionInstallPath).then(() => true).catch(() => false); console.log(`Installed: ${installedExists ? chalk.green('Yes') : chalk.red('No')}`); if (installedExists) { console.log(`Install Location: ${chalk.gray(extensionInstallPath)}`); } } catch (error) { console.error(chalk.red(`āŒ Error reading extension info: ${error.message}`)); } }); // Open dashboard command program .command('dashboard') .description('Open the FreshRoute dashboard in browser') .action(async () => { const url = 'http://localhost:3001/health'; console.log(chalk.blue(`🌐 Opening dashboard: ${url}`)); try { await open(url); } catch (error) { console.error(chalk.red(`āŒ Failed to open dashboard: ${error.message}`)); console.log(chalk.yellow('Make sure the server is running first: freshroute start')); } }); // Setup command program .command('setup') .description('Complete setup: install extension and start server') .action(async () => { console.log(chalk.blue.bold('šŸ”§ Setting up FreshRoute Development Environment...\n')); // Check if extension exists in package const extensionPath = join(packageRoot, 'extension'); const extensionExists = await fs.access(extensionPath).then(() => true).catch(() => false); if (!extensionExists) { console.log(chalk.red('āŒ Pre-built extension not found in package')); console.log(chalk.yellow('šŸ’” Try reinstalling: npm install -g freshroute-server')); return; } console.log(chalk.yellow('ā„¹ļø About FreshRoute Extension:')); console.log('• Designed for development with CORS disabled'); console.log('• Uses separate Chrome profile for safety'); console.log('• Will NOT appear in your regular Chrome browser'); console.log('• Access via: ' + chalk.green('freshroute open-chrome') + '\n'); // Install extension const installSpinner = ora('Installing Chrome extension to development profile...').start(); try { const { installExtension } = await import('../scripts/install-extension.js'); await installExtension(); installSpinner.succeed('Extension installed successfully'); } catch (error) { installSpinner.fail(`Extension installation failed: ${error.message}`); return; } const userHome = os.homedir(); const extensionInstallPath = join(userHome, '.freshroute', 'extension'); console.log(chalk.green.bold('\nāœ… Setup complete!')); console.log(chalk.cyan('\nšŸš€ Development Workflow:')); console.log('1. ' + chalk.green('freshroute start --dev') + ' - Start the development server'); console.log('2. ' + chalk.green('freshroute open-chrome') + ' - Open Chrome with extension & CORS disabled'); console.log('3. Configure mock rules via the extension UI'); console.log('4. Test your application with mocked APIs'); console.log(chalk.blue('\nšŸ“ Extension Locations:')); console.log('• Development profile: ' + chalk.gray('Auto-loaded via freshroute open-chrome')); console.log('• Manual installation: ' + chalk.gray(extensionInstallPath)); console.log('• Regular Chrome: ' + chalk.yellow('Run "freshroute install-manual" for instructions')); console.log(chalk.yellow('\nāš ļø Important:')); console.log('• The extension will NOT appear in your regular Chrome'); console.log('• Use ' + chalk.green('freshroute open-chrome') + ' to access it'); console.log('• This keeps your regular browsing profile clean and safe'); console.log(chalk.red('• Developer mode must be enabled in Chrome for extensions to load')); console.log(chalk.cyan('\nšŸ”§ Next Steps:')); console.log('1. ' + chalk.green('freshroute enable-dev-mode') + ' - Enable Developer mode in Chrome'); console.log('2. ' + chalk.green('freshroute start --dev') + ' - Start the development server'); console.log('3. ' + chalk.green('freshroute open-chrome') + ' - Open Chrome with extension'); console.log(chalk.gray('\nšŸ’” If extension doesn\'t appear, run: ') + chalk.green('freshroute enable-dev-mode')); }); // Diagnostics command program .command('diagnose') .description('Diagnose extension and Chrome setup issues') .action(async () => { console.log(chalk.blue.bold('šŸ” FreshRoute Diagnostics\n')); // Check extension installation const userHome = os.homedir(); const extensionInstallPath = join(userHome, '.freshroute', 'extension'); const extensionExists = await fs.access(extensionInstallPath).then(() => true).catch(() => false); console.log(chalk.cyan('šŸ“¦ Extension Installation:')); console.log(`Extension path: ${extensionInstallPath}`); console.log(`Extension exists: ${extensionExists ? chalk.green('āœ“') : chalk.red('āœ—')}`); if (extensionExists) { // Check manifest const manifestPath = join(extensionInstallPath, 'manifest.json'); const manifestExists = await fs.access(manifestPath).then(() => true).catch(() => false); console.log(`Manifest exists: ${manifestExists ? chalk.green('āœ“') : chalk.red('āœ—')}`); if (manifestExists) { try { const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8')); console.log(`Extension name: ${chalk.green(manifest.name)}`); console.log(`Extension version: ${chalk.green(manifest.version)}`); console.log(`Manifest version: ${chalk.green(manifest.manifest_version)}`); } catch (error) { console.log(`Manifest error: ${chalk.red(error.message)}`); } } // Check key files const keyFiles = ['background.js', 'popup.html', 'options.html', 'icons/icon16.png']; for (const file of keyFiles) { const filePath = join(extensionInstallPath, file); const fileExists = await fs.access(filePath).then(() => true).catch(() => false); console.log(`${file}: ${fileExists ? chalk.green('āœ“') : chalk.red('āœ—')}`); } } // Check Chrome installation console.log(chalk.cyan('\n🌐 Chrome Installation:')); const platform = process.platform; let chromeExecutable; switch (platform) { case 'darwin': chromeExecutable = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; break; case 'win32': chromeExecutable = 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'; break; default: chromeExecutable = 'google-chrome'; } console.log(`Platform: ${platform}`); console.log(`Expected Chrome path: ${chromeExecutable}`); if (platform === 'darwin' || platform === 'win32') { const chromeExists = await fs.access(chromeExecutable).then(() => true).catch(() => false); console.log(`Chrome executable: ${chromeExists ? chalk.green('āœ“') : chalk.red('āœ—')}`); } else { console.log(`Chrome executable: ${chalk.yellow('? (check with: which google-chrome)')}`); } // Check profile directory console.log(chalk.cyan('\nšŸ“ Profile Directory:')); const tempDir = join(os.tmpdir(), 'chrome-freshroute-dev'); const profileDir = join(tempDir, 'freshroute-dev'); const profileExists = await fs.access(profileDir).then(() => true).catch(() => false); console.log(`Profile path: ${profileDir}`); console.log(`Profile exists: ${profileExists ? chalk.green('āœ“') : chalk.yellow('Will be created')}`); // Check if Chrome is running with the profile console.log(chalk.cyan('\nšŸ”§ Chrome Process Check:')); try { const { exec } = await import('child_process'); const { promisify } = await import('util'); const execAsync = promisify(exec); if (platform === 'darwin') { const { stdout } = await execAsync('ps aux | grep "chrome-freshroute-dev" | grep -v grep || true'); if (stdout.trim()) { console.log(`Chrome development process: ${chalk.green('āœ“ Running')}`); console.log(`Process info: ${chalk.gray(stdout.trim().split('\n')[0])}`); } else { console.log(`Chrome development process: ${chalk.yellow('Not running')}`); } } else { console.log(`Chrome development process: ${chalk.yellow('Manual check required')}`); } } catch (error) { console.log(`Chrome development process: ${chalk.yellow('Could not check')}`); } // Provide recommendations console.log(chalk.blue('\nšŸ’” Recommendations:')); if (!extensionExists) { console.log(chalk.yellow('• Run: freshroute install-extension')); } if (platform === 'darwin' || platform === 'win32') { const chromeExists = await fs.access(chromeExecutable).then(() => true).catch(() => false); if (!chromeExists) { console.log(chalk.yellow('• Install Google Chrome from https://www.google.com/chrome/')); } } console.log(chalk.green('• After opening Chrome with freshroute open-chrome:')); console.log(' - Check chrome://extensions/ in the development browser'); console.log(' - Look for FreshRoute in the extensions list'); console.log(' - Check if Developer mode is enabled'); console.log(' - Look for any error messages in the extension'); console.log(chalk.cyan('\nšŸš€ Test Commands:')); console.log('• freshroute open-chrome --debug # Open with debug info'); console.log('• freshroute extension-info # Show extension details'); console.log('• freshroute install-manual # Manual installation guide'); }); // Check extension status command program .command('check-extension') .description('Check if FreshRoute extension is properly loaded in Chrome') .option('--open-extensions', 'Open Chrome extensions page for manual verification') .action(async (options) => { console.log(chalk.blue.bold('šŸ” Checking FreshRoute Extension Status...\n')); // Check extension installation const userHome = os.homedir(); const extensionPath = join(userHome, '.freshroute', 'extension'); const extensionExists = await fs.access(extensionPath).then(() => true).catch(() => false); if (!extensionExists) { console.log(chalk.red('āŒ Extension not installed')); console.log(chalk.yellow('šŸ’” Run: freshroute setup')); return; } console.log(chalk.green('āœ… Extension installed at: ' + extensionPath)); // Check manifest try { const manifestPath = join(extensionPath, 'manifest.json'); const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8')); console.log(chalk.green('āœ… Manifest valid: ' + manifest.name + ' v' + manifest.version)); // Check essential files const essentialFiles = ['background.js', 'popup.html', 'options.html']; for (const file of essentialFiles) { const filePath = join(extensionPath, file); const exists = await fs.access(filePath).then(() => true).catch(() => false); if (exists) { console.log(chalk.green('āœ… ' + file + ' exists')); } else { console.log(chalk.red('āŒ ' + file + ' missing')); } } } catch (error) { console.log(chalk.red('āŒ Manifest error: ' + error.message)); return; } console.log(chalk.cyan('\n🌐 Chrome Extension Visibility Troubleshooting:\n')); console.log(chalk.yellow('1. Extension Icon Location:')); console.log(' • Look for FreshRoute icon in Chrome toolbar (top-right)'); console.log(' • If not visible, click the puzzle piece (🧩) icon'); console.log(' • Pin FreshRoute by clicking the pin icon next to it'); console.log(chalk.yellow('\n2. Extensions Page Check:')); console.log(' • Go to chrome://extensions/ in your development Chrome'); console.log(' • Look for "FreshRoute" in the list'); console.log(' • It should show as "Enabled"'); console.log(' • If "Errors" shows, click to see details'); console.log(chalk.yellow('\n3. Common Issues & Solutions:')); console.log(chalk.red(' • Extension not visible? Developer mode is probably DISABLED')); console.log(chalk.cyan(' Run: freshroute enable-dev-mode')); console.log(' • Extension disabled? Click the toggle to enable it'); console.log(' • Permission errors? The extension needs broad permissions'); console.log(' • Multiple Chrome windows? Make sure you\'re in the development profile'); console.log(chalk.yellow('\n4. Development Profile vs Regular Chrome:')); console.log(' • FreshRoute ONLY works in development profile'); console.log(' • Must use: ' + chalk.green('freshroute open-chrome')); console.log(' • Will NOT appear in regular Chrome browser'); console.log(' • Development profile path: /tmp/chrome-freshroute-dev/'); console.log(chalk.cyan('\nšŸ”§ Quick Fixes:\n')); console.log(chalk.green('Reset Development Profile:')); console.log(' freshroute open-chrome --new-session --debug'); console.log(chalk.green('\nOpen Extensions Page:')); console.log(' freshroute check-extension --open-extensions'); console.log(chalk.green('\nManual Verification:')); console.log(' 1. freshroute open-chrome --debug'); console.log(' 2. In opened Chrome, go to chrome://extensions/'); console.log(' 3. Look for "FreshRoute" in the list'); console.log(' 4. Check if it\'s enabled and has no errors'); if (options.openExtensions) { console.log(chalk.blue('\n🌐 Opening extensions page in development Chrome...')); // Open development Chrome with extensions page const tempDir = join(os.tmpdir(), 'chrome-freshroute-dev'); const profileDir = join(tempDir, 'freshroute-dev'); await fs.mkdir(profileDir, { recursive: true }); const platform = process.platform; let chromeExecutable; switch (platform) { case 'darwin': chromeExecutable = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; break; case 'win32': chromeExecutable = 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'; if (!await fs.access(chromeExecutable).then(() => true).catch(() => false)) { chromeExecutable = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'; } break; default: chromeExecutable = 'google-chrome'; } const chromeFlags = [ `--user-data-dir=${profileDir}`, `--load-extension=${extensionPath}`, '--disable-web-security', 'chrome://extensions/' ]; try { const chromeProcess = spawn(chromeExecutable, chromeFlags, { detached: true, stdio: 'ignore' }); chromeProcess.unref(); console.log(chalk.green('āœ… Chrome opened with extensions page!')); console.log(chalk.yellow('šŸ’” Look for "FreshRoute" in the extensions list')); } catch (error) { console.log(chalk.red('āŒ Failed to open Chrome: ' + error.message)); } } console.log(chalk.gray('\nšŸ’” Still having issues? Run: freshroute diagnose')); }); // Enable developer mode command program .command('enable-dev-mode') .description('Enable Chrome Developer mode for extensions (required for FreshRoute)') .option('--interactive', 'Interactive mode with step-by-step guidance') .action(async (options) => { console.log(chalk.blue.bold('šŸ”§ Chrome Developer Mode Setup...\n')); if (options.interactive) { await interactiveDeveloperModeSetup(); return; } console.log(chalk.red.bold('āš ļø IMPORTANT: Developer mode must be MANUALLY enabled')); console.log(chalk.yellow(' Chrome security prevents automatic enabling\n')); // Check extension installation const userHome = os.homedir(); const extensionInstallPath = join(userHome, '.freshroute', 'extension'); const extensionExists = await fs.access(extensionInstallPath).then(() => true).catch(() => false); if (!extensionExists) { console.log(chalk.red('āŒ Extension not installed. Run "freshroute setup" first.')); return; } console.log(chalk.green('āœ… Extension found at: ' + extensionInstallPath)); // Open Chrome to extensions page with developer mode instructions const tempDir = join(os.tmpdir(), 'chrome-freshroute-dev'); const profileDir = join(tempDir, 'freshroute-dev'); await fs.mkdir(profileDir, { recursive: true }); const platform = process.platform; let chromeExecutable; switch (platform) { case 'darwin': chromeExecutable = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; break; case 'win32': chromeExecutable = 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'; if (!await fs.access(chromeExecutable).then(() => true).catch(() => false)) { chromeExecutable = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'; } break; default: chromeExecutable = 'google-chrome'; } const chromeFlags = [ `--user-data-dir=${profileDir}`, '--disable-web-security', 'chrome://extensions/' ]; console.log(chalk.cyan('🌐 Opening Chrome Extensions page...\n')); console.log(chalk.yellow.bold('šŸ“ EXACT STEPS TO FOLLOW:')); console.log(chalk.blue('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')); console.log(chalk.green('STEP 1: ') + 'Chrome will open to chrome://extensions/'); console.log(chalk.green('STEP 2: ') + chalk.cyan.bold('Look for "Developer mode" in the TOP-RIGHT corner')); console.log(chalk.green('STEP 3: ') + chalk.yellow.bold('Click the toggle switch to turn it ON')); console.log(chalk.green('STEP 4: ') + 'New buttons should appear: "Load unpacked", "Pack extension"'); console.log(chalk.green('STEP 5: ') + chalk.cyan.bold('Click "Load unpacked"')); console.log(chalk.green('STEP 6: ') + 'Navigate to: ' + chalk.magenta(extensionInstallPath)); console.log(chalk.green('STEP 7: ') + 'Select the extension folder and click "Select Folder"'); console.log(chalk.green('STEP 8: ') + chalk.green.bold('FreshRoute should appear in extensions list!')); console.log(chalk.blue('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')); console.log(chalk.red('\n🚨 TROUBLESHOOTING IF DEVELOPER MODE TOGGLE IS MISSING:')); console.log('• Your organization might have disabled developer mode'); console.log('• Chrome might be managed by enterprise policy'); console.log('• Try using a personal Chrome profile instead'); console.log('• Check if you\'re in the right Chrome window/profile'); try { const chromeProcess = spawn(chromeExecutable, chromeFlags, { detached: true, stdio: 'ignore' }); chromeProcess.unref(); console.log(chalk.green('\nāœ… Chrome opened to extensions page!')); console.log(chalk.yellow('\nā³ After enabling Developer mode:')); console.log('• Run: ' + chalk.green('freshroute check-extension') + ' to verify'); console.log('• Then: ' + chalk.green('freshroute open-chrome --new-session')); console.log(chalk.blue('\nšŸ’” Need more help? Run:')); console.log(chalk.green('freshroute enable-dev-mode --interactive')); } catch (error) { console.log(chalk.red('āŒ Failed to open Chrome: ' + error.message)); console.log(chalk.yellow('\nšŸ”§ Manual Steps:')); console.log('1. Open Chrome manually'); console.log('2. Type: chrome://extensions/ in address bar'); console.log('3. Enable Developer mode toggle (top-right)'); console.log('4. Click "Load unpacked"'); console.log('5. Select: ' + extensionInstallPath); } }); // Interactive developer mode setup function async function interactiveDeveloperModeSetup() { console.log(chalk.blue.bold('šŸŽÆ Interactive Developer Mode Setup\n')); const userHome = os.homedir(); const extensionInstallPath = join(userHome, '.freshroute', 'extension'); console.log(chalk.cyan('šŸ“ Extension location: ') + chalk.gray(extensionInstallPath)); console.log(chalk.yellow('\nšŸ” Let\'s check your current Chrome extensions page:')); console.log('1. Open Chrome manually'); console.log('2. Go to: ' + chalk.blue('chrome://extensions/')); console.log('3. Look at the TOP-RIGHT corner'); console.log(chalk.cyan('\nā“ What do you see in the top-right corner?')); console.log(chalk.green('A) ') + 'A toggle switch labeled "Developer mode" (OFF)'); console.log(chalk.green('B) ') + 'A toggle switch labeled "Developer mode" (ON)'); console.log(chalk.green('C) ') + 'No "Developer mode" toggle at all'); console.log(chalk.green('D) ') + 'Something else / Not sure'); console.log(chalk.blue('\nšŸ“‹ Based on what you see:')); console.log(chalk.yellow('\nšŸ…°ļø If you see "Developer mode" toggle (OFF):')); console.log(' → Click it to turn it ON'); console.log(' → You should see "Load unpacked" button appear'); console.log(' → Click "Load unpacked"'); console.log(' → Navigate to: ' + chalk.cyan(extensionInstallPath)); console.log(' → Select the folder'); console.log(chalk.yellow('\nšŸ…±ļø If Developer mode is already ON:')); console.log(' → Click "Load unpacked" button'); console.log(' → Navigate to: ' + chalk.cyan(extensionInstallPath)); console.log(' → Select the folder'); console.log(chalk.red('\nšŸ…² If NO "Developer mode" toggle exists:')); console.log(' → Your Chrome is managed by organization/enterprise'); console.log(' → Developer mode is disabled by policy'); console.log(' → Try: Different Chrome profile'); console.log(' → Try: Personal Chrome installation'); console.log(' → Or use: ' + chalk.green('freshroute install-manual') + ' for regular Chrome'); console.log(chalk.yellow('\nšŸ…³ If you\'re not sure:')); console.log(' → Take a screenshot of chrome://extensions/'); console.log(' → Look for a toggle/switch in corners of the page'); console.log(' → Try refreshing the page (F5)'); console.log(chalk.green('\nšŸŽÆ Quick Copy-Paste Path:')); console.log(chalk.gray(extensionInstallPath)); console.log(chalk.blue('\nšŸ” After loading extension, verify with:')); console.log(chalk.green('freshroute check-extension --open-extensions')); } // Debug Chrome setup command program .command('debug-chrome') .description('Debug Chrome configuration and extension loading issues') .action(async () => { console.log(chalk.blue.bold('šŸ” Chrome Configuration Debug\n')); const userHome = os.homedir(); const extensionInstallPath = join(userHome, '.freshroute', 'extension'); const tempDir = join(os.tmpdir(), 'chrome-freshroute-dev'); const profileDir = join(tempDir, 'freshroute-dev'); console.log(chalk.cyan('šŸ“ File System Check:')); // Check extension const extensionExists = await fs.access(extensionInstallPath).then(() => true).catch(() => false); console.log(`Extension directory: ${extensionExists ? chalk.green('āœ“') : chalk.red('āœ—')} ${extensionInstallPath}`); if (extensionExists) { const manifestExists = await fs.access(join(extensionInstallPath, 'manifest.json')).then(() => true).catch(() => false); console.log(`Manifest file: ${manifestExists ? chalk.green('āœ“') : chalk.red('āœ—')} ${join(extensionInstallPath, 'manifest.json')}`); if (manifestExists) { try { const manifest = JSON.parse(await fs.readFile(join(extensionInstallPath, 'manifest.json'), 'utf8')); console.log(`Extension name: ${chalk.green(manifest.name)}`); console.log(`Extension version: ${chalk.green(manifest.version)}`); console.log(`Manifest version: ${chalk.green(manifest.manifest_version)}`); } catch (error) { console.log(`Manifest error: ${chalk.red(error.message)}`); } } } // Check Chrome profile const profileExists = await fs.access(profileDir).then(() => true).catch(() => false); console.log(`Chrome profile: ${profileExists ? chalk.green('āœ“') : chalk.yellow('ā—‹')} ${profileDir}`); // Check Chrome executable const platform = process.platform; let chromeExecutable; switch (platform) { case 'darwin': chromeExecutable = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'; break; case 'win32': chromeExecutable = 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'; if (!await fs.access(chromeExecutable).then(() => true).catch(() => false)) { chromeExecutable = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'; } break; default: chromeExecutable = 'google-chrome'; } const chromeExists = await fs.access(chromeExecutable).then(() => true).catch(() => false); console.log(`Chrome executable: ${chromeExists ? chalk.green('āœ“') : chalk.red('āœ—')} ${chromeExecutable}`); console.log(chalk.cyan('\nšŸ”§ Chrome Policy Check:')); console.log('Run this in terminal to check Chrome policies:'); console.log(chalk.gray('chrome://policy/ (in Chrome address bar)')); console.log(chalk.cyan('\n🌐 Common Issues:')); console.log(chalk.yellow('1. Enterprise/Organization Chrome:')); console.log(' • Developer mode disabled by IT policy'); console.log(' • Extensions blocked by organization'); console.log(' • Use personal Chrome or different profile'); console.log(chalk.yellow('\n2. Chrome Profile Issues:')); console.log(' • Multiple Chrome instances running'); console.log(' • Wrong Chrome profile opened'); console.log(' • Profile corruption'); console.log(chalk.yellow('\n3. Extension Issues:')); console.log(' • Extension files corrupted or missing'); console.log(' • Invalid manifest.json'); console.log(' • Permission issues'); console.log(chalk.cyan('\nšŸŽÆ Diagnostic Steps:')); console.log('1. Go to chrome://version/ to check your Chrome details'); console.log('2. Go to chrome://policy/ to check enterprise policies'); console.log('3. Try chrome://extensions/ in a private/incognito window'); console.log('4. Check if you can enable developer mode in regular Chrome'); console.log(chalk.green('\nšŸ”§ Quick Tests:')); console.log(`Copy this path: ${chalk.cyan(extensionInstallPath)}`); console.log('1. Open regular Chrome (not dev profile)'); console.log('2. Go to chrome://extensions/'); console.log('3. Try to enable Developer mode there'); console.log('4. If it works, use: ' + chalk.green('freshroute install-manual')); console.log(chalk.blue('\nšŸ’” Alternative Solutions:')); console.log('• ' + chalk.green('freshroute install-manual') + ' - Install in regular Chrome profile'); console.log('• Use different Chrome installation (personal vs enterprise)'); console.log('• Try Chrome Canary or Chromium browser'); }); // Check Chrome policies command program .command('check-chrome-policies') .description('Check if Chrome is managed by organization and blocking extensions') .action(async () => { console.log(chalk.blue.bold('šŸ” Chrome Organization Policy Check\n')); const userHome = os.homedir(); const platform = process.platform; console.log(chalk.cyan('šŸ–„ļø System Information:')); console.log(`Platform: ${platform}`); console.log(`User: ${userHome}`); console.log(chalk.cyan('\nšŸ“‹ Chrome Policy Locations:')); let policyLocations = []; let managedPolicyFound = false; if (platform === 'darwin') { policyLocations = [ '/Library/Managed Preferences/com.google.Chrome.plist', join(userHome, 'Library/Managed Preferences/com.google.Chrome.plist'), '/Library/Application Support/Google/Chrome/policies', join(userHome, 'Library/Application Support/Google/Chrome/policies') ]; } else if (platform === 'win32') { policyLocations = [ 'C:\\Program Files\\Google\\Chrome\\Application\\policies', 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\policies' ]; } else { policyLocations = [ '/etc/opt/chrome/policies', '/etc/chromium/policies', join(userHome, '.config/google-chrome/policies') ]; } for (const policyPath of policyLocations) { try { const stats = await fs.stat(policyPath); console.log(`${chalk.red('🚨')} FOUND: ${policyPath}`); managedPolicyFound = true; if (stats.isFile() && policyPath.endsWith('.plist')) { try { const content = await fs.readFile(policyPath, 'utf8'); if (content.includes('ExtensionSettings') || content.includes('DeveloperToolsDisabled')) { console.log(` ${chalk.yellow('Contains extension restrictions')}`); } } catch (error) { console.log(` ${chalk.gray('Unable to read content')}`); } } } catch (error)