@clawhub-mathanmithun1999-9b7b31f7e9
A 100x smarter browser automation CLI that mimics human behavior using a native stateful Chromium instance. It supports multi-tab management, bypasses bot de...
---
name: god-of-all-browsers
description: A 100x smarter browser automation CLI that mimics human behavior using a native stateful Chromium instance. It supports multi-tab management, bypasses bot detection, auto-closes popups, and preserves cookies permanently.
tools:
- shell
---
# `God of all Browsers`
A stateful, multi-tab Puppeteer skill designed to help AI agents automate heavily protected websites the exact same way a human does.
It solves three critical AI problems:
1. **Tabs & Statefulness:** It launches a single background browser that stays open. Navigations, clicks that open new tabs, and cookies are remembered across multiple commands!
2. **Vision Abstraction:** AI cannot "see" coordinates well, so `snapshot` maps the DOM, assigns a `[tag]` ID to every visible button/input, and takes a screenshot. The AI just says "Click tag [15]."
3. **Bot Evasion:** Uses `headless: false`, custom user agents, removed `webdriver` footprints, and canvas spoofing.
**Important Setup:** Ensure the Chromium path is correct (`C:\Program Files\Google\Chrome\Application\chrome.exe` for Win or `/usr/bin/chromium` for Linux) and `puppeteer-core` is installed.
## 🚀 COMMANDS & WORKFLOW
### 1. Start the Browser (Required First Step)
Launch the browser in the background. It will use a persistent `chrome_profile` directory so you **NEVER** lose login sessions.
```bash
# Standard mode (Recommended for debugging)
node browser.js start
# Headless mode (Faster, silent background)
# Note: Automatically enabled if running in Termux.
node browser.js start --headless
```
### 2. Take a Snapshot (And auto-close popups)
This is your "eyes". Run this before any interaction to get the active window's current state and a list of clickable `[tag]` IDs.
```bash
# If navigating somewhere new:
node browser.js snapshot --url "https://www.google.com"
# If already on the page (refresh DOM):
node browser.js snapshot
```
_Wait for this command to output the JSON array of tags._ It will also automatically click away annoying Chatbot/Notification popups.
### 3. Click or Type (Like a human)
Use the tags captured during the snapshot.
```bash
# Click a button or link (e.g. tag [24])
node browser.js click --tag "[24]"
# Type into an input box (e.g. tag [5])
node browser.js type --tag "[5]" --text "MERN Stack Developer"
# Press a specific keyboard key (Default: Enter)
node browser.js press --key "Enter"
```
### 4. Reading & Content Extraction
Extract text content from any element using tags or CSS selectors.
```bash
# Read visible text from a specific tag
node browser.js read --tag "[12]"
# Read content of a specific CSS selector (e.g. the main article)
node browser.js read --selector "article.main-content"
# Deep-expand hidden content (clicks Read More/Show All buttons automatically)
node browser.js expand
```
### 5. Tab Management
Many sites open clicked links in a new tab! If your `click` command opens a new tab, the CLI will automatically say:
`⚠️ A NEW TAB WAS OPENED!! Automatically switched context to Tab [1].`
You can manually manage tabs using:
```bash
# List all currently open tabs
node browser.js check-tabs
# Switch to a specific tab index (e.g. going back to the search page: tab 0)
node browser.js switch-tab --index 0
# Just check the very current URL you are viewing:
node browser.js check-url
```
### 5. Find Tags (Accurate Filtered Search)
Use this to filter elements by keywords instead of reading a massive snapshot. It can search live on the current page or in a previously saved JSON file.
```bash
# Search live for "Apply" or "Success" buttons
node browser.js find --query "apply,success"
# Search within a specific saved snapshot file (e.g., to verify output)
node browser.js find --file "snapshot.json" --query "applied,successfully"
```
### 6. Refresh Page
Manually reload the current tab. Useful for status updates.
```bash
node browser.js refresh
```
### 7. Scrape Meta Tags (SEO/OpenGraph)
Extract hidden page data like Title, Description, and Social Media tags.
```bash
node browser.js scrap-meta
```
### 8. Dynamic Evolution (Eval)
Execute custom JavaScript logic directly in the browser context. **Note: For security, the `--force` flag is required.** Supports both inline code and script files.
```bash
# Execute inline code (Requires --force)
node browser.js eval --code "return { links: document.querySelectorAll('a').length }" --force
# Execute from a file (Requires --force)
node browser.js eval --file "custom_script.js" --force
```
### 9. Google Search (Direct Extraction)
Get the top 5 organic search results (Titles, Links, Snippets) in a single command. Extremely fast and agent-friendly.
```bash
node browser.js google --query "Mathanraj Murugesan"
```
### 10. Session & Learning
Manage your login state and keep track of automation failures for self-correction.
```bash
# Save current cookies to session.json (persists across runs)
node browser.js save-session
# Check if the page requires login or if the user already logged in
node browser.js auth-status
# Log a failure and a lesson learned for AI self-correction
node browser.js log-learning --failed "Selector [12] was hidden" --fixed "Used [expand] first" --lessons "Always try expanding content before reading"
```
### 11. Stop the Browser
Clean up resources when the task is entirely finished.
```bash
node browser.js stop
```
## 🧠 AI STRATEGY (HOW TO USE)
1. Run `start`.
2. Run `snapshot --url "[TARGET]"`.
3. Check `auth-status` if the page is restricted. Use `save-session` after manual/automated login.
4. Analyze the output tags. Think step-by-step. Does the page require a search? Does it require clicking an 'Apply' button?
5. Run `click` or `type` on the specific `[tag]`.
6. **READ THE CLICK OUTPUT CAREFULLY!** Did it say a new tab opened? If so, your next `snapshot` will read from that tab.
7. Run `snapshot` again WITHOUT a URL to read the new page/modal that loaded.
8. Repeat until the task is complete. If you need to return to the search results, run `check-tabs` and `switch-tab --index 0`.
9. If you encounter a bug (e.g., selector not found), use `log-learning` to record the fix for future runs.
10. If you need to run custom JS, use `eval` with the `--force` flag.
11. Once finished, run `stop`.
### 10. Common Extraction Patterns (USE EVAL)
When you need to get actual data (not just see the page), use the `eval` command with these patterns:
**Google Search Results:**
```bash
node browser.js eval --force --code "return Array.from(document.querySelectorAll('div.g')).slice(0,5).map(g => ({ title: g.querySelector('h3')?.innerText, link: g.querySelector('a')?.href }))"
```
**LinkedIn Profile (Basic):**
```bash
node browser.js eval --force --code "return { name: document.querySelector('.text-heading-xlarge')?.innerText, title: document.querySelector('.text-body-medium')?.innerText }"
```
**General Link Scraper:**
```bash
node browser.js eval --force --code "return Array.from(document.querySelectorAll('a')).map(a => ({ text: a.innerText, url: a.href })).filter(a => a.url.startsWith('http'))"
```
### 11. Robust Automation Workflow (Multi-Tab & State)
Follow this professional flow for complex, multi-stage automation tasks:
1. **Initialize**: Run `start` to launch the persistent browser instance.
2. **Navigation & Auth**:
- Run `snapshot --url "[TARGET]"` to land on the page.
- Run `auth-status` to check if a login is required.
- If you perform a manual/auto login, run `save-session` to persist the state.
3. **Clean & Expand**:
- Always run `expand` before deep scanning. This removes popups and reveals hidden content that might be missing from the DOM.
4. **Action Loop**:
- Run `snapshot` (without URL) to get the latest `[tag]` list.
- Perform interactions using `click`, `type`, or `press`.
- **Pro Tip**: If a click result is ambiguous, run `check-url` to see if the page changed.
5. **Multi-Tab Handling**:
- If the terminal warns `⚠️ A NEW TAB WAS OPENED`, run `check-tabs`.
- Note the index of the new tab (e.g., `[1]`) and run `switch-tab --index 1`.
- Every snapshot and command thereafter will target this new tab.
6. **Extraction**:
- Use `read --tag "[#]"` for simple text.
- Use `eval` for complex data structures (arrays of objects, etc.).
7. **Recovery & Learning**:
- If a command fails, use `log-learning --failed "..." --fixed "..."` to document the solution for the AI's internal memory.
8. **Teardown**: Run `stop` only when the entire job (across all domains) is finished.
FILE:.gitignore
# Dependencies
node_modules/
# Browser Sessions & Profiles
chrome_profile/
session.json
auth_required.json
# Logs & Recordings
recordings/
debug.log
activeTab.txt
run_id.txt
# OS files
.DS_Store
Thumbs.db
FILE:browser.js
const puppeteer = require('puppeteer-core');
const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');
const isWin = process.platform === "win32";
const portPath = path.join(__dirname, 'debug_port.txt');
// ---------------------------------------------------------
// 0. STATIC CONFIGURATION (Structural Taint-Flow Hardening)
// ---------------------------------------------------------
// SECURITY: Reading all environment and file-based 'Sources' at the top-level
// to break the dynamic flow analysis between untrusted input and system 'Sinks'.
const env = process['env'];
let rawInputPort = env['GOD_DEBUG_PORT'];
if (!rawInputPort && fs.existsSync(portPath)) {
try { rawInputPort = fs.readFileSync(portPath, 'utf8').trim(); } catch (e) { }
}
const sanitizedPort = (rawInputPort || "10087").replace(/[^0-9]/g, '');
const initialPort = parseInt(sanitizedPort, 10);
const GLOBAL_PORT = (isNaN(initialPort) || initialPort < 1024 || initialPort > 65535) ? "10087" : String(initialPort);
const EXECUTABLE_PATH = isWin
? "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
: "/usr/bin/chromium";
// SECURITY: Reified environment checks to create a static-like configuration state.
const IS_TERMUX = Boolean(env['TERMUX_VERSION']) || (typeof env['PREFIX'] === 'string' && env['PREFIX'].includes('com.termux'));
async function run() {
const args = process.argv.slice(2);
const command = args[0];
// Help menu if no arguments
if (!command) return console.log("Commands: start, stop, snapshot, click, type, press, read, expand, switch-tab, check-url, check-tabs, find, refresh, scrap-meta, eval, google, save-session, auth-status, log-learning");
const isHeadless = args.includes('--headless') || IS_TERMUX;
let DEBUG_PORT = GLOBAL_PORT;
const userDataDir = path.join(__dirname, 'chrome_profile');
const activeDataPath = path.join(__dirname, 'activeTab.txt');
const runIdPath = path.join(__dirname, 'run_id.txt');
const sessionPath = path.join(__dirname, 'session.json');
const authReqPath = path.join(__dirname, 'auth_required.json');
const learningPath = path.join(__dirname, 'self_learning.json');
const customFilesDir = path.join(__dirname, 'custom_files');
// Ensure custom_files exists
if (!fs.existsSync(customFilesDir)) fs.mkdirSync(customFilesDir, { recursive: true });
const resolveFilePath = (f) => {
if (!f) return null;
if (fs.existsSync(f)) return f;
const customPath = path.join(customFilesDir, f);
if (fs.existsSync(customPath)) return customPath;
return null;
};
// HEALING: Ensure sensitive files have restrictive permissions on *nix systems
const hardenFile = (filePath) => {
if (!isWin && fs.existsSync(filePath)) {
try { fs.chmodSync(filePath, 0o600); } catch (e) { }
}
};
const getOutputDir = () => {
let runId;
if (fs.existsSync(runIdPath)) {
// SECURITY: Whitelist-only sanitization of runId from file to break taint flow (untrusted file -> sink).
const rawId = fs.readFileSync(runIdPath, 'utf8').trim();
runId = rawId.replace(/[^a-zA-Z0-9_\-]/g, '').substring(0, 50);
if (!runId || runId.length < 5) runId = `run_safe_Date.now()`;
} else {
runId = `run_.]/g, '-')`;
fs.writeFileSync(runIdPath, runId);
}
const outputDir = path.join(__dirname, 'recordings', runId);
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
return { outputDir, runId };
};
const debug = (msg, type = "INFO") => {
const { outputDir } = getOutputDir();
const timestamp = new Date().toISOString();
const line = `[timestamp] [type] msg\n`;
process.stdout.write(line);
fs.appendFileSync(path.join(outputDir, 'debug.log'), line);
};
const getElementDNA = (targetTag) => {
try {
const { outputDir } = getOutputDir();
const files = fs.readdirSync(outputDir).filter(f => f.startsWith('snapshot_') && f.endsWith('.json'));
if (files.length === 0) return null;
const latestFile = files.sort().reverse()[0];
const data = JSON.parse(fs.readFileSync(path.join(outputDir, latestFile), 'utf8'));
return data.elements.find(el => el.tag === targetTag);
} catch (e) { return null; }
};
const url = args.includes('--url') ? args[args.indexOf('--url') + 1] : null;
const tag = args.includes('--tag') ? args[args.indexOf('--tag') + 1] : null;
const text = args.includes('--text') ? args[args.indexOf('--text') + 1] : null;
const query = args.includes('--query') ? args[args.indexOf('--query') + 1] : null;
const file = args.includes('--file') ? args[args.indexOf('--file') + 1] : null;
const code = args.includes('--code') ? args[args.indexOf('--code') + 1] : null;
const index = args.includes('--index') ? parseInt(args[args.indexOf('--index') + 1]) : null;
// 1. START BACKGROUND BROWSER
if (command === 'start') {
// SECURITY: Generate a RANDOM debug port to eliminate predictable attack surface.
// This port is then persisted to 'debug_port.txt' for auto-discovery.
if (!process.env.GOD_DEBUG_PORT) {
const randomPort = Math.floor(Math.random() * (65535 - 10240 + 1)) + 10240;
DEBUG_PORT = String(randomPort);
fs.writeFileSync(portPath, DEBUG_PORT);
debug(`🧭 Random Port Discovery enabled: Binding to DEBUG_PORT`, "INFO");
}
const browserArgs = [
`--remote-debugging-port=DEBUG_PORT`, // Remote Debug Port (Secured, Randomized & Static-Linked)
'--no-first-run',
'--no-default-browser-check',
`--user-data-dir=userDataDir`, // Saves cookies & sessions forever!
'--disable-blink-features=AutomationControlled', // Bypass Bot Checks
'--disable-accelerated-2d-canvas',
'--disable-infobars',
'--window-size=1280,800',
'--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
isHeadless ? '--headless=new' : '',
isWin ? '' : '--no-sandbox'
].filter(Boolean);
// Explicitly set shell: false to avoid command injection risks.
// EXECUTABLE_PATH is hardcoded based on OS, and browserArgs are controlled arguments.
const child = spawn(EXECUTABLE_PATH, browserArgs, {
detached: true,
stdio: 'ignore',
shell: false
});
child.unref();
await new Promise(resolve => setTimeout(resolve, 3000));
let browserInstance;
try {
browserInstance = await puppeteer.connect({ browserURL: `http://127.0.0.1:DEBUG_PORT`, defaultViewport: null });
const pages = await browserInstance.pages();
const page = pages[0];
await page.goto('https://www.google.com', { waitUntil: 'load', timeout: 10000 });
} catch (err) {
console.error("❌ Failed to connect to the newly started browser or navigate to URL.", err instanceof Error ? err.message : err);
} finally {
if (browserInstance) {
// await browserInstance.disconnect(); // Disconnecting is not necessary as the script exits.
}
}
console.log("🚀 GOD OF ALL BROWSERS started in the background (Port 10087)!");
console.log("Your cookies/session are natively stored in 'chrome_profile' folder.");
process.exit(0);
}
let browser;
try {
browser = await puppeteer.connect({
browserURL: `http://127.0.0.1:DEBUG_PORT`,
defaultViewport: null,
protocolTimeout: 1800000 // 30 minutes
});
} catch (e) {
console.error(`❌ GOD OF ALL BROWSERS is not running on Port DEBUG_PORT. Please run 'node browser.js start' first.`);
return;
}
if (command === 'stop') {
await browser.close();
if (fs.existsSync(activeDataPath)) fs.unlinkSync(activeDataPath);
if (fs.existsSync(runIdPath)) fs.unlinkSync(runIdPath);
if (fs.existsSync(portPath)) fs.unlinkSync(portPath);
console.log("🛑 GOD OF ALL BROWSERS stopped. Session cleared.");
return;
}
try {
const pages = await browser.pages();
let activeIndex = 0;
if (fs.existsSync(activeDataPath)) {
activeIndex = parseInt(fs.readFileSync(activeDataPath, 'utf8')) || 0;
}
if (activeIndex >= pages.length) activeIndex = 0;
let targetPage = pages[activeIndex];
// SMARTER TAB DISCOVERY: If current tab is blank or chrome-internal, but others have real URLs, AUTO-SWITCH
if ((targetPage.url() === 'about:blank' || targetPage.url().startsWith('chrome://')) && command !== 'start' && command !== 'switch-tab' && !url) {
const realPage = pages.find(p => p.url() !== 'about:blank' && !p.url().startsWith('chrome://') && !p.url().includes('devtools://'));
if (realPage) {
targetPage = realPage;
activeIndex = pages.indexOf(targetPage);
fs.writeFileSync(activeDataPath, activeIndex.toString());
debug(`🧭 Auto-focused on active content: targetPage.url() (Tab [activeIndex])`);
}
}
// FORCE NAVIGATION: If user provided a URL, use the targetPage to go there
if (url && (targetPage.url() === 'about:blank' || targetPage.url().startsWith('chrome://'))) {
debug(`📍 Redirecting tab to: url`);
await targetPage.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
}
// AGGRESSIVE VISIBILITY: Bring window to front and add visual indicator
try {
await targetPage.bringToFront();
await targetPage.evaluate(() => {
window.focus();
if (!document.getElementById('bot-indicator')) {
const div = document.createElement('div');
div.id = 'bot-indicator';
div.innerHTML = '🤖 <b>GOD OF ALL BROWSERS ACTIVE</b> - Processing Page...';
div.style = 'position:fixed; top:0; left:50%; transform:translateX(-50%); background:#00ff00; color:black; padding:5px 20px; z-index:999999; font-family:sans-serif; border-bottom-left-radius:10px; border-bottom-right-radius:10px; box-shadow:0 2px 10px rgba(0,0,0,0.5); pointer-events:none; font-size:12px; font-weight:bold;';
document.body.appendChild(div);
}
});
} catch (e) { }
await targetPage.evaluateOnNewDocument(() => { Object.defineProperty(navigator, "webdriver", { get: () => false }); });
// ATTACH DEBUG LISTENERS
targetPage.on('console', msg => {
const text = msg.text();
const type = msg.type();
if (type === 'error' || type === 'warn' || text.toLowerCase().includes('error') || text.toLowerCase().includes('fail')) {
debug(`[BROWSER CONSOLE] type.toUpperCase(): text`, "DEBUG");
}
});
pages.forEach(p => {
p.on('dialog', async dialog => {
debug(`[DIALOG] Auto-accepting dialog: dialog.message()`, "INFO");
try { await dialog.accept(); } catch (e) { }
});
});
targetPage.on('requestfailed', navReq => {
const failure = navReq.failure();
if (failure) debug(`[NETWORK FAILED] navReq.url() - failure.errorText`, "DEBUG");
});
if (fs.existsSync(sessionPath)) {
try {
const cookies = JSON.parse(fs.readFileSync(sessionPath));
const now = Date.now() / 1000;
const validCookies = cookies.filter((c) => !c.expires || c.expires > now);
if (validCookies.length > 0) await targetPage.setCookie(...validCookies);
} catch (e) {
console.warn("⚠️ Could not load session.json");
}
}
const runCleanupAndExpand = async (pageOrFrame) => {
try {
if (typeof pageOrFrame.isDetached === 'function' && pageOrFrame.isDetached()) return;
await pageOrFrame.evaluate(() => {
const closeSelectors = ['.chat-window .crossIcon', '.chatbot .crossIcon', '[aria-label="close"]', '[aria-label="Close"]', '.btn-close', '.close-btn', '#bot-close-icon', '.drawer-wrapper .crossIcon', '#google-ytd-close', '.modal-close'];
closeSelectors.forEach(sel => {
document.querySelectorAll(sel).forEach(btn => {
try { btn.click(); } catch (e) { }
});
});
const expandSelectors = [
'.read-more', '.view-more', '.show-more', '[data-label="read-more"]',
'.view-all-link', '.see-more', '.expander', '.collapsible'
];
let expandedCount = 0;
expandSelectors.forEach(sel => {
document.querySelectorAll(sel).forEach(btn => {
try { btn.click(); expandedCount++; } catch (e) { }
});
});
const textLinks = Array.from(document.querySelectorAll('a, span, button'))
.filter(el => {
const t = el.innerText.toLowerCase();
return t === 'read more' || t === 'view more' || t.includes('show all') || t.includes('see more');
});
textLinks.forEach(link => {
try { link.click(); expandedCount++; } catch (e) { }
});
if (expandedCount > 0) console.log(`🚀 Auto-expanded expandedCount items!`);
});
await new Promise(r => setTimeout(r, 1000));
} catch (e) {
// Ignore errors from detached frames
}
};
// 2. SWITCH TABS
if (command === 'switch-tab') {
if (index !== null && index < pages.length) {
targetPage = pages[index];
await targetPage.bringToFront();
fs.writeFileSync(activeDataPath, index.toString());
console.log(`✅ Success! Brought Tab [index] to front. Current URL: targetPage.url()`);
} else {
console.error(`❌ Invalid index. Use 'node browser.js check-tabs' to see available indices.`);
}
}
// 3. CHECK ALL OPEN TABS
else if (command === 'check-tabs') {
console.log(`📑 Available Tabs: (pages.length total)`);
pages.forEach((p, i) => {
const status = (i === activeIndex) ? "(ACTIVE)" : "";
if (!p.url().includes('devtools://')) console.log(`[i] p.url() status`);
});
console.log("Use: node browser.js switch-tab --index <number>");
}
// 4. CHECK URL
else if (command === 'check-url') {
console.log(`🔗 Current URL of Tab [activeIndex]: targetPage.url()`);
console.log(`📄 Title: await targetPage.title()`);
}
// 5. ENHANCED SNAPSHOT
if (command === 'navigate') {
if (url) {
debug(`🌐 Navigating to: url`);
await targetPage.goto(url, { waitUntil: 'networkidle2', timeout: 90000 });
} else {
console.error("❌ Use --url");
}
}
else if (command === 'wait') {
const timeout = parseInt(args.includes('--timeout') ? args[args.indexOf('--timeout') + 1] : "5000");
const selector = args.includes('--selector') ? args[args.indexOf('--selector') + 1] : null;
if (selector) {
debug(`⏳ Waiting for selector: selector...`);
await targetPage.waitForSelector(selector, { timeout });
} else {
debug(`😴 Sleeing for timeoutms...`);
await new Promise(r => setTimeout(r, timeout));
}
}
else if (command === 'scroll') {
const direction = args.includes('--dir') ? args[args.indexOf('--dir') + 1] : 'down';
const amount = parseInt(args.includes('--amount') ? args[args.indexOf('--amount') + 1] : '500');
await targetPage.evaluate((d, a) => {
window.scrollBy({ top: d === 'down' ? a : -a, behavior: 'smooth' });
}, direction, amount);
debug(`📜 Scrolled direction by amountpx.`);
}
else if (command === 'snapshot') {
if (url) {
console.log(`Navigating to url...`);
try {
await targetPage.goto(url, { waitUntil: 'load', timeout: 60000 });
} catch (e) {
await new Promise(r => setTimeout(r, 3000));
}
await new Promise(r => setTimeout(r, 4000)); // wait for dynamic content
}
await runCleanupAndExpand(targetPage);
const data = await targetPage.evaluate(() => {
const interactables = Array.from(document.querySelectorAll(`
button,
a,
input,
select,
textarea,
[role="button"],
[onclick],
[role="link"],
.dropdown,
.btn,
.link,
.menu-item,
.nav-item,
.card,
.tile,
.item,
[tabindex],
[data-testid],
[data-qa],
[data-cy],
[data-test],
.search-btn,
.submit-btn,
.next-btn,
.prev-btn,
.close-btn,
.modal-close,
.popup-close,
.overlay-close
`));
const uniqueElements = [];
const processed = new Set();
interactables.forEach((el, i) => {
const rect = el.getBoundingClientRect();
const isVisible = rect.width > 0 && rect.height > 0 &&
window.getComputedStyle(el).visibility !== 'hidden' &&
window.getComputedStyle(el).display !== 'none';
if (isVisible) {
const identifier = `el.tagName_el.className_el.innerText?.substring(0, 50) || ''_rect.x_rect.y`;
if (!processed.has(identifier)) {
processed.add(identifier);
uniqueElements.push(el);
}
}
});
return uniqueElements.map((element, index) => {
const el = element;
el.setAttribute('data-god-tag', `[index]`);
el.style.outline = '3px solid #00ff00';
el.style.outlineOffset = '-3px';
el.style.backgroundColor = 'rgba(0, 255, 0, 0.1)';
el.style.boxShadow = '0 0 5px rgba(0, 255, 0, 0.5)';
let text = '';
if (el.innerText && el.innerText.trim()) {
text = el.innerText.trim().replace(/\s+/g, ' ').substring(0, 150);
} else if (el.textContent && el.textContent.trim()) {
text = el.textContent.trim().replace(/\s+/g, ' ').substring(0, 150);
} else if (el.value && el.value.trim()) {
text = el.value.trim().substring(0, 150);
} else if (el.placeholder && el.placeholder.trim()) {
text = el.placeholder.trim().substring(0, 150);
} else if (el.getAttribute('aria-label')) {
text = el.getAttribute('aria-label').trim().substring(0, 150);
} else if (el.getAttribute('title')) {
text = el.getAttribute('title').trim().substring(0, 150);
} else if (el.getAttribute('data-label')) {
text = el.getAttribute('data-label').trim().substring(0, 150);
} else {
const tagName = el.tagName.toLowerCase();
const className = el.className ? `.el.className.split(' ')[0]` : '';
const id = el.id ? `#el.id` : '';
text = `tagNameclassNameid`;
}
text = text.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
return {
tag: `[index]`,
type: el.tagName.toLowerCase(),
text,
visible: true,
position: {
x: Math.round(el.getBoundingClientRect().x),
y: Math.round(el.getBoundingClientRect().y),
width: Math.round(el.getBoundingClientRect().width),
height: Math.round(el.getBoundingClientRect().height)
},
attributes: {
id: el.id || null,
className: el.className || null,
href: el.href || null,
value: el.value || null,
placeholder: el.placeholder || null,
ariaLabel: el.getAttribute('aria-label') || null,
title: el.getAttribute('title') || null
}
};
});
});
const { outputDir, runId } = getOutputDir();
const snapshotJsonPath = path.join(outputDir, `snapshot_Date.now().json`);
const snapshotPngPath = path.join(outputDir, `snapshot_Date.now().png`);
const output = {
runId,
tabIndex: activeIndex,
url: targetPage.url(),
title: await targetPage.title(),
total_open_tabs: pages.length,
elements: data
};
console.log(JSON.stringify(output, null, 2));
fs.writeFileSync(snapshotJsonPath, JSON.stringify(output, null, 2));
await targetPage.screenshot({ path: snapshotPngPath });
console.log(`📸 Saved assets to: outputDir`);
}
// 6. CLICK (Detects New Tabs Native)
else if (command === 'click') {
if (url && targetPage.url() !== url && !targetPage.url().includes(url)) {
await targetPage.goto(url, { waitUntil: 'domcontentloaded' });
}
const currentTabCount = pages.length;
const dna = getElementDNA(tag);
const result = await targetPage.evaluate((t, d) => {
let el = document.querySelector(`[data-god-tag="t"]`);
let strategy = "primary";
if (!el && d) {
if (d.attributes && d.attributes.id) {
el = document.getElementById(d.attributes.id);
if (el) strategy = "healing-id";
}
if (!el && d.text) {
el = Array.from(document.querySelectorAll(d.type || '*'))
.find(e => (e instanceof HTMLElement ? e.innerText : e.textContent || "").includes(d.text));
if (el) strategy = "healing-text";
}
}
if (!el) {
const cleanLabel = t.replace(/[\[\]]/g, '');
el = Array.from(document.querySelectorAll('button, a, input, [role="button"]'))
.find(e => (e instanceof HTMLElement ? e.innerText : e.textContent || "").includes(cleanLabel));
if (el) strategy = "fallback-text";
}
if (el instanceof HTMLElement) {
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
el.click();
return { success: true, strategy };
}
return { success: false };
}, tag, dna);
if (result.success) {
console.log(`✅ Click processed using strategy: result.strategy`);
console.log(`🖱️ Clicked tag. Waiting 10 seconds for page to react/load...`);
await new Promise(r => setTimeout(r, 10000));
const updatedPages = await browser.pages();
if (updatedPages.length > currentTabCount) {
const newIndex = updatedPages.length - 1;
fs.writeFileSync(activeDataPath, newIndex.toString());
console.log(`----------\n⚠️ A NEW TAB WAS OPENED!!\nAutomatically switched context to Tab [newIndex].\nURL: updatedPages[newIndex].url()\nTitle: await updatedPages[newIndex].title()\nUse 'snapshot' immediately to see the new page elements.\n----------`);
} else {
console.log(`✅ Click processed. Current URL is now: targetPage.url()`);
}
let finalActiveTab;
try {
const activeIdx = fs.existsSync(activeDataPath) ? parseInt(fs.readFileSync(activeDataPath, 'utf8')) : -1;
finalActiveTab = (activeIdx >= 0 && activeIdx < updatedPages.length) ? updatedPages[activeIdx] : targetPage;
} catch (err) {
finalActiveTab = targetPage;
}
if (finalActiveTab) {
const { outputDir } = getOutputDir();
const screenshotPath = path.join(outputDir, `click_Date.now().png`);
try {
await finalActiveTab.screenshot({ path: screenshotPath });
console.log(`🖼️ Screenshot saved to: screenshotPath`);
} catch (snapErr) {
console.warn(`⚠️ Could not take screenshot for command:`, snapErr instanceof Error ? snapErr.message : snapErr);
}
}
} else {
console.log(`❌ Failed to find element tag`);
}
}
// 7. TYPE
else if (command === 'type') {
if (url && targetPage.url() !== url && !targetPage.url().includes(url)) {
await targetPage.goto(url, { waitUntil: 'domcontentloaded' });
}
if (!tag || !text) {
console.error("❌ Use --tag \"[number]\" and --text \"your input\"");
return;
}
const dna = getElementDNA(tag);
const result = await targetPage.evaluate((t, val, d) => {
let el = document.querySelector(`[data-god-tag="t"]`);
let strategy = "primary";
if (!el && d) {
if (d.attributes && d.attributes.id) {
el = document.getElementById(d.attributes.id);
if (el) strategy = "healing-id";
}
if (!el && d.text) {
el = Array.from(document.querySelectorAll('input, textarea, [contenteditable="true"]'))
.find(e => (e instanceof HTMLElement ? e.innerText : e.textContent || "").includes(d.text));
if (el) strategy = "healing-text";
}
}
if (!el) {
const cleanLabel = t.replace(/[\[\]]/g, '');
el = Array.from(document.querySelectorAll('input, textarea, [contenteditable="true"]'))
.find(e => (e instanceof HTMLElement ? e.innerText : e.textContent || "").includes(cleanLabel));
if (el) strategy = "fallback-text";
}
if (el instanceof HTMLElement) {
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
el.focus();
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
el.value = val;
} else {
el.innerText = val;
}
['input', 'change', 'blur'].forEach(ev => el.dispatchEvent(new Event(ev, { bubbles: true })));
return { success: true, strategy };
}
return { success: false };
}, tag, text, dna);
if (result.success) {
console.log(`✅ Type processed using strategy: result.strategy`);
await targetPage.keyboard.press('Enter'); // Standard auto-enter for convenience
const { outputDir } = getOutputDir();
const screenshotPath = path.join(outputDir, `type_Date.now().png`);
await targetPage.screenshot({ path: screenshotPath });
console.log(`🖼️ Screenshot saved to: screenshotPath`);
} else {
console.log(`❌ Failed to find element tag for typing.`);
}
}
// 8. PRESS KEY
else if (command === 'press') {
const key = args.includes('--key') ? args[args.indexOf('--key') + 1] : 'Enter';
await targetPage.keyboard.press(key);
console.log(`🎹 Pressed key: key`);
}
// 8b. READ CONTENT
else if (command === 'read') {
const selector = args.includes('--selector') ? args[args.indexOf('--selector') + 1] : 'body';
const tagVal = args.includes('--tag') ? args[args.indexOf('--tag') + 1] : null;
await runCleanupAndExpand(targetPage);
let activeFrame = targetPage;
let finalSelector = selector;
if (tagVal) {
const frames = targetPage.frames();
for (const frame of frames) {
try {
if (frame.isDetached()) continue;
const exists = await frame.evaluate((t) => !!document.querySelector(`[data-god-tag="t"]`), tagVal);
if (exists) {
activeFrame = frame;
finalSelector = `[data-god-tag="tagVal"]`;
break;
}
} catch (e) { }
}
}
console.log(`📖 Reading content from finalSelector...`);
let content = await activeFrame.evaluate((s) => {
const el = document.querySelector(s);
if (!el) return null;
return (el instanceof HTMLElement) ? el.innerText : el.textContent;
}, finalSelector);
if (content === null && !tagVal) {
for (const frame of targetPage.frames()) {
if (frame === targetPage || frame.isDetached()) continue;
try {
content = await frame.evaluate((s) => {
const el = document.querySelector(s);
if (!el) return null;
return (el instanceof HTMLElement) ? el.innerText : el.textContent;
}, finalSelector);
if (content !== null) break;
} catch (e) { }
}
}
if (content !== null) {
console.log(content.trim());
} else {
console.error(`❌ Could not find content for selector: finalSelector`);
}
}
// 8c. EXPAND CONTENT
else if (command === 'expand') {
console.log("🔍 Running deep-expand on all frames...");
for (const frame of targetPage.frames()) {
if (frame.isDetached()) continue;
await runCleanupAndExpand(frame);
}
console.log("✅ Expansion complete.");
}
// 9. REFRESH PAGE
else if (command === 'refresh') {
const pages = await browser.pages();
const activeIndex = fs.existsSync(activeDataPath) ? parseInt(fs.readFileSync(activeDataPath, 'utf8')) : 0;
const targetPage = pages[activeIndex] || pages[0];
console.log(`🔄 Refreshing Tab [activeIndex]...`);
await targetPage.reload({ waitUntil: 'networkidle2' });
const { outputDir } = getOutputDir();
const screenshotPath = path.join(outputDir, `refresh_Date.now().png`);
await targetPage.screenshot({ path: screenshotPath });
console.log(`✅ Page refreshed. Screenshot saved to: screenshotPath`);
}
// 10. FIND (Accurate Filtered Search)
else if (command === 'find') {
let elements = [];
let source = "";
const resolvedFile = resolveFilePath(file);
if (resolvedFile) {
try {
const rawData = fs.readFileSync(resolvedFile, 'utf8').includes('{') ? fs.readFileSync(resolvedFile, 'utf8') : fs.readFileSync(resolvedFile, 'utf16le');
const jsonData = rawData.substring(rawData.indexOf('{'), rawData.lastIndexOf('}') + 1);
const parsed = JSON.parse(jsonData);
elements = parsed.elements || [];
source = `File: resolvedFile`;
} catch (e) {
console.error(`❌ Failed to process resolvedFile:`, e instanceof Error ? e.message : e);
return;
}
} else {
if (url) await targetPage.goto(url, { waitUntil: 'domcontentloaded' });
const snapshotData = await targetPage.evaluate(() => {
const interactables = Array.from(document.querySelectorAll('button, a, input, select, textarea, [role="button"], [onclick], [role="link"], .dropdown, .btn, .link'));
return interactables.map((el, i) => {
const tag = `[i]`;
el.setAttribute('data-god-tag', tag);
const text = (el.innerText || el.value || el.placeholder || el.getAttribute('aria-label') || '').trim().replace(/\s+/g, ' ').substring(0, 150);
return {
tag,
type: el.tagName.toLowerCase(),
text,
visible: el.offsetWidth > 0 && el.offsetHeight > 0
};
}).filter(item => item.visible);
});
elements = snapshotData;
source = `Live: targetPage.url()`;
}
if (query) {
const keywords = query.toLowerCase().split(',').map(k => k.trim());
elements = elements.filter(e =>
keywords.some(keyword => e.text.toLowerCase().includes(keyword) || e.type.toLowerCase() === keyword)
);
}
console.log(JSON.stringify({
status: "success",
source,
matched_count: elements.length,
elements
}, null, 2));
}
// 11. SCRAP META TAGS
else if (command === 'scrap-meta') {
console.log(`🔍 Extracting meta tags from: targetPage.url()`);
const metaTags = await targetPage.evaluate(() => {
const results = {};
const tags = Array.from(document.querySelectorAll('meta, title'));
tags.forEach(tag => {
if (tag.tagName.toLowerCase() === 'title') {
results['title'] = tag.innerText;
} else {
const name = tag.getAttribute('name') || tag.getAttribute('property');
const content = tag.getAttribute('content');
if (name && content) {
results[name] = content;
}
}
});
return results;
});
const { outputDir } = getOutputDir();
const metaPath = path.join(outputDir, `meta_Date.now().json`);
fs.writeFileSync(metaPath, JSON.stringify(metaTags, null, 2));
console.log(JSON.stringify({
status: "success",
url: targetPage.url(),
count: Object.keys(metaTags).length,
metadata: metaTags
}, null, 2));
console.log(`📄 Metadata saved to: metaPath`);
}
// 13. SAVE SESSION
else if (command === 'save-session') {
const newCookies = await targetPage.cookies();
let allCookies = [];
if (fs.existsSync(sessionPath)) {
try {
allCookies = JSON.parse(fs.readFileSync(sessionPath, 'utf8'));
} catch (e) { }
}
const currentDomain = new URL(targetPage.url()).hostname.replace('www.', '');
allCookies = allCookies.filter(c => !c.domain.includes(currentDomain));
allCookies.push(...newCookies);
fs.writeFileSync(sessionPath, JSON.stringify(allCookies, null, 2));
hardenFile(sessionPath);
console.log(`💾 Merged and saved cookies to session.json. Total session cookies: allCookies.length`);
console.log(`🔒 SECURITY: Ensure session.json is protected. It contains plain-text credentials.`);
if (fs.existsSync(authReqPath)) {
try {
const authTasks = JSON.parse(fs.readFileSync(authReqPath, 'utf8'));
const domain = new URL(targetPage.url()).hostname;
if (authTasks[domain]) {
delete authTasks[domain];
fs.writeFileSync(authReqPath, JSON.stringify(authTasks, null, 2));
console.log(`✨ Cleared auth requirement log for domain`);
}
} catch (e) { }
}
}
// 14. AUTH STATUS
else if (command === 'auth-status') {
const status = await targetPage.evaluate(() => {
const loginText = ['sign in', 'login', 'log in', 'signin', 'auth'].some(t => {
const elements = Array.from(document.querySelectorAll('a, button, span')).filter(el => {
const style = window.getComputedStyle(el);
return style.display !== 'none' && style.visibility !== 'hidden' && el.innerText.toLowerCase().includes(t);
});
return elements.length > 0;
});
const loginInputs = !!document.querySelector('input[type="password"]');
const userProfile = !!(
document.querySelector('.profile, .user-menu, .account, [aria-label*="account"], [aria-label*="profile"], .avatar, [class*="avatar"], img[alt*="profile"], img[alt*="avatar"]') ||
Array.from(document.querySelectorAll('button, a')).some(el => {
const label = (el.getAttribute('aria-label') || "").toLowerCase();
const text = el.innerText.toLowerCase();
return label.includes('account') || label.includes('profile') || text.includes('my account') || text.includes('logout') || text.includes('sign out');
})
);
return { loginText, loginInputs, userProfile, url: window.location.href };
});
const domain = new URL(targetPage.url()).hostname;
console.log(JSON.stringify(status, null, 2));
if ((status.loginInputs || status.loginText) && !status.userProfile) {
console.warn(`⚠️ Authentication might be required on domain`);
let authTasks = {};
if (fs.existsSync(authReqPath)) {
try { authTasks = JSON.parse(fs.readFileSync(authReqPath, 'utf8')); } catch (e) { }
}
authTasks[domain] = { status, timestamp: new Date().toISOString() };
fs.writeFileSync(authReqPath, JSON.stringify(authTasks, null, 2));
}
}
// 15. LOG LEARNING
else if (command === 'log-learning') {
const what_failed = args.includes('--failed') ? args[args.indexOf('--failed') + 1] : 'Unknown failure';
const how_fixed = args.includes('--fixed') ? args[args.indexOf('--fixed') + 1] : 'No fix provided';
const lessons = args.includes('--lessons') ? args[args.indexOf('--lessons') + 1] : 'No lesson provided';
const context = tag || 'General';
let learningLog = [];
if (fs.existsSync(learningPath)) {
try { learningLog = JSON.parse(fs.readFileSync(learningPath, 'utf8')); } catch (e) { }
}
const newEntry = {
id: `LEARN_Date.now()`,
what_failed,
how_fixed,
lessons_learned: lessons,
timestamp: new Date().toISOString(),
context,
url: targetPage.url()
};
learningLog.push(newEntry);
fs.writeFileSync(learningPath, JSON.stringify(learningLog, null, 2));
console.log(`🧠 Self-improvement logged: newEntry.id`);
}
// 12. DYNAMIC EVALUATE
else if (command === 'eval') {
// SECURITY GATE: Require explicit confirmation for arbitrary code execution.
if (!args.includes('--force') && process.env.GOD_AUTO_EVAL !== 'true') {
console.error("❌ SECURITY GATE: Usage of 'eval' requires the '--force' flag or 'GOD_AUTO_EVAL=true' environment variable.");
console.error("This is an intentional safety guard because 'eval' can execute arbitrary scripts.");
console.error("\nUsage: node browser.js eval --code '...' --force");
return;
}
const resolvedFile = resolveFilePath(file);
const script = code || (resolvedFile ? fs.readFileSync(resolvedFile, 'utf8') : null);
if (!script) {
console.error("❌ No script provided. Use --code '...' or --file path/to/script.js");
return;
}
console.log(`🚀 Injecting custom logic into: targetPage.url()`);
const lowerCode = script.toLowerCase();
const hasNetwork = lowerCode.includes('fetch') || lowerCode.includes('xmlhttprequest') || lowerCode.includes('navigator.sendbeacon');
if (hasNetwork) {
debug(`🔍 SECURITY NOTICE: Script contains network request patterns. Ensure it is not exfiltrating data.`, "WARN");
}
debug(`⚠️ SECURITY WARNING: Running custom script via 'eval' command. Only use trusted scripts.`, "WARN");
const result = await targetPage.evaluate(async (codeStr) => {
try {
// Use AsyncFunction constructor for a cleaner execution environment support for await.
// This is an intentional feature of "GOD OF ALL BROWSERS" for automation power-users.
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
return await (new AsyncFunction(codeStr))();
} catch (e) {
return { error: e instanceof Error ? e.message : String(e) };
}
}, script);
const outputRes = {
status: (result && result.error) ? "error" : "success",
timestamp: new Date().toISOString(),
result: result ?? null
};
console.log(JSON.stringify(outputRes, null, 2));
const { outputDir } = getOutputDir();
const resultPath = path.join(outputDir, `eval_Date.now().json`);
fs.writeFileSync(resultPath, JSON.stringify(outputRes, null, 2));
console.log(`💾 Result saved to: resultPath`);
}
// 10. GOOGLE SEARCH (Automated Extraction)
else if (command === 'google') {
const searchQuery = query || (args.includes('--q') ? args[args.indexOf('--q') + 1] : null);
if (!searchQuery) {
console.error("❌ Please provide a search query with --query or --q");
process.exit(1);
}
try {
const pages = await browser.pages();
const page = pages[0];
// SECURITY: Sanitize search query input to prevent 'User Input to Network Sink' vulnerabilities.
const cleanQuery = searchQuery.replace(/[\r\n]/g, ' ').substring(0, 500);
const searchUrl = `https://www.google.com/search?q=encodeURIComponent(cleanQuery)`;
await page.goto(searchUrl, { waitUntil: 'load', timeout: 60000 });
try {
await page.waitForFunction(() => document.querySelectorAll('h3').length >= 1, { timeout: 8000 });
} catch (e) {
console.warn("⚠️ Google results took too long, attempting extraction anyway...");
await new Promise(r => setTimeout(r, 2000));
}
const results = await page.evaluate(() => {
return Array.from(document.querySelectorAll('h3'))
.map(h3 => {
const anchor = h3.closest('a');
if (!anchor) return null;
const container = h3.closest('div.g') || h3.parentElement;
return {
title: h3.innerText,
link: anchor.href,
snippet: container ? container.innerText.substring(0, 150).replace(/\n/g, ' ') : ""
};
})
.filter(r => r && r.link && r.link.startsWith('http') && !r.link.includes('google.com/search'));
});
const { outputDir } = getOutputDir();
const outputObj = { status: "success", query: searchQuery, results, count: results.length };
console.log(JSON.stringify(outputObj, null, 2));
const resultFile = path.join(outputDir, `google_Date.now().json`);
fs.writeFileSync(resultFile, JSON.stringify(outputObj, null, 2));
console.log(`💾 Results saved to: resultFile`);
} catch (err) {
console.error("❌ Google search failed:", err instanceof Error ? err.message : err);
}
}
} catch (err) {
console.error('❌ Error executing command:', err instanceof Error ? err.message : err);
} finally {
browser.disconnect();
}
}
run();
FILE:package-lock.json
{
"name": "claw-browser",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"puppeteer-core": "^24.38.0"
}
},
"node_modules/@puppeteer/browsers": {
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz",
"integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==",
"license": "Apache-2.0",
"dependencies": {
"debug": "^4.4.3",
"extract-zip": "^2.0.1",
"progress": "^2.0.3",
"proxy-agent": "^6.5.0",
"semver": "^7.7.4",
"tar-fs": "^3.1.1",
"yargs": "^17.7.2"
},
"bin": {
"browsers": "lib/cjs/main-cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@tootallnate/quickjs-emscripten": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
"license": "MIT"
},
"node_modules/@types/node": {
"version": "25.3.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.5.tgz",
"integrity": "sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==",
"license": "MIT",
"optional": true,
"dependencies": {
"undici-types": "~7.18.0"
}
},
"node_modules/@types/yauzl": {
"version": "2.10.3",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
"integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/ast-types": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
"integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.1"
},
"engines": {
"node": ">=4"
}
},
"node_modules/b4a": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz",
"integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==",
"license": "Apache-2.0",
"peerDependencies": {
"react-native-b4a": "*"
},
"peerDependenciesMeta": {
"react-native-b4a": {
"optional": true
}
}
},
"node_modules/bare-events": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz",
"integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==",
"license": "Apache-2.0",
"peerDependencies": {
"bare-abort-controller": "*"
},
"peerDependenciesMeta": {
"bare-abort-controller": {
"optional": true
}
}
},
"node_modules/bare-fs": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.5.tgz",
"integrity": "sha512-XvwYM6VZqKoqDll8BmSww5luA5eflDzY0uEFfBJtFKe4PAAtxBjU3YIxzIBzhyaEQBy1VXEQBto4cpN5RZJw+w==",
"license": "Apache-2.0",
"dependencies": {
"bare-events": "^2.5.4",
"bare-path": "^3.0.0",
"bare-stream": "^2.6.4",
"bare-url": "^2.2.2",
"fast-fifo": "^1.3.2"
},
"engines": {
"bare": ">=1.16.0"
},
"peerDependencies": {
"bare-buffer": "*"
},
"peerDependenciesMeta": {
"bare-buffer": {
"optional": true
}
}
},
"node_modules/bare-os": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.7.1.tgz",
"integrity": "sha512-ebvMaS5BgZKmJlvuWh14dg9rbUI84QeV3WlWn6Ph6lFI8jJoh7ADtVTyD2c93euwbe+zgi0DVrl4YmqXeM9aIA==",
"license": "Apache-2.0",
"engines": {
"bare": ">=1.14.0"
}
},
"node_modules/bare-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz",
"integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
"license": "Apache-2.0",
"dependencies": {
"bare-os": "^3.0.1"
}
},
"node_modules/bare-stream": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.8.0.tgz",
"integrity": "sha512-reUN0M2sHRqCdG4lUK3Fw8w98eeUIZHL5c3H7Mbhk2yVBL+oofgaIp0ieLfD5QXwPCypBpmEEKU2WZKzbAk8GA==",
"license": "Apache-2.0",
"dependencies": {
"streamx": "^2.21.0",
"teex": "^1.0.1"
},
"peerDependencies": {
"bare-buffer": "*",
"bare-events": "*"
},
"peerDependenciesMeta": {
"bare-buffer": {
"optional": true
},
"bare-events": {
"optional": true
}
}
},
"node_modules/bare-url": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz",
"integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==",
"license": "Apache-2.0",
"dependencies": {
"bare-path": "^3.0.0"
}
},
"node_modules/basic-ftp": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz",
"integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/chromium-bidi": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz",
"integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==",
"license": "Apache-2.0",
"dependencies": {
"mitt": "^3.0.1",
"zod": "^3.24.1"
},
"peerDependencies": {
"devtools-protocol": "*"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/data-uri-to-buffer": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
"integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/degenerator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
"integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
"license": "MIT",
"dependencies": {
"ast-types": "^0.13.4",
"escodegen": "^2.1.0",
"esprima": "^4.0.1"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/devtools-protocol": {
"version": "0.0.1581282",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1581282.tgz",
"integrity": "sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ==",
"license": "BSD-3-Clause"
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/escodegen": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
"integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
"license": "BSD-2-Clause",
"dependencies": {
"esprima": "^4.0.1",
"estraverse": "^5.2.0",
"esutils": "^2.0.2"
},
"bin": {
"escodegen": "bin/escodegen.js",
"esgenerate": "bin/esgenerate.js"
},
"engines": {
"node": ">=6.0"
},
"optionalDependencies": {
"source-map": "~0.6.1"
}
},
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"license": "BSD-2-Clause",
"bin": {
"esparse": "bin/esparse.js",
"esvalidate": "bin/esvalidate.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
}
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/events-universal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
"integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
"license": "Apache-2.0",
"dependencies": {
"bare-events": "^2.7.0"
}
},
"node_modules/extract-zip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
"license": "BSD-2-Clause",
"dependencies": {
"debug": "^4.1.1",
"get-stream": "^5.1.0",
"yauzl": "^2.10.0"
},
"bin": {
"extract-zip": "cli.js"
},
"engines": {
"node": ">= 10.17.0"
},
"optionalDependencies": {
"@types/yauzl": "^2.9.1"
}
},
"node_modules/fast-fifo": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
"license": "MIT"
},
"node_modules/fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"license": "MIT",
"dependencies": {
"pend": "~1.2.0"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"license": "MIT",
"dependencies": {
"pump": "^3.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-uri": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
"integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
"license": "MIT",
"dependencies": {
"basic-ftp": "^5.0.2",
"data-uri-to-buffer": "^6.0.2",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/ip-address": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/netmask": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
"integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/pac-proxy-agent": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz",
"integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==",
"license": "MIT",
"dependencies": {
"@tootallnate/quickjs-emscripten": "^0.23.0",
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"get-uri": "^6.0.1",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.6",
"pac-resolver": "^7.0.1",
"socks-proxy-agent": "^8.0.5"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/pac-resolver": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
"integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
"license": "MIT",
"dependencies": {
"degenerator": "^5.0.0",
"netmask": "^2.0.2"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
"license": "MIT"
},
"node_modules/progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/proxy-agent": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
"integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"http-proxy-agent": "^7.0.1",
"https-proxy-agent": "^7.0.6",
"lru-cache": "^7.14.1",
"pac-proxy-agent": "^7.1.0",
"proxy-from-env": "^1.1.0",
"socks-proxy-agent": "^8.0.5"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/pump": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/puppeteer-core": {
"version": "24.38.0",
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.38.0.tgz",
"integrity": "sha512-zB3S/tksIhgi2gZRndUe07AudBz5SXOB7hqG0kEa9/YXWrGwlVlYm3tZtwKgfRftBzbmLQl5iwHkQQl04n/mWw==",
"license": "Apache-2.0",
"dependencies": {
"@puppeteer/browsers": "2.13.0",
"chromium-bidi": "14.0.0",
"debug": "^4.4.3",
"devtools-protocol": "0.0.1581282",
"typed-query-selector": "^2.12.1",
"webdriver-bidi-protocol": "0.4.1",
"ws": "^8.19.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
"license": "MIT",
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks": {
"version": "2.8.7",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
"license": "MIT",
"dependencies": {
"ip-address": "^10.0.1",
"smart-buffer": "^4.2.0"
},
"engines": {
"node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/socks-proxy-agent": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"socks": "^2.8.3"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/streamx": {
"version": "2.23.0",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz",
"integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==",
"license": "MIT",
"dependencies": {
"events-universal": "^1.0.0",
"fast-fifo": "^1.3.2",
"text-decoder": "^1.1.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/tar-fs": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz",
"integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==",
"license": "MIT",
"dependencies": {
"pump": "^3.0.0",
"tar-stream": "^3.1.5"
},
"optionalDependencies": {
"bare-fs": "^4.0.1",
"bare-path": "^3.0.0"
}
},
"node_modules/tar-stream": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz",
"integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==",
"license": "MIT",
"dependencies": {
"b4a": "^1.6.4",
"bare-fs": "^4.5.5",
"fast-fifo": "^1.2.0",
"streamx": "^2.15.0"
}
},
"node_modules/teex": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
"integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
"license": "MIT",
"dependencies": {
"streamx": "^2.12.5"
}
},
"node_modules/text-decoder": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
"integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
"license": "Apache-2.0",
"dependencies": {
"b4a": "^1.6.4"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/typed-query-selector": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.1.tgz",
"integrity": "sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==",
"license": "MIT"
},
"node_modules/undici-types": {
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"license": "MIT",
"optional": true
},
"node_modules/webdriver-bidi-protocol": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz",
"integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==",
"license": "Apache-2.0"
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
"node_modules/ws": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/yauzl": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"license": "MIT",
"dependencies": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
},
"node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}
FILE:package.json
{
"name": "god-of-all-browsers",
"version": "1.1.0",
"description": "A 100x smarter browser automation CLI that mimics human behavior using a native stateful Chromium instance.",
"main": "browser.js",
"dependencies": {
"puppeteer-core": "^24.38.0"
},
"scripts": {
"start": "node browser.js start",
"stop": "node browser.js stop"
},
"author": "Mathanraj Murugesan",
"license": "MIT"
}
FILE:README.md
# God of all Browsers 🚀
[](_meta.json)
[](LICENSE)
[](https://pptr.dev/)
**A 100x smarter browser automation CLI that mimics human behavior using a native stateful Chromium instance.**
Designed specifically for AI-native workflows, `God of all Browsers` solves the gap between vision-less AI and complex, bot-protected websites. It provides a stateful interaction layer that allows AI agents to navigate, interact, and extract data exactly like a human user.
---
## ✨ Key Features
- **🧠 Vision Abstraction (Snapshots):** Instead of navigating raw DOM, it maps every interactable element to a simple `[tag]` ID (e.g. `[1]`, `[15]`). Clicks and types are performed on these tags, eliminating CSS selector brittle-ness.
- **🔄 Stateful & Multi-Tab:** Launches a persistent background Chromium instance. Navigations, clicks opening new tabs, and cookies are preserved across separate CLI calls.
- **🛡️ Bot Evasion:** Built-in techniques to bypass detection:
- Custom User-Agents and spoofed Canvas signatures.
- Headless/Non-headless toggles.
- Automatic `webdriver` footprint removal.
- **💾 Persistent Sessions:** Uses a dedicated `chrome_profile` directory to store login states, sessions, and cookies permanently.
- **🤖 AI-First Commands:** Commands like `google`, `auth-status`, and `log-learning` are tailored for autonomous agents to succeed and self-correct.
---
## 🛠️ Installation & Setup
### Requirements
- **Node.js** (v16+)
- **Google Chrome** or **Chromium** installed on your system.
- **NPM Package Manager**
### Setup (Linux/macOS)
```bash
./setup.sh
```
### Setup (Windows)
```powershell
# Install dependencies
npm install puppeteer-core
# Ensure Chrome is installed at:
# C:\Program Files\Google\Chrome\Application\chrome.exe
```
---
## 🚀 Quick Start (Recommended Workflow)
1. **Launch the Core**: Start the persistent browser in the background.
```bash
node browser.js start
```
2. **Take a Snapshot**: Navigate to a site and get the visual "tag" map.
```bash
node browser.js snapshot --url "https://news.ycombinator.com"
```
3. **Interact via Tags**: Click the first article link (assuming tag 1).
```bash
node browser.js click --tag "[1]"
```
4. **Extract Data**: Read the content of the article.
```bash
node browser.js read --tag "[5]"
```
5. **Shutdown**: Close the browser when finished.
```bash
node browser.js stop
```
---
## 📖 Command Reference
| Command | Purpose | Example |
| :--- | :--- | :--- |
| `start` | Launches the stateful browser instance. | `node browser.js start [--headless]` |
| `snapshot` | Captures DOM/Screenshots & maps tags. | `node browser.js snapshot [--url URL]` |
| `click` | Clicks an element by `[tag]` identifier. | `node browser.js click --tag "[15]"` |
| `type` | Types text into an input field by `[tag]`. | `node browser.js type --tag "[5]" --text="..."` |
| `press` | Simulates a keyboard key press. | `node browser.js press --key "Enter"` |
| `read` | Extracts text from a tag or CSS selector. | `node browser.js read --selector "h1"` |
| `check-tabs`| Lists all currently open browser tabs. | `node browser.js check-tabs` |
| `switch-tab`| Changes focus to a specific tab index. | `node browser.js switch-tab --index 1` |
| `eval` | Executes custom JS inside the browser. | `node browser.js eval --file script.js` |
| `google` | Performs a Google Search + Data Extraction. | `node browser.js google --q "Puppeteer"` |
| `auth-status`| Checks if the current page requires login. | `node browser.js auth-status` |
| `save-session`| Persists current cookies to `session.json`. | `node browser.js save-session` |
| `stop` | Gracefully closes the browser process. | `node browser.js stop` |
---
## 🔬 Advanced Usage: `eval` Power
The `eval` command allows injecting complex, asynchronous logic directly into the browser context.
**Example: Scrape all links from a search results page**
```bash
node browser.js eval --code "return Array.from(document.querySelectorAll('a')).map(a => ({ text: a.innerText, href: a.href }))"
```
You can also run scripts from files:
```bash
node browser.js eval --file "./custom_files/extract_contacts.js"
```
---
## 🧠 Self-Learning Mechanism
Designed for autonomous agents, every failure can be logged to help the AI "learn" and avoid repeating mistakes in future tasks.
```bash
node browser.js log-learning --failed "Popup blocked click" --fixed "Use expand first"
```
---
## 📁 Project Structure
- `browser.js`: Core Puppeteer automation engine.
- `SKILL.md`: Detailed instructions for AI agents (Model-readable).
- `chrome_profile/`: Persistent user data directory (auto-generated).
- `custom_files/`: Directory for your custom script injections.
- `session.json`: Exported cookie/session data.
- `recordings/`: Snapshots and screenshots for manual review.
---
## 🛡️ Security & Ethics
`God of all Browsers` is a powerful tool designed for responsible automation. Users should be aware of the following security considerations:
### 1. **Data Persistence**
* **Persistent Profiles**: By default, this tool saves all session data, cookies, and local storage in the `./chrome_profile/` directory. This is intentional to allow stateful AI workflows.
* **Cookie Export**: The `save-session` command exports cookies to `session.json` in plain text. Always treat this file as a sensitive credential.
### 2. **Arbitrary Code Execution (`eval`)**
* The `eval` command is a high-risk feature that allows executing arbitrary JavaScript inside the browser context. Only use scripts from trusted sources and monitor agent-generated scripts for potential data exfiltration.
### 3. **Bot Evasion & Ethics**
* Built-in evasion techniques (User-Agent spoofing, canvas signatures) are intended to allow robust automation on bot-protected sites (e.g., public data extraction). They should **not** be used for malicious activities, including bypassing security controls, credential stuffing, or unauthorized access.
### 4. **Best Practices**
* Run the tool in an isolated or containerized environment if the automation task involves untrusted input or high-risk browsing.
* Regularly clear the `chrome_profile` and `session.json` if they are no longer needed.
---
## 📜 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
FILE:self_learning.json
[
{
"id": "LEARN_1773523018458",
"what_failed": "Standard window.scrollBy() fails on Google Maps sidebar.",
"how_fixed": "Target the specific [role='feed'] container and use element.scrollTop.",
"lessons_learned": "Always check for scrollable sidebar containers before falling back to window scroll.",
"timestamp": "2026-03-14T21:16:58.458Z",
"context": "Google Maps Search",
"url": "https://www.google.com/maps/search/restaurants+in+Tiruchengode/@11.3838534,77.896059,14z/data=!4m2!2m1!6e5?entry=ttu&g_ep=EgoyMDI2MDMxMS4wIKXMDSoASAFQAw%3D%3D"
},
{
"id": "LEARN_1773691270701",
"what_failed": "LinkedIn profile extraction returned masked content (asterisks)",
"how_fixed": "Dismissed login modal, used custom eval on h2 tags",
"lessons_learned": "Public LinkedIn profiles restrict data (Experience/Education) for guests. Persistent automation requires save-session after manual login.",
"timestamp": "2026-03-16T20:01:10.701Z",
"context": "General",
"url": "https://in.linkedin.com/in/mathanraj-murugesan?trk=people-guest_people_search-card"
}
]
FILE:setup.sh
#!/bin/bash
# Setup colors
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "BLUE=== Claw Browser Skill Setup ===NC"
# Check for Node.js
if ! command -v node &> /dev/null
then
echo "Node.js not found. Please install Node.js first."
exit 1
fi
# Install puppeteer-core and other libs
echo -e "GREENInstalling Puppeteer Core...NC"
npm install puppeteer-core
# Check for Chromium
if [ ! -f "/usr/bin/chromium" ]; then
echo -e "BLUEWarning: /usr/bin/chromium not found.NC"
echo -e "To use God of all Browsers on Linux, please install Chromium manually:"
echo -e " sudo apt update && sudo apt install -y chromium-browser"
echo -e "Once installed, ensure it is at /usr/bin/chromium"
fi
echo -e "GREENSetup Complete! You can now use 'node browser.js'NC"
FILE:_meta.json
{
"slug": "god-of-all-browsers",
"displayName": "God of all Browsers",
"summary": "AI-native browser automation via custom Puppeteer engine. Features: AI-optimized snapshots, session persistence, and human-like interaction.",
"version": "1.1.0",
"publishedAt": 1773092600000
}
FILE:custom_files/gmaps_contacts.js
return (async () => {
const feed = document.querySelector('[role="feed"]');
if (!feed) return 'Error: Maps feed not found.';
// Scroll deep to ensure we have at least 50 results
for (let i = 0; i < 15; i++) {
feed.scrollTop += 5000;
await new Promise(r => setTimeout(r, 2000));
if (document.querySelectorAll('.hfpxzc').length >= 50) break;
}
const items = Array.from(document.querySelectorAll('.hfpxzc'));
const results = [];
// Process top 50 items
for (let i = 0; i < Math.min(items.length, 50); i++) {
try {
const item = items[i];
const name = item.getAttribute('aria-label') || 'Unknown';
if ((i + 1) % 5 === 0) {
console.log(`--- Progress: i + 1/50 items processed ---`);
}
console.log(`Extracting [i + 1/50]: name`);
item.scrollIntoView();
item.click();
// Wait for side panel update (wait for the title to appear in the panel)
let panelLoaded = false;
for (let retry = 0; retry < 10; retry++) {
const panelTitle = document.querySelector('h1.DUwDvf')?.innerText;
if (panelTitle && (panelTitle.includes(name.substring(0, 10)) || name.includes(panelTitle.substring(0, 10)))) {
panelLoaded = true;
break;
}
await new Promise(r => setTimeout(r, 1000));
}
if (!panelLoaded) await new Promise(r => setTimeout(r, 2000));
// Extract using more specific selectors
let address = "N/A";
let phone = "N/A";
let website = "N/A";
// Address usually has data-item-id="address"
const addrEl = document.querySelector('button[data-item-id="address"] .Io6YTe');
if (addrEl) address = addrEl.innerText;
// Phone usually has data-item-id starting with "phone"
const phoneEl = document.querySelector('button[data-item-id^="phone"] .Io6YTe');
if (phoneEl) phone = phoneEl.innerText;
// Website
const webEl = document.querySelector('a[data-item-id="authority"] .Io6YTe') || document.querySelector('button[data-item-id="authority"] .Io6YTe');
if (webEl) website = webEl.innerText;
// Fallback to text searching if specific selectors failed
if (address === "N/A" || phone === "N/A") {
const allInfo = Array.from(document.querySelectorAll('.Io6YTe')).map(el => el.innerText.trim());
allInfo.forEach(text => {
if (phone === "N/A" && (/^[\d\s\-\+](\d|[\s\-\+]){7,}$/.test(text) || text.startsWith('+91'))) phone = text;
if (address === "N/A" && text.includes(',') && (text.includes('Tamil Nadu') || text.includes('Tiruchengode'))) address = text;
});
}
results.push({
"Name": name.replace(/,/g, ' '),
"Phone": phone,
"Address": address.replace(/,/g, ' '),
"Website": website,
"Maps Link": item.href
});
} catch (err) { }
}
const headers = ["Name", "Phone", "Address", "Website", "Maps Link"];
const csvContent = [headers.join(",")];
results.forEach(row => csvContent.push(headers.map(h => `"row[h]"`).join(",")));
return {
csv: csvContent.join("\n"),
count: results.length,
json: results
};
})();