apna-browser
v1.1.1
Published
Complete Browser Automation Suite built on Puppeteer with stealth mode, SSL handling, auto-detection, dialogs, console and network monitoring, drag & drop, iframes, mobile emulation, performance metrics, geolocation, downloads, experimental WebMCP tool su
Maintainers
Readme
Apna Browser
All-in-one Puppeteer-based browser automation manager with 30+ battle-tested utilities: stealth browsing, smart tab tracking, dialogs, clipboard, console + network monitoring, downloads, performance metrics, mobile emulation, geolocation, automatic retries, and experimental WebMCP tool support.
Installation
npm install apna-browserRequires Node.js 22.12.0 or newer.
Quick Start
import ApnaBrowser from 'apna-browser';
const browser = new ApnaBrowser();
await browser.launchBrowser({
stealth: true,
ignoreSSLErrors: true,
});
await browser.openNewTab('https://www.google.com');
await browser.typeText('textarea[name="q"]', 'Apna Browser automation');
await browser.click('input[name="btnK"]');
await browser.wait(5000);
await browser.takeScreenshot('google-search.png');
await browser.closeBrowser();Highlights
- Stealth-ready launch options that bypass common bot detections
- Automatic tab tracking and tab management helpers
- Rich interaction helpers (typing, clicking, drag-and-drop, hover, iframes, clipboard)
- Framework-safe form filling —
pasteIntoFielddispatches realinput/changeevents so React, Vue, and Angular controlled inputs update correctly - Built-in console log and network request capture, with automatic memory caps
- Request interception/blocking that safely coexists with other interceptors (
isInterceptResolutionHandled()guard) - Automatic retry logic for flaky clicks and navigation (
withRetry) - Persistent browser profiles via
userDataDir - Custom Chrome path via
executablePathorchannel(system Chrome, Canary, Edge, etc.) - Connect to an already running browser via
connectBrowser() - Modern Navigation Timing API for performance metrics (no deprecated APIs)
- Experimental WebMCP support — discover and invoke tools that a page registers for AI agents
- Performance, device emulation, geolocation and download helpers
- Fully English console output and inline documentation
Table of Contents
- Installation
- Quick Start
- Highlights
- Configuration
- Custom Chrome & Connect
- Advanced Usage
- WebMCP (Experimental)
- Complete Usage Reference
- API Overview
- Example Script
- Changelog
- Support & Feedback
- License
Configuration
ApnaBrowser accepts a config object in its constructor to tune defaults:
const browser = new ApnaBrowser({
defaultTimeout: 30000, // default timeout for waitForSelector etc.
maxLogs: 1000, // console log cap (prevents memory leaks)
maxNetworkRequests: 1000, // network request cap
tabSyncIntervalMs: 2000, // how often auto-detection syncs tabs
defaultRetries: 2, // retry count for withRetry-wrapped actions
defaultRetryDelay: 500, // ms between retries
logging: false, // true = show library debug logs on errors/retries
});Custom Chrome & Connect
Use your own Chrome (custom path)
Pass executablePath to point at a specific Chrome/Chromium binary, or use channel if Chrome is installed in a standard location:
// Windows — full path to chrome.exe
await browser.launchBrowser({
stealth: true,
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
});
// macOS
await browser.launchBrowser({
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
});
// Linux
await browser.launchBrowser({
executablePath: '/usr/bin/google-chrome',
});
// Or use an installed channel (no path needed)
await browser.launchBrowser({
channel: 'chrome', // stable Chrome
// channel: 'chrome-beta',
// channel: 'chrome-canary',
// channel: 'msedge',
enableWebMCP: true, // useful with system Chrome 150+
});Use executablePath or channel — not both at once.
Connect to a running browser
Launch Chrome separately with remote debugging, then attach Apna Browser:
# Windows
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222
# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
# Linux
google-chrome --remote-debugging-port=9222Copy the WebSocket URL from the Chrome output, then:
await browser.connectBrowser({
browserWSEndpoint: 'ws://127.0.0.1:9222/devtools/browser/...',
});
await browser.openNewTab('https://example.com');
await browser.click('button');
// Disconnect only — Chrome keeps running
await browser.closeBrowser();
// Or fully close the connected browser
await browser.closeBrowser({ closeRunningBrowser: true });See Puppeteer browser management for more on launch vs connect.
Advanced Usage
Launch, Tabs & Navigation
import ApnaBrowser from 'apna-browser';
const browser = new ApnaBrowser();
await browser.launchBrowser({
stealth: true,
ignoreSSLErrors: true,
autoDetect: true,
userDataDir: './profile', // optional: persist cookies/login across runs
});
await browser.openNewTab('https://news.ycombinator.com');
await browser.openNewTab('https://github.com/alsocoders');
await browser.switchToTab(0);
await browser.reload();
await browser.nextTab();
await browser.navigateTo('https://alsocoder.com');Dialogs, Console & Network Monitoring
browser.enableAutoDialogHandler({
alert: 'accept',
confirm: 'accept',
prompt: 'accept',
promptText: 'Namaste!',
});
await browser.enableNetworkMonitoring();
browser.setupConsoleCapture(browser.page);
await browser.click('#trigger-dialog');
const consoleErrors = browser.getConsoleErrors();
const recentRequests = browser.getNetworkRequests({ limit: 5 });Interactions, Clipboard & Elements
await browser.typeText('#email', '[email protected]');
await browser.typeText('#password', 'super-secret');
await browser.click('#login');
await browser.waitForNavigation();
await browser.selectText('.profile-name');
const copiedName = await browser.copySelectedText();
const linkCount = await browser.countElements('a');
const isVisible = await browser.isElementVisible('#dashboard');Performance, Screenshots & Downloads
await browser.getPerformanceMetrics();
browser.printPerformanceMetrics();
await browser.takeScreenshot('dashboard.png');
await browser.generatePDF('dashboard.pdf');
await browser.enableDownloads('./downloads');
await browser.click('#download-report');
await browser.waitForDownload(5000);Mobile Emulation, Geolocation & Devices
await browser.setViewport(390, 844);
await browser.emulateMobile('iPhone 13 Pro');
await browser.grantGeolocationPermission();
await browser.setGeolocation(28.6139, 77.2090); // New Delhi
await browser.reload();Iframes, Drag & Drop, Keyboard Shortcuts
await browser.clickInFrame('#ad-frame', 'button.close');
await browser.dragAndDrop('#item-1', '#dropzone', { steps: 15, holdDelay: 100 });
await browser.pressKeyCombination(['Control', 'Shift', 'KeyT']);
await browser.ctrlF();
await browser.escape();Request Blocking (safe with other interceptors)
// Blocks images/fonts across every current AND future tab
await browser.enableRequestBlocking(['image', 'font']);
// ...later, to stop blocking
await browser.disableRequestBlocking();Cleanup
await browser.closeAllTabs();
await browser.closeBrowser();WebMCP (Experimental)
WebMCP lets a web page register "tools" (name + description + input schema) that an external agent can discover and invoke directly — instead of guessing CSS selectors and simulating clicks. It requires Chrome 150+ and must be explicitly enabled at launch.
await browser.launchBrowser({ stealth: true, enableWebMCP: true });
await browser.openNewTab('https://example-webmcp-site.com');
// Discover tools registered on the page
const tools = browser.getWebMCPTools();
console.log(tools.map(t => t.name));
// Call a tool directly
const result = await browser.executeWebMCPTool('search_products', { query: 'laptop' });
// React to tools appearing/disappearing dynamically
browser.onWebMCPToolsAdded(tools => console.log('New tools:', tools));
browser.onWebMCPToolsRemoved(tools => console.log('Removed tools:', tools));
// Watch every invocation for debugging
browser.onWebMCPToolInvoked(call => console.log('Invoked:', call.tool.name, call.input));
browser.onWebMCPToolResponded(response => console.log('Responded:', response.status));
// Register your own tool directly on a page
await browser.registerWebMCPTool({
name: 'get_page_title',
description: 'Returns the current document title',
inputSchema: { type: 'object', properties: {} },
execute: async () => ({ title: document.title }),
});All WebMCP methods throw a clear error if enableWebMCP wasn't set at launch, or if the connected browser doesn't support the WebMCP CDP domain.
Complete Usage Reference
Each snippet assumes you created an instance via:
import ApnaBrowser from 'apna-browser';
const browser = new ApnaBrowser();Browser Lifecycle
await browser.launchBrowser({ headless: false, stealth: true, userDataDir: './profile' });
await browser.enableAutoDetection();
await browser.refreshTabs();
await browser.closeBrowser();
// Custom Chrome path or channel
await browser.launchBrowser({
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
});
await browser.launchBrowser({ channel: 'chrome-canary' });
// Connect to a browser already running with --remote-debugging-port=9222
await browser.connectBrowser({ browserWSEndpoint: 'ws://127.0.0.1:9222/devtools/browser/...' });
await browser.closeBrowser(); // disconnect only
await browser.closeBrowser({ closeRunningBrowser: true }); // fully close ChromeTabs & Contexts
await browser.openNewTab('https://example.com');
await browser.switchToTab(0);
await browser.duplicateCurrentTab();
await browser.closeCurrentTab();
await browser.closeAllTabs();
const tabs = await browser.getAllTabs();
const { page: incognitoPage, context } = await browser.openIncognitoTab('https://alsocoder.com');
await context.close();Navigation & Waiting
await browser.navigateTo('https://alsocoder.com/blog'); // auto-retried on failure
await browser.waitForNavigation();
await browser.goBack();
await browser.goForward();
await browser.reload();
await browser.wait(1500);
await browser.waitForSelector('#hero');Dialog Handling
await browser.handleAlert(true);
await browser.handleConfirm(false);
await browser.handlePrompt('Namaste', true);
browser.enableAutoDialogHandler({
alert: 'accept',
confirm: 'dismiss',
prompt: 'accept',
promptText: 'Namaste!',
});
browser.disableAutoDialogHandler();Interaction Helpers
await browser.typeText('#search', 'Apna Browser rocks!');
await browser.click('.submit'); // auto-retried on failure
await browser.doubleClick('.card');
await browser.rightClick('.card');
await browser.hover('.menu');
await browser.hoverAndClick('.menu', '.menu-item:last-child');
await browser.dragAndDrop('#item-1', '#drop-target');Clipboard & Form Utilities
await browser.selectText('article h1');
const headline = await browser.copySelectedText();
await browser.selectAll();
await browser.copy();
await browser.cut();
await browser.paste();
// Works correctly on React/Vue/Angular controlled inputs
await browser.pasteIntoField('#notes', 'Copied content');
await browser.selectOption('#country', 'IN');
await browser.setCheckbox('#terms', true);
await browser.uploadFile('input[type="file"]', './assets/resume.pdf');
await browser.focus('#comments');Element Info & Queries
const exists = await browser.elementExists('.ticket');
const titleText = await browser.getText('.title');
const href = await browser.getAttribute('.cta', 'href');
const count = await browser.countElements('a');
const allHeadings = await browser.getAllText('h2');
const visible = await browser.isElementVisible('#status');Evaluation & Scrolling
await browser.evaluate(() => window.dispatchEvent(new Event('resize')));
await browser.scrollTo(0, 500);
await browser.scrollToBottom();
await browser.scrollToElement('#footer');Screenshots & PDFs
await browser.takeScreenshot('full-page.png');
await browser.takeScreenshot('viewport.png', { fullPage: false });
await browser.generatePDF('report.pdf', { format: 'A4', printBackground: true });Console & Network Monitoring
browser.setupConsoleCapture(browser.page);
await browser.enableNetworkMonitoring();
await browser.enableRequestBlocking(['image', 'font']);
await browser.disableRequestBlocking();
browser.printConsoleLogs();
const errors = browser.getConsoleErrors();
const warnings = browser.getConsoleWarnings();
const requests = browser.getNetworkRequests({ type: 'xhr', limit: 10 });
browser.clearConsoleLogs();
browser.clearNetworkRequests();Performance Metrics
const metrics = await browser.getPerformanceMetrics(); // uses modern Navigation Timing API
console.log(metrics.pageLoadTime);
browser.printPerformanceMetrics();Storage & Cookies
const cookies = await browser.getCookies();
await browser.setCookie({ name: 'user', value: 'also-coder' });
await browser.clearCookies();
const localStorage = await browser.getLocalStorage();
await browser.clearLocalStorage();Downloads & File Handling
await browser.enableDownloads('./tmp/downloads');
await browser.click('#download');
await browser.waitForDownload(10000);Viewport, Devices & Geolocation
await browser.setViewport(1280, 800);
await browser.setUserAgent('Mozilla/5.0 (...custom UA...)');
await browser.emulateMobile('iPhone 13 Pro');
const availableDevices = browser.getAvailableDevices();
await browser.grantGeolocationPermission();
await browser.setGeolocation(12.9716, 77.5946); // BengaluruKeyboard Shortcuts
await browser.pressKey('Enter');
await browser.pressKeyCombination(['Control', 'Shift', 'KeyT']);
await browser.ctrlS();
await browser.ctrlF();
await browser.ctrlW();
await browser.ctrlT();
await browser.ctrlShiftT();
await browser.escape();
await browser.enter();
await browser.tab();Frames & Multi-Context
await browser.switchToFrame('iframe#payment');
await browser.currentFrame.type('#card', '4242424242424242');
await browser.switchToMainFrame();
await browser.clickInFrame('iframe#ad', 'button.close');Metrics & Utilities Combo
const dimensions = await browser.getPageDimensions();
console.log(dimensions.width, dimensions.height);
const url = await browser.getURL();
const title = await browser.getTitle();Retry Utility
// Wrap any custom async action with automatic retry
await browser.withRetry(
() => browser.page.click('.flaky-button'),
{ retries: 3, delay: 1000, label: 'flaky button click' }
);API Overview
| Category | Key Methods |
| --- | --- |
| Browser | launchBrowser, connectBrowser, closeBrowser, enableAutoDetection, refreshTabs |
| Tabs | openNewTab, switchToTab, nextTab, closeTab, duplicateCurrentTab |
| Navigation | navigateTo, reload, goBack, goForward, waitForNavigation |
| Interactions | typeText, click, doubleClick, hover, dragAndDrop, pressKeyCombination |
| Clipboard | selectText, copySelectedText, pasteIntoField |
| Media | takeScreenshot, generatePDF |
| Monitoring | setupConsoleCapture, getConsoleLogs, enableNetworkMonitoring |
| Networking | enableRequestBlocking, disableRequestBlocking |
| Reliability | withRetry, defaultTimeout, defaultRetries |
| WebMCP | getWebMCPTools, executeWebMCPTool, registerWebMCPTool, onWebMCPToolInvoked |
| Utilities | getPerformanceMetrics, setViewport, emulateMobile, setGeolocation, enableDownloads |
See lib/ApnaBrowser.js for the full API documentation via inline comments.
Example Script
The following script demonstrates a full-featured flow you can copy into your project:
import ApnaBrowser from 'apna-browser';
async function exampleDemo() {
const manager = new ApnaBrowser();
try {
await manager.launchBrowser({ stealth: true, ignoreSSLErrors: true });
manager.enableAutoDialogHandler({ alert: 'accept', confirm: 'accept' });
await manager.openNewTab('https://www.google.com');
await manager.wait(2000);
await manager.openNewTab('https://www.github.com');
await manager.wait(2000);
await manager.openNewTab('https://www.youtube.com');
await manager.wait(2000);
await manager.switchToTab(0);
await manager.typeText('textarea[name="q"]', 'Browser automation', { delay: 100 });
await manager.wait(1000);
await manager.click('input[name="btnK"]');
await manager.wait(5000);
try {
await manager.selectText('h3');
const copiedText = await manager.copySelectedText();
console.log(`Copied: ${copiedText.substring(0, 50)}...`);
} catch {
console.log('Copy demo skipped');
}
await manager.getPerformanceMetrics();
manager.printPerformanceMetrics();
const linkCount = await manager.countElements('a');
console.log(`Total links: ${linkCount}`);
await manager.takeScreenshot('demo-screenshot.png');
} catch (error) {
console.error('Error:', error.message);
await manager.takeScreenshot('error.png');
} finally {
await manager.closeBrowser();
}
}
exampleDemo();Changelog
1.1.1
- Fixed SPA sites (WhatsApp Web, etc.) breaking on
openNewTab/navigateTo - Navigation default changed from
networkidle2toload(configurable viadefaultWaitUntil) - Browser now uses real window size (
defaultViewport: null) instead of forced 1280×720 - Stealth mode no longer uses command-line flags that trigger Chrome warnings on Windows
- Stealth page patches simplified — removed fake plugins and extra HTTP headers that blocked JS/CSS loading
1.1.0
- Requires Node.js 22.12.0 or newer
- Updated to Puppeteer
^25.3.0 - Added
connectBrowser()to attach to a running Chrome viabrowserWSEndpoint - Added explicit
executablePathandchannelsupport inlaunchBrowser() closeBrowser()disconnects without killing Chrome when attached viaconnectBrowser(); pass{ closeRunningBrowser: true }to close the process- Added
loggingoption (defaultfalse) to control library status logs - Fixed blank tab reuse for
chrome://new-tab-page/when connecting to real Chrome - Fixed deprecated
page.target().createCDPSession()→page.createCDPSession() - Fixed deprecated
createIncognitoBrowserContext()→createBrowserContext()(with fallback for older versions) - Fixed request-blocking
"Request is already handled!"errors viaisInterceptResolutionHandled() pasteIntoFieldnow dispatches nativeinput/changeevents for React/Vue/Angular compatibilitydragAndDropnow uses intermediate mouse-move steps for JS-based drag libraries- Performance metrics migrated from deprecated
performance.timingto Navigation Timing Level 2 - Added automatic retry logic (
withRetry) forclickandnavigateTo - Added memory caps for console logs and network request history
- Added
userDataDirsupport for persistent browser profiles - Added experimental WebMCP support (
getWebMCPTools,executeWebMCPTool,registerWebMCPTool, and related event listeners) - Added
disableRequestBlocking() - All console output, errors, and inline comments are now in English
1.0.0
- Initial release
Support & Feedback
- Report bugs or feature requests at github.com/alsocoders/apna-browser/issues
- Explore examples, updates, and tips on alsocoder.com
- Share feedback via discussions or pull requests—community contributions are welcome!
License
MIT © Also Coder · https://alsocoder.com
Use the package freely in your projects, but do not redistribute modified versions under the same name.
