Loading...
Loading...
Browser automation with persistent page state. Use when users ask to navigate websites, fill forms, take screenshots, extract web data, test web apps, or automate browser workflows. Trigger phrases include "go to [url]", "click on", "fill out the form", "take a screenshot", "scrape", "automate", "test the website", "log into", or any browser interaction request.
npx skill4agent add jtsang4/efficient-coding dev-browsergetAISnapshot()selectSnapshotRef()./skills/dev-browser/server.sh &--headless--stealth--no-stealth--driver [playwright|patchright|auto]autopatchrightplaywright--no-flaresolverr--stop--helpReady# Disable stealth entirely
./skills/dev-browser/server.sh --no-stealth &
# Disable FlareSolverr auto-start
./skills/dev-browser/server.sh --no-flaresolverr &
# Use plain Playwright without anti-detection
./skills/dev-browser/server.sh --no-stealth --driver playwright &# Default: Full anti-detection (stealth + patchright + flaresolverr)
./skills/dev-browser/server.sh &
# Headless mode with full anti-detection
./skills/dev-browser/server.sh --headless &
# Force specific driver
./skills/dev-browser/server.sh --driver patchright &
# Minimal setup without any anti-detection
./skills/dev-browser/server.sh --no-stealth --no-flaresolverr &cd skills/dev-browser && npm i && npm run start-extension &Waiting for extension to connect...Extension connectedclient.page("name")Run all scripts fromdirectory. Theskills/dev-browser/import alias requires this directory's config.@/
cd skills/dev-browser && npx tsx <<'EOF'
import { connect, waitForPageLoad } from "@/client.js";
const client = await connect();
// Create page with custom viewport size (optional)
const page = await client.page("example", { viewport: { width: 1920, height: 1080 } });
await page.goto("https://example.com");
await waitForPageLoad(page);
console.log({ title: await page.title(), url: page.url() });
await client.disconnect();
EOFtmp/"checkout""login""main"await client.disconnect()page.evaluate()page.evaluate()// ✅ Correct: plain JavaScript
const text = await page.evaluate(() => {
return document.body.innerText;
});
// ❌ Wrong: TypeScript syntax will fail at runtime
const text = await page.evaluate(() => {
const el: HTMLElement = document.body; // Type annotation breaks in browser!
return el.innerText;
});const client = await connect();
// Get or create named page (viewport only applies to new pages)
const page = await client.page("name");
const pageWithSize = await client.page("name", { viewport: { width: 1920, height: 1080 } });
const pages = await client.list(); // List all page names
await client.close("name"); // Close a page
await client.disconnect(); // Disconnect (pages persist)
// ARIA Snapshot methods
const snapshot = await client.getAISnapshot("name"); // Get accessibility tree
const element = await client.selectSnapshotRef("name", "e5"); // Get element by refpageimport { waitForPageLoad } from "@/client.js";
await waitForPageLoad(page); // After navigation
await page.waitForSelector(".results"); // For specific elements
await page.waitForURL("**/success"); // For specific URLawait page.screenshot({ path: "tmp/screenshot.png" });
await page.screenshot({ path: "tmp/full.png", fullPage: true });getAISnapshot()- banner:
- link "Hacker News" [ref=e1]
- navigation:
- link "new" [ref=e2]
- main:
- list:
- listitem:
- link "Article Title" [ref=e8]
- link "328 comments" [ref=e9]
- contentinfo:
- textbox [ref=e10]
- /placeholder: "Search"[ref=eN][checked][disabled][expanded][level=N]/url:/placeholder:const snapshot = await client.getAISnapshot("hackernews");
console.log(snapshot); // Find the ref you need
const element = await client.selectSnapshotRef("hackernews", "e2");
await element.click();cd skills/dev-browser && npx tsx <<'EOF'
import { connect } from "@/client.js";
const client = await connect();
const page = await client.page("hackernews");
await page.screenshot({ path: "tmp/debug.png" });
console.log({
url: page.url(),
title: await page.title(),
bodyText: await page.textContent("body").then((t) => t?.slice(0, 200)),
});
await client.disconnect();
EOF| Feature | Auto-Enable | Requirements | Usage |
|---|---|---|---|
| Patchright | ✅ Yes (Default) | npm | Auto-installed and used for stealth automation |
| Stealth Mode | ✅ Yes (Default) | None | Enabled by default with args + patches |
| FlareSolverr | ✅ Yes (Default) | Docker | Auto-started if Docker is available |
./server.sh--enable-automationnavigator.webdriver--no-flaresolverrdev-browser-flaresolverrbypassCloudflare()# Stop all services (server, browser, FlareSolverr Docker)
./skills/dev-browser/server.sh --stop# Disable all anti-detection features
./skills/dev-browser/server.sh --no-stealth --no-flaresolverr --driver playwright./server.shcd skills/dev-browser && npm install patchright# FlareSolverr requires Docker. If Docker is not running, the skill will skip it with a warning.
docker info./skills/dev-browser/server.sh --no-stealth --no-flaresolverr --driver playwrightimport { connect, applyStealthMode } from "@/client.js";
const client = await connect();
const page = await client.page("example");
// Apply stealth patches before navigating
await applyStealthMode(page);
await page.goto("https://example.com");
console.log("Page title:", await page.title());
await client.disconnect();import { connect, bypassCloudflare } from "@/client.js";
const client = await connect();
const page = await client.page("protected");
// Use FlareSolverr to solve the challenge and inject cookies
await bypassCloudflare(page, "https://protected-site.com", {
maxTimeout: 60000,
});
// Page now has valid Cloudflare session
console.log("After bypass:", await page.title());
await client.disconnect();bypassCloudflareimport { launchBrowser, USER_AGENTS } from "@/client.js";
const context = await launchBrowser({
usePatchright: true,
stealth: true,
headless: false,
userAgent: USER_AGENTS.chrome.windows,
viewport: { width: 1920, height: 1080 },
});
const page = await context.newPage();
await page.goto("https://example.com");import { FlareSolverrClient } from "@/client.js";
const flaresolverr = new FlareSolverrClient({
baseUrl: "http://localhost:8191",
defaultTimeout: 60000,
});
// Check health
await flaresolverr.health();
// Create a session for persistence
const sessionId = await flaresolverr.createSession();
// Solve URLs within the same session (cookies persist)
const result1 = await flaresolverr.solveUrl("https://site.com/page1", { sessionId });
const result2 = await flaresolverr.solveUrl("https://site.com/page2", { sessionId });
// Clean up
await flaresolverr.destroySession(sessionId);