ego-browser
AI agents के लिए browser automation runtime, जो ego lite के असली Chromium session को चलाता है।
ego-browser, ego lite द्वारा AI agents के लिए दिया गया browser automation runtime है। यह Chrome DevTools Protocol के ज़रिए ego lite के असली Chromium session से जुड़ता है, और entry point के तौर पर Node.js heredoc script लेता है: agent एक ही stdin delivery में पूरा JS flow लिखता है, सभी helpers पहले से script के scope में inject रहते हैं, और browser का state Space में बना रहता है।
ego-browser का मक़सद यह नहीं है कि कोई इंसान browser को हाथ से चलाए, और यह Playwright या Puppeteer का विकल्प भी नहीं है। इसका पाठक LLM agent है।
किसके लिए है
- ऐसे AI coding agents के लिए जिन्हें browser चलाना है: Claude Code, Codex, Cursor, custom SDK agents।
- वर्टिकल agents बनाने वाली टीमें: Lark, Google Docs, Salesforce जैसे back-offices को automate करने वाले।
- दोहराव वाले web flows: login, form filling, export, search, table reading।
- वे लोग जिन्होंने कभी पूरा DOM या पूरा HTML page LLM में डाला और token limit से टकराए।
Install करें
ego lite के साथ ही आता है—देखें त्वरित आरंभ। Install के बाद आप किसी भी directory में ego-browser कमांड चला सकते हैं।
Skill अकेले भी install हो सकती है:
npx skills add github:CitroLabs/ego-lite/skills/ego-browser
मुख्य loop
Agent द्वारा page चलाने का सामान्य ढाँचा—सब कुछ एक ही heredoc में:
ego-browser nodejs <<'EOF'
const task = await useOrCreateTaskSpace('search github issues')
await openOrReuseTab('https://github.com/issues', { wait: true, timeout: 20 })
cliLog(await snapshotText())
EOF
- Task Space reuse करें या नया बनाएँ (हर heredoc में ज़रूरी—देखें Space)।
- लक्ष्य page खोलें।
- Snapshot पढ़ें (
snapshotText()) और[ref=N, loc=..., url=...]वाला semantic tree पाएँ। @Nref या CSS selector से page पर action करें।cliLog(...)से अंतिम परिणाम print करें।
Heredoc के अंदर आप Node.js process में हैं;
js(...)के अंदर ही browser page context में हैं। इन दोनों को न मिलाएँ।
Helper reference
सभी helpers script के scope में camelCase नाम से उपलब्ध हैं, import की ज़रूरत नहीं।
Task Space
await listTaskSpaces()
const task = await useOrCreateTaskSpace('describe task') // reuse करें या बनाएँ
await completeTaskSpace(task.name) // पूरा हुआ, tab रहने दें
await closeTaskSpace(task.name) // अब ज़रूरत नहीं, space बंद करें
name को 3–6 शब्दों में, सामान्य भाषा में task describe करें। Placeholder न इस्तेमाल करें।
Navigation और state
await listTabs()
await openOrReuseTab(url, { wait: true, timeout: 20 })
await gotoAndWait(url, { timeout: 20, settle: 1 })
await newTab(url)
await switchTab(tabId)
await currentTab()
await pageInfo()
await ensureRealTab() // नया task space तब बना हो जब tab अभी न हो
Observation
await snapshotText() // पूरी page का semantic snapshot (default)
await snapshotText({ scope: 'only_within_viewport' })
await captureScreenshot('result.png')
await drainEvents() // navigation / network events queue clear करें
Mouse और scroll
click / doubleClick / hover / dragMouse एक ही target format (CSS pixels) लेते हैं:
'string': CSS selector या@ref। Element के बीच में click करता है।[x, y]या{x, y}: viewport coordinates।{selector, x, y}: element के top-left से relative offset।options.label: 3–6 शब्दों का description; देने पर visual highlight animation चलती है।
await click('@21', { label: 'login state देखें' })
await click('button.primary', { label: 'Submit button पर click करें' })
await click([420, 260])
await hover('@5', { label: 'menu पर hover करें' })
await dragMouse([from, to], { label: 'card को drag करें' })
await scrollBy(900)
await scroll({ dy: 900 })
await scrollToBottomUntil(
async () => await js(String.raw`document.querySelectorAll('article').length`) >= 20,
{ step: 900, wait: 1, maxSteps: 20 },
)
Keyboard और input
await typeText('hello world')
await fillInput('@2', 'user@test.com')
await pressKey('Enter')
await dispatchKey({ ... })
Files और network
await uploadFile('input[type="file"]', '/absolute/path/to/file.pdf')
await httpGet('https://api.example.com/data') // page context से किया गया GET
Wait करना
await wait(1) // seconds
await waitForLoad()
await waitForElement('@1')
await waitForNetworkIdle()
wait()औरtimeoutseconds में हैं। केवलMsसे खत्म होने वाले parameters milliseconds में हैं।
Browser में execute
js(source) असल में Runtime.evaluate है और string लेता है। Puppeteer की तरह function + arguments मत भेजें—warning आती है, सब .toString() में लिपट जाता है, closure variables और arguments channel दोनों खो जाते हैं।
Multi-step logic को एक ही IIFE में लपेटें और एक बार return करें:
const data = await js(String.raw`(() => {
const items = [...document.querySelectorAll('article')]
return items.map(el => ({
text: el.innerText,
links: [...el.querySelectorAll('a')].map(a => a.href),
}))
})()`)
await elementEval('@1', el => el.getBoundingClientRect())
await cdp('Page.captureScreenshot', { format: 'png' })
Output और self-discovery
cliLog(value) // heredoc में output का एकमात्र चैनल
cliLog(help('click')) // किसी helper का इस्तेमाल देखें
अनुशंसित workflow
पहले snapshotText + ref / loc से शुरू करें—यह semantic structure बनाए रखता है और coordinates की नज़ाक़त से बचाता है:
- Task Space reuse करें या नया बनाएँ।
- Page खोलें या switch करें (
openOrReuseTab/gotoAndWait)। snapshotText()से[ref=N, loc=..., url=...]tree लें। Refs अपने आप refMap में register हो जाते हैं।@Nके साथclick/fillInput/elementEvaluse करें, याjs(...)के अंदर एक बार में DOM extract कर लें।cliLog(...)से अंतिम result print करें।
मिलाने योग्य अन्य रास्ते:
captureScreenshot+click([x, y]): visual layouts, canvas UIs, virtual lists, अधूरी accessibility वाले pages।js/elementEval/cdp: सीधे DOM extract करना, browser state देखना, या जब standard helper काफ़ी न हो।
Navigation, observation, scrolling, extraction, filtering, aggregation और output—सब एक ही
ego-browser nodejsheredoc में रखें। उसी data के लिए दूसरा localnodescript न चलाएँ।
Ref का दायरा
@N केवल नवीनतम snapshotText के refMap के लिए valid है। हर snapshotText() refMap नए सिरे से बनाता है। Ref numbers element के CDP backendNodeId से आते हैं, इसलिए वही element अक्सर कई snapshots में एक ही number रखता है—लेकिन @N operate करने के लिए N का नवीनतम snapshot output में होना ज़रूरी है।
Unknown ref के सामान्य कारण:
- Element viewport से बाहर चला गया।
- DOM फिर से render हुआ।
- पिछली बार
scope: 'only_within_viewport'था और इस बार वह element cover नहीं हुआ।
कई rounds तक एक ही element को स्थिर रूप से refer करना हो, तो snapshot output के loc=... को stable selector की तरह उपयोग करें या सीधा CSS selector लिखें। यही Experience संचय का आधार भी है (देखें Skills)।
Skill workspace
ego-browser खुद कोई परिवर्तनशील agent experience नहीं रखता। Default में यह repo के skill bundle से helper extensions और सीखे हुए site experience load करता है:
../../skills/ego-browser
Environment variable से override करें:
EGO_BROWSER_AGENT_WORKSPACE=/path/to/ego-browser ego-browser nodejs <<'EOF'
cliLog(await siteSkills())
EOF
learnings/ के अंदर का site experience हमेशा सक्रिय रहता है; हर helper call पर पढ़ा जाता है। Experience के लिखने और खोजने का तरीक़ा Skills में है।
सीखी हुई experience verify करें:
npm run validate:learnings
Directory structure
package/ego-browser/
├── src/ # browser-runtime / helpers / run.js
│ ├── browser-runtime.js # browser side का ego runtime bridge
│ ├── helpers.js # agent script को दिखाई जाने वाली helpers
│ ├── run.js # CLI entry (stdin execute करता है)
│ └── learning/ # experience index, domain validation, format validation
├── artifacts/ego-browser/ # build artifacts; npm bin यहाँ इशारा करता है
└── test/ # unit tests
skills/ego-browser/
├── SKILL.md / SKILL.zh.md # agent का entry point
└── learnings/ # per-site experience directory
ध्यान देने योग्य बातें
snapshotText()का defaultscope: 'full_page'है (पूरा page)। केवल visible area चाहिए हो तभी'only_within_viewport'पास करें।js()expression का सीधा result लौटाता है; इस पर फिर सेJSON.parse(...)न करें।js()की template string में regex लिखते समय backslash double करें (\\d,\\s), याString.rawuse करें।- Top-level
returnअपने आप IIFE में लिप्त हो जाता है। Nested callback में लिखाreturnभी यही trigger कर सकता है, इसलिए complex expressions शुरू से ही(() => { ... })()के रूप में लिखें। - जब user ने स्पष्ट रूप से ego-browser माँगा है, runtime पहले से तैयार है। पहली बार run में error आने तक
which ego-browser/node -v/ help dump न चलाएँ।