Install
$ agentstack add skill-jtsang4-efficient-coding-dev-browser ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Dev Browser Skill
Browser automation that maintains page state across script executions. Write small, focused scripts to accomplish tasks incrementally. Once you've proven out part of a workflow and there is repeated work to be done, you can write a script to do the repeated work in a single execution.
Choosing Your Approach
- Local/source-available sites: Read the source code first to write selectors directly
- Unknown page layouts: Use
getAISnapshot()to discover elements andselectSnapshotRef()to interact with them - Visual feedback: Take screenshots to see what the user sees
Setup
Two modes available. Ask the user if unclear which to use.
Standalone Mode (Default)
Launches a new Chromium browser for fresh automation sessions.
./skills/dev-browser/server.sh &
Options:
--headless- Run in headless mode (no visible browser window)--stealth- Enable anti-detection mode (default: ON)--no-stealth- Disable stealth mode (use plain browser)--driver [playwright|patchright|auto]- Choose browser driver (default: auto)auto- Auto-install and use patchright, fallback to playwright with patchespatchright- Force use of patchrightplaywright- Use standard playwright--no-flaresolverr- Disable FlareSolverr auto-start (default: enabled if Docker available)--stop- Stop all dev-browser services and cleanup--help- Show help message
Wait for the Ready message before running scripts.
By default, the server automatically:
- Installs Patchright (if not present) for stealth automation
- Enables stealth mode with browser args and runtime patches
- Starts FlareSolverr Docker container for Cloudflare bypass (requires Docker)
To disable automatic features:
# 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 &
Examples:
# 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 &
Extension Mode
Connects to user's existing Chrome browser. Use this when:
- The user is already logged into sites and wants you to do things behind an authed experience that isn't local dev.
- The user asks you to use the extension
Important: The core flow is still the same. You create named pages inside of their browser.
Start the relay server:
cd skills/dev-browser && npm i && npm run start-extension &
Wait for Waiting for extension to connect... followed by Extension connected in the console. To know that a client has connected and the browser is ready to be controlled. Workflow:
- Scripts call
client.page("name")just like the normal mode to create new pages / connect to existing ones. - Automation runs on the user's actual browser session
If the extension hasn't connected yet, tell the user to launch and activate it. Download link: https://github.com/SawyerHood/dev-browser/releases
Writing Scripts
> Run all scripts from skills/dev-browser/ directory. The @/ import alias requires this directory's config.
Execute scripts inline using heredocs:
cd skills/dev-browser && npx tsx {
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;
});
Scraping Data
For scraping large datasets, intercept and replay network requests rather than scrolling the DOM. See [references/scraping.md](references/scraping.md) for the complete guide covering request capture, schema discovery, and paginated API replay.
Client API
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 ref
The page object is a standard Playwright Page.
Waiting
import { waitForPageLoad } from "@/client.js";
await waitForPageLoad(page); // After navigation
await page.waitForSelector(".results"); // For specific elements
await page.waitForURL("**/success"); // For specific URL
Inspecting Page State
Screenshots
await page.screenshot({ path: "tmp/screenshot.png" });
await page.screenshot({ path: "tmp/full.png", fullPage: true });
ARIA Snapshot (Element Discovery)
Use getAISnapshot() to discover page elements. Returns YAML-formatted accessibility tree:
- 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"
Interpreting refs:
[ref=eN]- Element reference for interaction (visible, clickable elements only)[checked],[disabled],[expanded]- Element states[level=N]- Heading level/url:,/placeholder:- Element properties
Interacting with refs:
const snapshot = await client.getAISnapshot("hackernews");
console.log(snapshot); // Find the ref you need
const element = await client.selectSnapshotRef("hackernews", "e2");
await element.click();
Error Recovery
Page state persists after failures. Debug with:
cd skills/dev-browser && npx tsx t?.slice(0, 200)),
});
await client.disconnect();
EOF
Anti-Detection Features (Auto-Enabled by Default)
By default, the skill automatically enables all anti-detection features for protected sites with bot detection (Cloudflare, DataDome, etc.):
| 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 |
How It Works
Patchright Auto-Install: When you start the server with defaults (./server.sh):
- Checks if patchright is installed
- If not found → automatically installs it via npm
- Uses Patchright with built-in stealth capabilities
- Falls back to Playwright with manual patches if install fails
Stealth Mode (Default ON): Stealth mode is now enabled by default and includes:
- Modified browser launch args (removes
--enable-automation, etc.) - If using Playwright: runtime patches to hide
navigator.webdriverand other indicators - If using Patchright: native stealth features handle evasion automatically
- All new pages get stealth patches applied
FlareSolverr (Auto-Start): FlareSolverr Docker container is automatically started when:
- Docker is installed and running
--no-flaresolverrflag was NOT used- A container named
dev-browser-flaresolverrdoesn't already exist
The container runs on port 8191 and is ready for bypassCloudflare() calls.
Stopping Services
To stop all dev-browser services gracefully:
# Stop all services (server, browser, FlareSolverr Docker)
./skills/dev-browser/server.sh --stop
This will:
- Stop the dev-browser HTTP server (port 9222)
- Close all browser contexts and pages
- Stop the FlareSolverr Docker container
- Clean up any stale Chrome processes
Disabling Anti-Detection
If you need a standard browser without anti-detection:
# Disable all anti-detection features
./skills/dev-browser/server.sh --no-stealth --no-flaresolverr --driver playwright
Prerequisites
All anti-detection features are auto-enabled by default when you run ./server.sh. No manual setup required.
Optional: Pre-install Patchright (to avoid install delay on first run):
cd skills/dev-browser && npm install patchright
Optional: Ensure Docker is running (for FlareSolverr auto-start):
# FlareSolverr requires Docker. If Docker is not running, the skill will skip it with a warning.
docker info
To completely disable anti-detection and use a standard browser:
./skills/dev-browser/server.sh --no-stealth --no-flaresolverr --driver playwright
Stealth Mode
Apply runtime anti-detection patches to a page to hide automation flags:
import { 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();
Cloudflare Bypass
For sites protected by Cloudflare challenge pages:
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();
The bypassCloudflare function:
- Sends the URL to FlareSolverr to solve the challenge
- Receives authentication cookies
- Injects cookies into the Playwright context
- Navigates to the target URL
Using Patchright Directly
To launch a browser with Patchright directly (for full stealth capabilities):
import { 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");
Advanced: FlareSolverr Client
For more control over challenge solving, use the FlareSolverr client directly:
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);
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: jtsang4
- Source: jtsang4/efficient-coding
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.