UNPKG

aironin-browse-cli

Version:

aiRonin Browse CLI tool with headed Chrome support

582 lines โ€ข 26.4 kB
#!/usr/bin/env node import { Command } from "commander"; import chalk from "chalk"; import { BrowserSession } from "./browser/BrowserSession.js"; import { discoverChromeHostUrl } from "./browser/browserDiscovery.js"; import { createInterface } from "node:readline"; const program = new Command(); // Helper function to detect and configure remote browser async function detectRemoteBrowser(forceRemote = false) { // Try remote browser detection first (unless explicitly disabled) if (forceRemote || process.env.REMOTE_BROWSER_ENABLED !== "false") { console.log(chalk.blue("๐Ÿ” Attempting remote browser detection...")); const remoteHostUrl = await discoverChromeHostUrl(9222); if (remoteHostUrl) { console.log(chalk.green(`โœ… Found remote browser at: ${remoteHostUrl}`)); process.env.REMOTE_BROWSER_ENABLED = "true"; process.env.REMOTE_BROWSER_HOST = remoteHostUrl; } else { console.log(chalk.yellow("โŒ No remote browser found, using local browser")); process.env.REMOTE_BROWSER_ENABLED = "false"; } } } program .name("aironin-browse") .description("aiRonin Browse CLI with headed Chrome support") .version("1.0.0"); // Global options program .option("-v, --viewport <size>", "Browser viewport size (e.g., 900x600)", "900x600") .option("-q, --quality <number>", "Screenshot quality (1-100)", "75") .option("-r, --remote", "Enable remote browser connection") .option("-h, --host <url>", "Remote browser host URL"); // Test command program .command("test") .description("Test browser connection and functionality") .option("-u, --url <url>", "Test URL to navigate to", "https://example.com") .action(async (options) => { try { console.log(chalk.yellow("๐Ÿงช Testing browser automation...")); // Set environment variables from options process.env.BROWSER_VIEWPORT_SIZE = options.viewport || "900x600"; process.env.SCREENSHOT_QUALITY = (options.quality || 75).toString(); // Detect remote browser (unless explicitly disabled) await detectRemoteBrowser(options.remote); const browser = new BrowserSession(); // Test 1: Launch browser console.log(chalk.blue("1. Testing browser launch...")); await browser.launchBrowser(); console.log(chalk.green("โœ… Browser launched successfully")); // Test 2: Navigate to URL console.log(chalk.blue("2. Testing navigation...")); const navResult = await browser.navigateToUrl(options.url); console.log(chalk.green("โœ… Navigation successful")); console.log(chalk.gray(" Current URL:"), navResult.currentUrl); // Test 3: Take screenshot console.log(chalk.blue("3. Testing screenshot capture...")); if (navResult.screenshot) { console.log(chalk.green("โœ… Screenshot captured successfully")); console.log(chalk.gray(" Screenshot size:"), `${Math.round(navResult.screenshot.length / 1024)}KB`); } else { console.log(chalk.red("โŒ Screenshot capture failed")); } // Test 4: Console logs console.log(chalk.blue("4. Testing console log capture...")); if (navResult.logs) { console.log(chalk.green("โœ… Console logs captured")); console.log(chalk.gray(" Log count:"), navResult.logs.split('\n').filter(line => line.trim()).length); } else { console.log(chalk.yellow("โš ๏ธ No console logs captured")); } // Test 5: Mouse interaction console.log(chalk.blue("5. Testing mouse interaction...")); const clickResult = await browser.click("100,100"); console.log(chalk.green("โœ… Mouse click successful")); // Test 6: Keyboard input console.log(chalk.blue("6. Testing keyboard input...")); const typeResult = await browser.type("test"); console.log(chalk.green("โœ… Keyboard input successful")); // Test 7: Scroll console.log(chalk.blue("7. Testing scroll...")); const scrollResult = await browser.scrollDown(); console.log(chalk.green("โœ… Scroll successful")); // Test 8: Close browser console.log(chalk.blue("8. Testing browser close...")); await browser.closeBrowser(); console.log(chalk.green("โœ… Browser closed successfully")); // Exit the process explicitly process.exit(0); console.log(chalk.green("\n๐ŸŽ‰ All tests passed! Browser automation is working correctly.")); console.log(chalk.gray("\nYou can now use the browser automation tool with your AI agent.")); } catch (error) { console.error(chalk.red("โŒ Test failed:"), error); console.log(chalk.yellow("\nTroubleshooting tips:")); console.log(chalk.gray("1. Ensure you have sufficient disk space for Chromium download")); console.log(chalk.gray("2. Check your internet connection for Chromium download")); console.log(chalk.gray("3. Verify Chrome/Chromium is not already running in debug mode")); console.log(chalk.gray("4. Try running with --remote flag if you have Chrome running with --remote-debugging-port=9222")); process.exit(1); } }); // Launch command program .command("launch") .description("Launch browser and navigate to URL") .argument("<url>", "URL to navigate to") .option("-r, --remote <boolean>", "Connect to remote Chrome browser", "true") .option("--host <host>", "Remote Chrome host URL") .action(async (url, options = {}) => { try { // Set environment variables from options with defaults process.env.BROWSER_VIEWPORT_SIZE = options.viewport || "900x600"; process.env.SCREENSHOT_QUALITY = (options.quality || 75).toString(); // Detect remote browser (unless explicitly disabled) await detectRemoteBrowser(options.remote); const browser = new BrowserSession(); await browser.launchBrowser(); const result = await browser.navigateToUrl(url); console.log(chalk.green("โœ… Browser launched successfully")); console.log(chalk.blue("Current URL:"), result.currentUrl); console.log(chalk.blue("Console logs:"), result.logs || "No logs"); if (result.screenshot) { console.log(chalk.blue("Screenshot captured")); // You could save the screenshot to a file here } // Close the browser to prevent hanging await browser.closeBrowser(); console.log(chalk.gray("Browser closed")); // Exit the process explicitly process.exit(0); } catch (error) { console.error(chalk.red("โŒ Error launching browser:"), error); process.exit(1); } }); // Click command program .command("click") .description("Click at coordinates") .argument("<coordinates>", "Coordinates in format x,y") .action(async (coordinates, options = {}) => { try { const browser = new BrowserSession(); const result = await browser.click(coordinates); console.log(chalk.green("โœ… Clicked successfully")); console.log(chalk.blue("Console logs:"), result.logs || "No logs"); if (result.screenshot) { console.log(chalk.blue("Screenshot captured")); } // Close the browser to prevent hanging await browser.closeBrowser(); console.log(chalk.gray("Browser closed")); // Exit the process explicitly process.exit(0); } catch (error) { console.error(chalk.red("โŒ Error clicking:"), error); process.exit(1); } }); // Type command program .command("type") .description("Type text") .argument("<text>", "Text to type") .action(async (text, options = {}) => { try { const browser = new BrowserSession(); const result = await browser.type(text); console.log(chalk.green("โœ… Typed successfully")); console.log(chalk.blue("Console logs:"), result.logs || "No logs"); if (result.screenshot) { console.log(chalk.blue("Screenshot captured")); } // Close the browser to prevent hanging await browser.closeBrowser(); console.log(chalk.gray("Browser closed")); // Exit the process explicitly process.exit(0); } catch (error) { console.error(chalk.red("โŒ Error typing:"), error); process.exit(1); } }); // Scroll command program .command("scroll") .description("Scroll page") .argument("<direction>", "Scroll direction (up or down)") .action(async (direction, options = {}) => { try { const browser = new BrowserSession(); const result = direction === "down" ? await browser.scrollDown() : await browser.scrollUp(); console.log(chalk.green(`โœ… Scrolled ${direction} successfully`)); console.log(chalk.blue("Console logs:"), result.logs || "No logs"); if (result.screenshot) { console.log(chalk.blue("Screenshot captured")); } // Close the browser to prevent hanging await browser.closeBrowser(); console.log(chalk.gray("Browser closed")); // Exit the process explicitly process.exit(0); } catch (error) { console.error(chalk.red("โŒ Error scrolling:"), error); process.exit(1); } }); // Hover command program .command("hover") .description("Hover at coordinates") .argument("<coordinates>", "Coordinates in format x,y") .action(async (coordinates, options = {}) => { try { const browser = new BrowserSession(); const result = await browser.hover(coordinates); console.log(chalk.green("โœ… Hovered successfully")); console.log(chalk.blue("Console logs:"), result.logs || "No logs"); if (result.screenshot) { console.log(chalk.blue("Screenshot captured")); } // Close the browser to prevent hanging await browser.closeBrowser(); console.log(chalk.gray("Browser closed")); // Exit the process explicitly process.exit(0); } catch (error) { console.error(chalk.red("โŒ Error hovering:"), error); process.exit(1); } }); // Resize command program .command("resize") .description("Resize browser window") .argument("<size>", "Size in format width,height") .action(async (size, options = {}) => { try { const browser = new BrowserSession(); const result = await browser.resize(size); console.log(chalk.green("โœ… Resized successfully")); console.log(chalk.blue("Console logs:"), result.logs || "No logs"); if (result.screenshot) { console.log(chalk.blue("Screenshot captured")); } // Close the browser to prevent hanging await browser.closeBrowser(); console.log(chalk.gray("Browser closed")); // Exit the process explicitly process.exit(0); } catch (error) { console.error(chalk.red("โŒ Error resizing:"), error); process.exit(1); } }); // Console logs command program .command("logs") .description("Get JavaScript console logs from the current page") .action(async (options) => { try { // Detect remote browser (unless explicitly disabled) await detectRemoteBrowser(false); const browser = new BrowserSession(); await browser.launchBrowser(); const result = await browser.getConsoleLogs(); console.log(chalk.green("โœ… Console logs retrieved successfully")); console.log(chalk.blue("Current URL:"), result.currentUrl); if (result.logs && result.logs.trim()) { const logLines = result.logs.split('\n').filter((line) => line.trim()); console.log(chalk.blue(`Total log entries: ${logLines.length}`)); console.log(chalk.yellow("\n๐Ÿ“‹ Console Logs:")); logLines.forEach((line, index) => { console.log(chalk.gray(`${index + 1}.`), line); }); } else { console.log(chalk.yellow("๐Ÿ“ญ No console logs captured")); console.log(chalk.gray("This could mean:")); console.log(chalk.gray("- No JavaScript errors or logs occurred")); console.log(chalk.gray("- Page is static or has minimal JavaScript")); console.log(chalk.gray("- Logs were cleared recently")); } // Close the browser to prevent hanging await browser.closeBrowser(); console.log(chalk.gray("Browser closed")); // Exit the process explicitly process.exit(0); } catch (error) { console.error(chalk.red("โŒ Error getting console logs:"), error); process.exit(1); } }); // Close command program .command("close") .description("Close browser") .action(async (options) => { try { const browser = new BrowserSession(); await browser.closeBrowser(); console.log(chalk.green("โœ… Browser closed successfully")); } catch (error) { console.error(chalk.red("โŒ Error closing browser:"), error); process.exit(1); } }); // Interactive mode command program .command("interactive") .description("Start interactive mode") .action(async (options) => { console.log(chalk.yellow("๐Ÿš€ Starting interactive browser automation mode")); console.log(chalk.gray("Type 'help' for available commands")); console.log(chalk.gray("Type 'exit' to quit")); const browser = new BrowserSession(); let isLaunched = false; const rl = createInterface({ input: process.stdin, output: process.stdout }); const question = (query) => { return new Promise((resolve) => { rl.question(query, resolve); }); }; while (true) { try { const input = await question(chalk.blue("browser> ")); const [command, ...args] = input.trim().split(" "); switch (command?.toLowerCase()) { case "help": console.log(chalk.cyan("Available commands:")); console.log(" launch <url> - Launch browser and navigate to URL"); console.log(" click <x,y> - Click at coordinates"); console.log(" type <text> - Type text"); console.log(" scroll <up|down> - Scroll page"); console.log(" hover <x,y> - Hover at coordinates"); console.log(" resize <w,h> - Resize browser window"); console.log(" logs - Get JavaScript console logs"); console.log(" close - Close browser"); console.log(" test - Run connection test"); console.log(" exit - Exit interactive mode"); break; case "test": console.log(chalk.yellow("๐Ÿงช Running connection test...")); if (!isLaunched) { await browser.launchBrowser(); isLaunched = true; } const testResult = await browser.navigateToUrl("https://example.com"); console.log(chalk.green("โœ… Test successful")); console.log(chalk.blue("Current URL:"), testResult.currentUrl); break; case "launch": if (args.length === 0) { console.log(chalk.red("โŒ URL required")); break; } if (!isLaunched) { await browser.launchBrowser(); isLaunched = true; } const result = await browser.navigateToUrl(args[0]); console.log(chalk.green("โœ… Navigated successfully")); console.log(chalk.blue("Current URL:"), result.currentUrl); break; case "click": if (args.length === 0) { console.log(chalk.red("โŒ Coordinates required (x,y format)")); break; } await browser.click(args[0]); console.log(chalk.green("โœ… Clicked successfully")); break; case "type": if (args.length === 0) { console.log(chalk.red("โŒ Text required")); break; } await browser.type(args.join(" ")); console.log(chalk.green("โœ… Typed successfully")); break; case "scroll": if (args.length === 0 || !["up", "down"].includes(args[0])) { console.log(chalk.red("โŒ Direction required (up or down)")); break; } if (args[0] === "down") { await browser.scrollDown(); } else { await browser.scrollUp(); } console.log(chalk.green(`โœ… Scrolled ${args[0]} successfully`)); break; case "hover": if (args.length === 0) { console.log(chalk.red("โŒ Coordinates required (x,y format)")); break; } await browser.hover(args[0]); console.log(chalk.green("โœ… Hovered successfully")); break; case "resize": if (args.length === 0) { console.log(chalk.red("โŒ Size required (width,height format)")); break; } await browser.resize(args[0]); console.log(chalk.green("โœ… Resized successfully")); break; case "logs": const logsResult = await browser.getConsoleLogs(); console.log(chalk.green("โœ… Console logs retrieved successfully")); console.log(chalk.blue("Current URL:"), logsResult.currentUrl); if (logsResult.logs && logsResult.logs.trim()) { const logLines = logsResult.logs.split('\n').filter((line) => line.trim()); console.log(chalk.blue(`Total log entries: ${logLines.length}`)); console.log(chalk.yellow("\n๐Ÿ“‹ Console Logs:")); logLines.forEach((line, index) => { console.log(chalk.gray(`${index + 1}.`), line); }); } else { console.log(chalk.yellow("๐Ÿ“ญ No console logs captured")); console.log(chalk.gray("This could mean:")); console.log(chalk.gray("- No JavaScript errors or logs occurred")); console.log(chalk.gray("- Page is static or has minimal JavaScript")); console.log(chalk.gray("- Logs were cleared recently")); } break; case "close": await browser.closeBrowser(); isLaunched = false; console.log(chalk.green("โœ… Browser closed")); break; case "exit": if (isLaunched) { await browser.closeBrowser(); } rl.close(); console.log(chalk.yellow("๐Ÿ‘‹ Goodbye!")); process.exit(0); break; default: console.log(chalk.red(`โŒ Unknown command: ${command}`)); console.log(chalk.gray("Type 'help' for available commands")); } } catch (error) { console.error(chalk.red("โŒ Error:"), error); } } }); // Test connection command program .command("test-connection") .description("Test connection to Chrome browser (useful for dev containers)") .option("-p, --port <port>", "Chrome debugging port", "9222") .option("-h, --host <host>", "Specific host to test") .action(async (options = {}) => { try { const port = parseInt(options.port || "9222"); const specificHost = options.host; console.log(chalk.blue("๐Ÿ” Testing Chrome browser connection...")); console.log(chalk.gray(`Port: ${port}`)); if (specificHost) { console.log(chalk.gray(`Testing specific host: ${specificHost}`)); const { tryChromeHostUrl } = await import("./browser/browserDiscovery.js"); const isValid = await tryChromeHostUrl(specificHost); if (isValid) { console.log(chalk.green(`โœ… Successfully connected to ${specificHost}`)); } else { console.log(chalk.red(`โŒ Failed to connect to ${specificHost}`)); } return; } console.log(chalk.gray("Auto-discovering Chrome instances...")); const { discoverChromeHostUrl } = await import("./browser/browserDiscovery.js"); const chromeHostUrl = await discoverChromeHostUrl(port); if (chromeHostUrl) { console.log(chalk.green(`โœ… Auto-discovered and tested connection to Chrome: ${chromeHostUrl}`)); console.log(chalk.blue("You can now use this host with:")); console.log(chalk.gray(`export REMOTE_BROWSER_HOST="${chromeHostUrl}"`)); console.log(chalk.gray(`export REMOTE_BROWSER_ENABLED=true`)); } else { console.log(chalk.red("โŒ No Chrome instances found with remote debugging enabled")); console.log(chalk.yellow("\nTo start Chrome with remote debugging:")); console.log(chalk.gray("chrome --remote-debugging-port=9222")); console.log(chalk.gray("chromium --remote-debugging-port=9222")); console.log(chalk.gray("google-chrome --remote-debugging-port=9222")); } } catch (error) { console.error(chalk.red("โŒ Error testing connection:"), error); process.exit(1); } }); // Dev containers command program .command("dev-containers") .description("Setup and test browser automation for dev containers") .option("-p, --port <port>", "Chrome debugging port", "9222") .option("-h, --host <host>", "Host machine IP address") .action(async (options = {}) => { try { const port = parseInt(options.port || "9222"); const hostIP = options.host; console.log(chalk.blue("๐Ÿณ Dev Containers Browser Automation Setup")); console.log(chalk.gray("This will help you connect to Chrome running on your host machine")); // Test current connection console.log(chalk.yellow("\n1. Testing current connection...")); const { discoverChromeHostUrl } = await import("./browser/browserDiscovery.js"); const chromeHostUrl = await discoverChromeHostUrl(port); if (chromeHostUrl) { console.log(chalk.green(`โœ… Found Chrome at: ${chromeHostUrl}`)); } else { console.log(chalk.red("โŒ No Chrome found")); if (hostIP) { console.log(chalk.yellow(`\n2. Testing provided host: ${hostIP}:${port}`)); const { tryChromeHostUrl } = await import("./browser/browserDiscovery.js"); const testUrl = `http://${hostIP}:${port}`; const isValid = await tryChromeHostUrl(testUrl); if (isValid) { console.log(chalk.green(`โœ… Successfully connected to ${testUrl}`)); console.log(chalk.blue("\n3. Environment variables to set:")); console.log(chalk.gray(`export REMOTE_BROWSER_HOST="${testUrl}"`)); console.log(chalk.gray(`export REMOTE_BROWSER_ENABLED=true`)); } else { console.log(chalk.red(`โŒ Failed to connect to ${testUrl}`)); console.log(chalk.yellow("\nTroubleshooting:")); console.log(chalk.gray("1. Make sure Chrome is running on host with --remote-debugging-port=9222")); console.log(chalk.gray("2. Check if the host IP is correct")); console.log(chalk.gray("3. Verify network connectivity between container and host")); } } else { console.log(chalk.yellow("\n2. To connect to host Chrome:")); console.log(chalk.gray("a) Start Chrome on host with remote debugging:")); console.log(chalk.gray(" chrome --remote-debugging-port=9222")); console.log(chalk.gray("b) Find your host IP address:")); console.log(chalk.gray(" ip addr show | grep inet")); console.log(chalk.gray("c) Run this command with your host IP:")); console.log(chalk.gray(` browser-automation dev-containers --host YOUR_HOST_IP`)); } } console.log(chalk.blue("\n4. Usage in dev containers:")); console.log(chalk.gray("# Set environment variables")); console.log(chalk.gray("export REMOTE_BROWSER_ENABLED=true")); console.log(chalk.gray("export REMOTE_BROWSER_HOST=http://HOST_IP:9222")); console.log(chalk.gray("")); console.log(chalk.gray("# Test the connection")); console.log(chalk.gray("browser-automation test-connection")); console.log(chalk.gray("")); console.log(chalk.gray("# Use browser automation")); console.log(chalk.gray("browser-automation launch https://example.com")); } catch (error) { console.error(chalk.red("โŒ Error in dev containers setup:"), error); process.exit(1); } }); program.parse(); //# sourceMappingURL=cli.js.map