ego-browser
ego-browser stellt einen echten Chromium-Browser über eine CLI-zugängliche Node.js-Laufzeitumgebung bereit. Seine vorinstallierten Fassaden page, page.locator(...), browser und taskSpaces folgen Playwright-ähnlichen Namen und Aufrufstrukturen; taskSpaces, site, fetch und cdp bieten ego-browser-spezifische Fähigkeiten.
Informationen zu Einrichtung, Installation oder Verbindungsproblemen finden Sie in references/install.md.
Führen Sie Browserarbeit mit dem Bash-Tool als ego-browser nodejs <<'EOF' ... EOF aus. Platzieren Sie das JavaScript direkt im Heredoc; erstellen Sie keine .js-Datei, importieren Sie Playwright nicht, starten Sie keinen weiteren Browser und erfinden Sie keine Hilfsnamen.
Ein Heredoc ist nur der JavaScript-Container; die Bash-Invokation ist der Ausführungsdurchlauf. Standardmäßig wird eine Bash-Invokation für die gesamte Browseraufgabe verwendet. Jedes await ist eine interne Operation, keine Schrittgrenze. Kodieren Sie vor dem Start jede vorhersehbare Beobachtung, Aktion, Wartezeit, Extraktion, Verifizierung und begrenzte Alternative im Skript. Verwenden Sie Browserergebnisse sofort in JavaScript und passen Sie sich weiterhin innerhalb des Prozesses an, bis die Aufgabe abgeschlossen ist; beenden Sie das Skript nicht nur, um die Zwischenausgabe zu inspizieren oder die nächste Aktion zu planen. Starten Sie einen anderen Bash-Befehl nur für eine erforderliche Benutzer- oder externe Steuerung, eine visuelle Inspektion, die nicht innerhalb des Prozesses erfolgen kann, oder einen Fehler auf Prozessebene, von dem sich das Skript nicht erholen kann.
Wählen Sie den zustandsärmsten zuverlässigen Weg, bevor Sie Seitensteuerelemente inspizieren. Wenn die Aufgabe ein Ergebnis oder Einschränkungen, aber keine erforderliche Interaktion vorgibt, bevorzugen Sie einen bereits korrekten Zustand oder eine bekannte stabile URL oder Websiteroute, die diese direkt kodiert; verifizieren Sie den resultierenden Zielzustand, anstatt gleichwertige Filter, Sortierungen oder Navigation über die Benutzeroberfläche nachzuspielen. Verwenden Sie Seitensteuerelemente, wenn der Benutzer diese Interaktion angefordert hat, die Interaktion selbst getestet wird oder kein zuverlässiges Äquivalent bekannt ist. Erfinden Sie niemals eine fragile Route.
Behandeln Sie eine bereits erfüllte Nachbedingung als abgeschlossene Arbeit. Bevor Sie ein Steuerelement manipulieren, dessen erforderlicher Wert möglicherweise bereits sichtbar ist, führen Sie nur die kleinste Leseoperation durch, die erforderlich ist, um diesen Zustand zu bestimmen. Wenn er übereinstimmt, öffnen Sie nicht den Editor, spielen Sie die Interaktion nicht nach und lesen Sie ihn nicht erneut; fahren Sie direkt mit den verbleibenden unerfüllten Ergebnissen fort. Wörter wie „setzen“, „auswählen“ oder „sicherstellen“ beschreiben den erforderlichen Endzustand, es sei denn, der Benutzer fordert explizit den Übergang oder die Interaktion selbst wird getestet.
Trennen Sie Browserarbeit vom Terminalabschluss. useOrCreate beginnt oder setzt ein Benutzerziel fort; behalten Sie die zurückgegebene task.id und verwenden Sie dieselbe ID oder denselben genauen Namen, bis dieses Ziel beendet ist. Halten Sie jede vorhersehbare Beobachtung, Aktion, Wartezeit, Extraktion und Verifizierung kompakt, aber rufen Sie taskSpaces.complete(...) nicht in einer Bash-Invokation auf, die noch feststellt, ob das Ziel erfüllt ist. Schließen Sie zuerst die Browserarbeit ab und geben Sie Nachweise aus, dass jedes angeforderte Ergebnis sowie alle erforderlichen Umfangs- oder Abdeckungsgrenzen nachgewiesen wurden. Erst nachdem Sie diese vorherige Ausgabe überprüft haben, darf eine dedizierte letzte Bash-Invokation den ursprünglichen Aufgabenbereich abschließen; sie führt keine page- oder browser-Arbeit aus. Dieser einzelne Lebenszyklus-Commit ist die Ausnahme von der Standardeinstellung mit einer Invokation, kein Browser-Schritt oder -Durchlauf. Nicht leere oder plausible Teilergebnisse, eine blockierte Seite, erschöpfte Wiederholungen oder ein Fallback-Versuch sind keine Abschlussnachweise. keep: true bewahrt ein terminales Ergebnis für den Benutzer; es hält keine unvollendete Aufgabe am Leben.
Frieren Sie das Zeitfenster für aktuelle oder relative Datumsarbeit ein. Legen Sie „heute/aktuell/neueste“ einmal aus der Benutzer-/Aufgabenumgebung oder dem explizit verifizierten aktuellen Seitenzustand fest, bevor Sie Datensätze sammeln. Behandeln Sie Inhaltszeitstempel als Daten, nicht als Uhr. Ältere Datensätze, die durch Scrollen, Virtualisierung, Neuladen, Cache oder eine geänderte Ergebnismenge aufgedeckt werden, dürfen diesen Anker nicht ersetzen. Fahren Sie mit der Bewertung von Datensätzen anhand des ursprünglichen Fensters fort; führen Sie die Aufgabe nicht stillschweigend auf das neueste beobachtete Inhaltsdatum zurück.
Kurzstart
Jedes Beispiel in dieser Fähigkeit ist bewusst zusammengesetzt. Passen Sie URL, Selektoren und Daten an die Benutzeraufgabe an.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('Beispielseite inspizieren')
await browser.openOrReuseTab('https://example.com', { wait: true, timeout: 20000 })
const heading = await page.getByRole('heading').first().innerText()
const info = await page.info()
if (!heading || !('url' in info)) throw new Error('Beispielseite war nicht bereit')
const result = { taskSpaceId: task.id, heading, url: info.url }
console.log(JSON.stringify(result, null, 2))
EOF
Behalten Sie alle vorhersehbaren Arbeiten im Skript, bis die Aufgabe abgeschlossen ist. Geben Sie Endergebnisse mit console.log(...) aus.
Zusammengesetzte Muster
Extrahieren, auswählen, navigieren, verifizieren
Extrahieren Sie auf einer Listen- oder Suchseite strukturierte Kandidaten, bevor Sie eine Auswahl treffen. Behalten Sie Extraktion, Auswahl, Aktion, Wartezeit, Verifizierung und Bereinigung in einer Bash-Invokation.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('Suchergebnisse vergleichen')
await browser.openOrReuseTab('https://example.com/search?q=browser+automation', {
wait: true,
timeout: 20000,
})
const cards = page.locator('article')
const items = await cards.evaluateAll((nodes) =>
nodes.map((node) => ({
title: node.querySelector('h2')?.textContent?.trim(),
href: node.querySelector('a')?.href,
})),
)
const chosenIndex = items.findIndex((item) => item.title && item.href)
if (chosenIndex < 0) throw new Error('Kein brauchbares Ergebnis: ' + JSON.stringify(items))
const before = await page.url()
const navigation = page.waitForURL((url) => url.href !== before, { timeout: 15000 })
await cards.nth(chosenIndex).getByRole('link').first().click()
if (!(await navigation)) throw new Error('Ausgewähltes Ergebnis hat nicht navigiert')
const info = await page.info()
if (!('url' in info) || info.url === before) throw new Error('Navigation wurde nicht verifiziert')
const result = { chosen: items[chosenIndex], opened: info.url }
console.log(JSON.stringify(result, null, 2))
EOF
Ausfüllen, auslösen, warten, zurücklesen
Registrieren Sie Request-/Response-Wartezeiten vor der Aktion, die sie auslöst, und verifizieren Sie dann den resultierenden Seitenzustand, anstatt den Klick als Erfolg zu behandeln.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('Bestellungen durchsuchen')
await browser.openOrReuseTab('https://example.com/orders', { wait: true, timeout: 20000 })
const responsePromise = page.waitForResponse(
(response) => response.url().includes('/api/orders') && response.ok(),
{ timeout: 15000 },
)
await page.getByLabel('Bestellungen durchsuchen').fill('ausstehend')
await page.getByRole('button', { name: /suchen/i }).click()
const response = await responsePromise
const rows = await page.locator('table tbody tr').allInnerTexts()
if (!rows.length) throw new Error('Suche abgeschlossen, aber keine sichtbaren Zeilen zurückgegeben')
const result = { status: response.status(), rows }
console.log(JSON.stringify(result, null, 2))
EOF
Tab-Handles aktualisieren, wechseln, inspizieren
Behandeln Sie targetId als kurzlebiges Handle. Entdecken, validieren und verwenden Sie es in derselben Bash-Invokation.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('Generierten Bericht überprüfen')
const tabs = await browser.listTabs({ includeChrome: false })
const reportTab = tabs.find((tab) => tab.url.includes('/reports/'))
if (!reportTab?.targetId) throw new Error('Bericht-Tab nicht gefunden: ' + JSON.stringify(tabs))
await browser.switchTab(reportTab.targetId)
const info = await page.info()
const heading = await page.getByRole('heading').first().innerText()
if (!('url' in info) || !info.url.includes('/reports/')) throw new Error('Falscher Tab ausgewählt')
const result = { taskSpaceId: task.id, heading, url: info.url }
console.log(JSON.stringify(result, null, 2))
EOF
Laufzeitübersicht
page: Navigation und Zustand (goto,reload,url,title,info), semantische Locators, Wartezeiten,snapshot,screenshot,screencast,evaluate,keyboard,mouse, Downloads und Ereignisverarbeitung.page.locator(selector): Verkettung und Filterung;first/nth/last; Klick, Hover,dragTo,scrollIntoViewIfNeeded, Formular-, Tastatur-, Upload-, Zustandslese-, Sammlungs-, Element-Evaluierungs-, Screenshot- und Wartemethoden.browser:listTabs,currentTab,switchTab,openOrReuseTab,closeTab,ensureRealTab,iframeTarget.taskSpaces:list,switch,new,useOrCreate,claim,complete,handOff,takeOver,waitForAgentControl.fetch.serverführt Node-seitige Requests aus;fetch.browserführt Requests im Ursprung der aktuellen Seite aus. Verwenden Siecdpnur als Notluke.console.logist der Ausgabekanal. Verwenden Sieconsole.log(help('page')),console.log(help('locator'))oder einen anderenhelp(name)-Aufruf, wenn eine genaue Signatur unklar ist.
Ausführungsregeln
page.url()ist asynchron in ego-browser; verwenden Sie immerawait page.url(). Einpage.waitForURL(...)-Prädikat empfängt einURL-Objekt, also inspizieren Sieurl.href,url.pathnameoderurl.searchParams. Es wartet standardmäßig aufload; verwenden SiewaitUntil: 'commit'nur, wenn Sie absichtlich vor dem Laden fortfahren.page.waitForURL,page.waitForLoadState,page.waitForSelector, LocatorwaitForundpage.waitForFunctiongeben bei Zeitüberschreitung einen falsy-Wert zurück. Überprüfen Sie das Ergebnis oder verifizieren Sie sofort den erforderlichen Zustand, bevor Sie fortfahren.- Registrieren Sie Request-, Response- oder Navigationswartezeiten vor der Aktion, die sie auslöst. Bevorzugen Sie zustandsbasierte Wartezeiten; verwenden Sie
page.waitForTimeout(...)nur für kurze visuelle Beruhigung und halten Sie es bei oder unter 2000 ms. - Bevorzugen Sie stabile semantische Locators. Wenn die Seitenstruktur unbekannt ist, sammeln Sie die relevanten Steuerelemente oder Kandidaten einmal mit
evaluateAll,allInnerTextsoder einer anderen begrenzten Leseoperation, leiten Sie die nächsten Aktionen in JavaScript ab und fahren Sie im selben Heredoc fort, anstatt Selektorversuche über Befehle hinweg aufzulisten. - Einzelelement-Aktionen und erforderliche Leseoperationen – einschließlich roher CSS- und roher
xpath=-Locators – sind strikt und warten automatisch. Bei null Treffern überprüfen Sie den Ladezustand, den aktiven Tab und den Modal-/Overlay-Zustand, bevor Sie den Locator korrigieren. Bei mehreren Treffern inspizieren Siecount()/allInnerTexts(), grenzen Sie semantisch oder mitfilter(...)ein und verwenden Siefirst()/nth()erst, nachdem Sie bestätigt haben, dass Duplikate legitim sind. Lassen Sie eine erfolgreiche Aktion das Skript voranbringen; lesen Sie den Zustand, wenn er einen Zweig bestimmt, und einmal für die erforderlichen endgültigen Nachbedingungen der Aufgabe, nicht nach jeder Aktion. Ein bereits erfüllter erforderlicher Zustand benötigt keine Wiederholung. - Verwenden Sie bei Fehlern eine gezielte Beobachtung, um die Strategie wesentlich zu ändern. Wiederholen Sie keine nahezu identischen Locators oder Befehle; wechseln Sie basierend auf den Beweisen zu einem stabilen semantischen, DOM- oder visuellen Pfad.
- Bewahren Sie explizit angeforderte, für den Benutzer sichtbare Übergänge und Stopp-Grenzen. Wenn ein erforderlicher Klick den aktuellen Tab navigieren oder einen anderen öffnen kann, klicken Sie einmal und lösen Sie das Ergebnis aus
await page.url()plus einem aktualisiertenbrowser.listTabs()im selben Skript; ersetzen Sie den Klick nicht durch direkte Navigation, nur weil das Ziel bekannt ist. Schlucken Sie keine Fehler von erforderlichen Aktionen.
Aufgabenbereiche
Ein Aufgabenbereich ist ein isolierter Browsing-Kontext mit eigenen Tabs, der den Login-Zustand des Benutzers erbt. Wählen Sie ihn einmal zu Beginn des ersten Bash-Skripts mit taskSpaces.useOrCreate(nameOrId) aus. Wenn eine externe Abhängigkeit einen späteren Befehl unvermeidlich macht, wählen Sie dieselbe zurückgegebene numerische task.id oder denselben genauen kurzen Zielnamen, bevor Sie fortfahren; erstellen Sie einen neuen Bereich nur für ein separates Benutzerziel. Bewahren Sie bereits verifizierte Fakten über Befehle hinweg, anstatt die Einrichtung neu zu starten.
useOrCreate verwendet oder erstellt agenteneigene Bereiche wieder. Wenn der passende Bereich benutzereigen ist, wird der Bereich ausgewählt, ohne ihn zu beanspruchen, sodass Browserarbeit auf die harte Grenze der Benutzersteuerung stößt. Nach expliziter Benutzerbestätigung, dort zu arbeiten, verwenden Sie taskSpaces.list() → taskSpaces.claim(id) → browser.listTabs() → browser.switchTab(targetId).
Jeder Bereich hat ownership: 'agent' | 'agentDelegatedToUser' | 'user':
| Operation auf einem benutzereigenen Bereich | Verhalten |
|---|---|
taskSpaces.switch | Wirft einen Fehler; es wechselt nur agenteneigene Bereiche |
taskSpaces.claim | Überträgt das Eigentum an den Agenten und wählt den Bereich aus |
taskSpaces.handOff / complete(..., { keep: true }) | Überspringt mit { done: false, skipped: 'user-owned' } |
taskSpaces.complete(..., { keep: false }) | Beansprucht, schließt dann den Bereich |
taskSpaces.takeOver / waitForAgentControl | Führt keine Eigentumsprüfung durch |
Überprüfen Sie das done-Ergebnis von handOff und complete, bevor Sie den Erfolg beanspruchen.
Behandeln Sie den Abschluss als terminalen Commit, getrennt von der Browserausführung. Beenden Sie die arbeitende Bash-Invokation ohne Abschluss, nachdem Sie die endgültige URL, Werte und andere Nachweise erfasst und ausgegeben haben. Überprüfen Sie diese Ausgabe: Jede angeforderte Nachbedingung und alle erforderlichen Umfangs- oder Abdeckungsgrenzen müssen nachgewiesen sein, nicht nur wahrscheinlich. Wenn sie nachgewiesen sind, führen Sie eine dedizierte letzte Bash-Invokation aus, die taskSpaces.complete(nameOrId, { keep }) höchstens einmal für die ursprüngliche ID oder den genauen Namen aufruft, done überprüft und keine page- oder browser-Arbeit ausführt. Wenn etwas nicht erfüllt oder nicht nachgewiesen ist, fahren Sie stattdessen im selben ursprünglichen Aufgabenbereich fort; eine Korrektur, Wiederholung oder spätere Phase ist kein neues Ziel. keep ist erforderlich. Standard ist false; verwenden Sie true nur, wenn der Benutzer darum gebeten hat, die fertige Seite zu behalten, manuell darin handeln muss oder das Ergebnis nicht als URL, Datei, Artefakt oder Zusammenfassung geliefert werden kann. Schließen Sie temporäre Tabs nach und nach und behalten Sie nur die Tabs, die der Benutzer benötigt.
Kodieren Sie niemals eine targetId fest, kopieren Sie sie nicht von Hand und benennen Sie sie nicht in id um. Besorgen und verwenden Sie sie innerhalb der aktuellen Bash-Invokation. Wenn ein anderer Befehl wirklich notwendig ist, aktualisieren Sie browser.listTabs() und validieren Sie find(...)-Ergebnisse, bevor Sie wechseln oder schließen. browser.iframeTarget(...) gibt eine Ziel-ID-Zeichenfolge oder null zurück, kein Objekt.
Steuerungsübergabe
Ein Fehler „Benutzer steuert“, „inaktiv“ oder „nicht zugewiesen“ ist ein harter Stopp für die gesamte Aufgabe. Wiederholen Sie es nicht, umgehen Sie es nicht und rufen Sie nicht automatisch taskSpaces.takeOver auf. Fragen Sie den Benutzer und warten Sie.
Für Login, Captcha oder einen anderen manuellen Schritt schließen Sie …