silentbrowse
v0.2.0
Published
Stealth browser for AI agents — 95% token reduction + bot detection evasion
Downloads
325
Maintainers
Readme
SilentBrowse
Stealth browser for AI agents. 95% fewer tokens. Zero detection.
npm install silentbrowseThe Problem
AI agents waste 50,000+ tokens every time they look at a web page. They send the entire DOM to the LLM, even though only a few interactive elements matter.
I ran 41 AI agents on cron jobs. My GPT Pro $200 plan ran out in 3 days.
The Solution
SilentBrowse gives agents an accessibility-tree-based view of any web page in 200-300 tokens instead of 50,000.
Wikipedia full DOM: 23,174 tokens
SilentBrowse terse: 233 tokens (99.0% reduction)It also passes bot detection. 20/20 on bot.sannysoft.com. Bypasses Cloudflare challenges.
Quick Start
import { launch, snapshot, click, navigate } from 'silentbrowse'
const browser = await launch({ profile: 'my-agent' })
const page = browser.pages()[0]
// Navigate
await navigate(page, 'https://news.ycombinator.com')
// Snapshot — 200 tokens instead of 10,000
const snap = await snapshot(page, {
interactive: true,
format: 'terse',
maxNodes: 30,
})
console.log(snap.content)
// @e1 [link] "Hacker News"
// @e2 [link] "new"
// @e3 [link] "Show HN: Something cool"
// @e4 [link] "142 comments"
// ...
// Click by ref
await click(page, '@e3')
await browser.close()Snapshot Formats
Terse (recommended for AI agents)
Minimal tokens. One line per element.
const snap = await snapshot(page, {
interactive: true,
format: 'terse',
maxNodes: 30,
maxTextLength: 60,
})
// snap.content = '@e1 [link] "Home"\n@e2 [button] "Sign in"\n...'
// snap.tokens = 200JSON (for programmatic use)
const snap = await snapshot(page, {
interactive: true,
compact: true,
})
// snap.content = [{ role: 'link', text: 'Home', ref: '@e1' }, ...]Tree (hierarchical structure)
const snap = await snapshot(page, {
compact: false,
})
// snap.content = { role: 'document', children: [{ role: 'navigation', children: [...] }] }Stealth
SilentBrowse applies 9 CDP-level patches automatically:
navigator.webdriverremovalnavigator.pluginsspoofing (5 fake plugins)window.chrome.runtimenormalizationnavigator.deviceMemorypatch (headless fix)navigator.hardwareConcurrencyminimum 8- WebGL vendor/renderer normalization
- Permissions API patch
- Real Chrome User-Agent (no "HeadlessChrome")
--disable-blink-features=AutomationControlled
Test Results
| Test | Result | |------|--------| | bot.sannysoft.com | 20/20 passed | | nowsecure.nl (Cloudflare) | Bypassed |
Token Benchmarks
| Site | Full DOM | Terse (30 nodes) | Reduction | |------|----------|-----------------|-----------| | Hacker News | 9,684 | 202 | 97.9% | | Reddit | 14,688 | 291 | 98.0% | | X (Twitter) | 1,031 | 173 | 83.2% | | Google Search | 4,011 | 208 | 94.8% | | Wikipedia | 23,174 | 233 | 99.0% |
CLI
# Start browser
silentbrowse launch --profile default
# In another terminal
silentbrowse open https://example.com
silentbrowse snap -i # interactive elements
silentbrowse click @e1
silentbrowse type @e3 "Hello"
silentbrowse scroll down
silentbrowse closePlugin Interface (for agent frameworks)
Drop-in replacement for browser plugins:
import { SilentBrowsePlugin } from 'silentbrowse'
const plugin = new SilentBrowsePlugin('my-profile')
await plugin.init()
await plugin.handle({ tool: 'browser.navigate', parameters: { url: 'https://x.com' } })
const snap = await plugin.handle({ tool: 'browser.snapshot', parameters: { format: 'terse' } })
await plugin.handle({ tool: 'browser.click', parameters: { ref: '@e1' } })
// Track token usage
console.log(plugin.getTokenUsage())
// { total: 215, calls: 3, breakdown: { 'browser.snapshot': 200, ... } }API Reference
launch(options?)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| profile | string | 'default' | Profile directory for cookies/sessions |
| proxy | string | - | Proxy URL |
| headless | boolean | 'new' | 'new' | 'new' = Chrome new headless, false = headful |
| executablePath | string | auto-detect | Chrome path |
| cdpPort | number | - | CDP debugging port |
snapshot(page, options?)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| interactive | boolean | false | Only interactive elements |
| format | 'json' | 'terse' | 'json' | Output format |
| compact | boolean | true | Flat array vs tree |
| maxNodes | number | unlimited | Limit output nodes |
| maxTextLength | number | 80 | Truncate text |
| selector | string | - | CSS selector to scope |
| maxDepth | number | unlimited | Tree depth limit |
Actions
await click(page, '@e1') // or CSS selector
await type(page, '@e3', 'text')
await select(page, '@e5', 'value')
await scroll(page, 'down')
await navigate(page, 'https://...')Requirements
- Node.js >= 18
- Chrome/Chromium installed
- playwright-core (bundled)
License
MIT
