ego-browser
Die Browser-Automatisierungslaufzeit, die KI-Agenten verwenden, um die echte Chromium-Sitzung von ego (lite) zu steuern.
ego-browser ist die Browser-Automatisierungslaufzeit, die ego (lite) für KI-Agenten ausliefert. Es kommuniziert über das Chrome DevTools Protocol mit der echten Chromium-Sitzung innerhalb von ego (lite) und verwendet ein Node.js-Heredoc-Skript als Einstiegspunkt: Der KI-Agent schreibt den gesamten JS-Ablauf in einer einzigen stdin-Übergabe, alle Helper sind bereits in den Skript-Scope eingefügt und der Browserstatus bleibt innerhalb eines Space bestehen.
ego-browser ist nicht für die manuelle Browsersteuerung durch Menschen gedacht und ersetzt weder Playwright noch Puppeteer. Die Zielgruppe sind LLM-Agenten.
Für wen es ist
- KI-Coding-Agenten, die einen Browser steuern müssen: Claude Code, Codex, Cursor und benutzerdefinierte SDK-Agenten.
- Teams, die vertikale KI-Agenten entwickeln und Lark, Google Docs, Salesforce oder ähnliche Backoffices automatisieren.
- Wiederholte feste Webabläufe: Anmelden, Ausfüllen, Exportieren, Suchen, Tabellen lesen.
- Jeder, der versucht hat, ein vollständiges DOM oder eine HTML-Seite in ein LLM zu stopfen und dabei an die Token-Wall stößt.
Installieren
Im Lieferumfang ist ego (lite) enthalten – siehe Schnellstart. Führen Sie nach der Installation ego-browser in einem beliebigen Verzeichnis aus.
Sie können den Skill auch eigenständig installieren:
npx skills add github:CitroLabs/ego-lite/skills/ego-browser
Kernschleife
Der typische Rhythmus für KI-Agenten, die eine Seite steuern – alles in einem einzigen Heredoc:
ego-browser nodejs <<'EOF'
const task = await useOrCreateTaskSpace('GitHub-Issues durchsuchen')
await openOrReuseTab('https://github.com/issues', { wait: true, timeout: 20 })
cliLog(await snapshotText())
EOF
- Einen Task Space wiederverwenden oder erstellen (deklarieren Sie ihn in jedem Heredoc – siehe Space).
- Öffnen Sie die Zielseite.
- Lesen Sie den Snapshot (
snapshotText()), um einen semantischen Baum mit[ref=N, loc=..., url=...]zu erhalten. - Handeln Sie auf der Seite durch
@Nref oder CSS-Selektor. - Drucken Sie das Endergebnis mit
cliLog(...)aus.
Im Heredoc befinden Sie sich in einem Node.js-Prozess; Innerhalb von
js(...)befinden Sie sich im Seitenkontext. Mischen Sie sie nicht.
Helfer-Referenz
Jeder Helfer ist im Gültigkeitsbereich des Skripts über seinen CamelCase-Namen verfügbar. Kein import erforderlich.
Task Space
await listTaskSpaces()
const task = await useOrCreateTaskSpace('Aufgabe beschreiben') // wiederverwenden oder erstellen
await completeTaskSpace(task.name) // erledigt; Tab geöffnet lassen
await closeTaskSpace(task.name) // Space schließen
name sollte eine aus 3 bis 6 Wörtern bestehende Beschreibung der Aufgabe in natürlicher Sprache sein. Verwenden Sie keine Platzhalter.
Navigation und Zustand
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() // ein neuer Task Space hat möglicherweise noch keinen Tab
Beobachtung
await snapshotText() // semantischer Snapshot der ganzen Seite (Standard)
await snapshotText({ scope: 'only_within_viewport' })
await captureScreenshot('result.png')
await drainEvents() // Warteschlange der Navigations- und Netzwerkereignisse leeren
Maus und Scrollen
click, doubleClick, hover und dragMouse akzeptieren dasselbe Zielformat (CSS-Pixel):
'string': CSS-Selektor oder@ref. Klickt auf die Elementmitte.[x, y]oder{x, y}: Ansichtsfensterkoordinaten.{selector, x, y}: relativer Versatz von der oberen linken Ecke des Elements.options.label: Beschreibung mit 3 bis 6 Wörtern. Wenn Sie sie übergeben, hebt die Aktion das Zielelement visuell hervor.
await click('@21', { label: 'Anmeldestatus prüfen' })
await click('button.primary', { label: 'Senden-Schaltfläche anklicken' })
await click([420, 260])
await hover('@5', { label: 'Menü durch Hover einblenden' })
await dragMouse([from, to], { label: 'Karte ziehen' })
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 },
)
Tastatur und Eingabe
await typeText('hello world')
await fillInput('@2', 'user@test.com')
await pressKey('Enter')
await dispatchKey({ ... })
Dateien und Netzwerk
await uploadFile('input[type="file"]', '/absolute/path/to/file.pdf')
await httpGet('https://api.example.com/data') // GET-Anfrage im Seitenkontext
Warten
await wait(1) // Sekunden
await waitForLoad()
await waitForElement('@1')
await waitForNetworkIdle()
wait()undtimeoutwerden in Sekunden angegeben. Nur Parameter, die mitMsenden, sind Millisekunden.
Browserausführung
js(source) ist Runtime.evaluate unter der Haube und nimmt einen String. Übergeben Sie ihm keine Funktion und Argumente wie Puppeteer – das erzeugt eine Warnung, wird in .toString() eingeschlossen und die Abschlussvariablen und der Argumentkanal verschwinden beide.
Für eine mehrstufige Logik packen Sie sie in ein IIFE ein, das einmal zurückgibt:
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' })
Ausgabe und Selbstfindung
cliLog(value) // einziger Ausgabekanal in einem Heredoc
cliLog(help('click')) // Verwendung eines Helpers nachschlagen
Empfohlener Arbeitsablauf
Beginnen Sie mit snapshotText plus ref / loc – dadurch bleibt die Semantik intakt und die Sprödigkeit der Koordinaten wird vermieden:
- Verwenden Sie den Task Space wieder oder erstellen Sie einen neuen.
- Öffnen oder wechseln Sie zur Seite (
openOrReuseTab/gotoAndWait). snapshotText(), um den[ref=N, loc=..., url=...]-Baum zu erhalten. Refs werden automatisch in der refMap registriert.- Reagieren Sie auf
@Nmitclick/fillInput/elementEvaloder führen Sie eine einmalige DOM-Extraktion innerhalb vonjs(...)durch. cliLog(...)das Endergebnis.
Weitere nützliche Wege zum Kombinieren:
captureScreenshot+click([x, y]): visuelle Layouts, Canvas-gesteuerte Benutzeroberflächen, virtuelle Listen, Seiten mit unvollständiger Barrierefreiheit.js/elementEval/cdp: DOM direkt extrahieren, Browserstatus überprüfen oder alles, was nicht sauber in einen Standardhelfer passt.
Bewahren Sie Navigation, Beobachtung, Scrollen, Extraktion, Filterung, Aggregation und Ausgabe in einem einzigen
ego-browser nodejs-Heredoc auf. Leiten Sie die Daten nicht über ein zweites lokalesnode-Skript weiter.
Ref-Bereich
@N ist nur für die refMap des aktuellsten snapshotText gültig. Jeder snapshotText() erstellt die refMap neu. Ref-Nummern stammen aus dem CDP backendNodeId des Elements, sodass dasselbe Element in der Regel über alle Snapshots hinweg dieselbe Nummer trägt – damit @N jedoch funktionsfähig ist, muss N in der neuesten Snapshot-Ausgabe erscheinen.
Häufige Ursachen für Unknown ref:
- Das Element wurde aus dem Ansichtsfenster gescrollt.
- Das DOM wurde neu gerendert.
- In einer vorherigen Runde wurde
scope: 'only_within_viewport'verwendet und in der nächsten Runde wurde das Element nicht abgedeckt.
Wenn Sie über mehrere Runden hinweg einen stabilen Verweis auf dasselbe Element benötigen, verwenden Sie den Selektor loc=... aus dem Snapshot oder schreiben Sie direkt einen CSS-Selektor. Dies ist auch die Grundlage für gesammelte Erfahrung – siehe Skills.
Arbeitsbereich des Skills
ego-browser verwaltet nicht selbstständig die veränderbare Experience eines KI-Agenten. Standardmäßig lädt es Helper-Erweiterungen und erlernte Website-Experience aus dem Skill-Bundle des Repositories:
../../skills/ego-browser
Überschreiben über env var:
EGO_BROWSER_AGENT_WORKSPACE=/path/to/ego-browser ego-browser nodejs <<'EOF'
cliLog(await siteSkills())
EOF
Die Website-Experience unter learnings/ ist immer aktiv; jeder Helper-Aufruf liest sie. Das Schreib- und Erkennungsmodell für Experience wird unter Skills beschrieben.
Gelernte Erfahrung validieren:
npm run validate:learnings
Verzeichnislayout
package/ego-browser/
├── src/ # browser-runtime / helpers / run.js
│ ├── browser-runtime.js # ego runtime bridge on the browser side
│ ├── helpers.js # helpers exposed to the agent script
│ ├── run.js # CLI entry point (executes stdin)
│ └── learning/ # experience index, domain check, format check
├── artifacts/ego-browser/ # build output; npm bin points here
└── test/ # unit tests
skills/ego-browser/
├── SKILL.md / SKILL.zh.md # entry point for the agent
└── learnings/ # site experience directory
Notizen
snapshotText()ist standardmäßigscope: 'full_page'. Übergeben Sie'only_within_viewport'nur, wenn Sie wirklich nur den sichtbaren Bereich benötigen.js()gibt das Auswertungsergebnis direkt zurück. Wenden Sie darauf nicht erneutJSON.parse(...)an.- Wenn Sie einen regulären Ausdruck in eine
js()-Vorlagenzeichenfolge schreiben, verdoppeln Sie die Backslashes (\\d,\\s) oder wechseln Sie zuString.raw. - Ein
returnder obersten Ebene wird automatisch in ein IIFE eingeschlossen. Einreturninnerhalb eines verschachtelten Rückrufs kann dies ebenfalls auslösen. Schreiben Sie daher komplexe Ausdrücke im Voraus als(() => { ... })(). - Wenn der Benutzer explizit nach ego-browser gefragt hat, ist die Laufzeit bereit. Führen Sie keinen Preflight mit
which ego-browser/node -v/ einem Hilfe-Dump durch – tun Sie dies nur, wenn bei der ersten Ausführung tatsächlich ein Fehler auftritt.