ego-browser
ego-browser expose un navigateur Chromium réel via un runtime Node.js accessible en ligne de commande (CLI). Ses facades préchargées page, page.locator(...), browser et taskSpaces suivent les noms et les signatures de Playwright ; taskSpaces, site, fetch et cdp fournissent des capacités spécifiques à ego-browser.
Pour les problèmes d'installation, de configuration ou de connexion, lisez references/install.md.
Exécutez les travaux du navigateur avec l'outil Bash comme suit : ego-browser nodejs <<'EOF' ... EOF. Placez le code JavaScript directement dans le heredoc ; ne créez pas de fichier .js, n'importez pas Playwright, ne lancez pas un autre navigateur et n'inventez pas de noms d'aide.
Un heredoc est uniquement le conteneur JavaScript ; l'invocation Bash est le cycle d'exécution. Par défaut, utilisez une seule invocation Bash pour l'ensemble de la tâche navigateur. Chaque await est une opération interne, pas une limite d'étape. Avant le lancement, encodez dans le script chaque observation prévisible, action, attente, extraction, vérification et alternative bornée. Utilisez immédiatement les résultats du navigateur en JavaScript et continuez à vous adapter dans le processus jusqu'à ce que la tâche soit terminée ; ne sortez pas simplement pour inspecter une sortie intermédiaire ou planifier l'action suivante. Lancez une autre commande Bash uniquement pour une intervention utilisateur ou externe requise, une inspection visuelle qui ne peut pas avoir lieu dans le processus, ou une défaillance au niveau du processus que le script ne peut pas gérer.
Choisissez la route fiable la moins dépendante de l'état avant d'inspecter les contrôles de la page. Lorsque la tâche spécifie un résultat ou des contraintes mais pas une interaction requise, préférez un état déjà correct ou une URL ou route de site stable connue qui les encode directement ; vérifiez l'état final du résultat au lieu de rejouer des filtres, des tris ou une navigation équivalents via l'interface utilisateur. Utilisez les contrôles de la page lorsque l'utilisateur a demandé cette interaction, que l'interaction elle-même est testée ou qu'aucun équivalent fiable n'est connu. N'inventez jamais une route fragile.
Traitez une postcondition déjà satisfaite comme un travail terminé. Avant de manipuler un contrôle dont la valeur requise peut déjà être visible, effectuez uniquement la plus petite lecture nécessaire pour décider de cet état. Si elle correspond, n'ouvrez pas son éditeur, ne rejouez pas l'interaction et ne la lisez pas à nouveau ; passez directement aux résultats non satisfaits restants. Les mots comme « définir », « sélectionner » ou « s'assurer » décrivent l'état final requis, sauf si l'utilisateur exige explicitement la transition ou que l'interaction elle-même est testée.
Séparez le travail du navigateur de la fin du terminal. useOrCreate commence ou reprend un objectif utilisateur ; conservez son task.id retourné et réutilisez le même id ou le même nom exact jusqu'à ce que cet objectif soit terminal. Gardez chaque observation prévisible, action, attente, extraction et vérification compacte, mais n'appelez pas taskSpaces.complete(...) dans une invocation Bash qui détermine encore si l'objectif est satisfait. Terminez d'abord le travail du navigateur et affichez la preuve que chaque résultat demandé et toute portée ou limite de couverture requise ont été prouvés. Ce n'est qu'après avoir examiné cette sortie préalable qu'une invocation Bash finale dédiée peut compléter l'espace de tâche original ; elle n'effectue aucun travail de page ou browser. Cet unique engagement de cycle de vie est l'exception à la règle par défaut d'une invocation, pas une étape ou un cycle de navigateur. Des résultats partiels non vides ou plausibles, une page bloquée, des tentatives épuisées ou une tentative de repli ne constituent pas une preuve de fin. keep: true préserve un résultat terminal pour l'utilisateur ; il ne maintient pas une tâche non terminée en vie.
Gelez la fenêtre temporelle pour le travail en cours ou relatif à une date. Établissez « aujourd'hui/actuel/le plus récent » une fois à partir de l'environnement utilisateur/tâche ou de l'état de page actuel explicitement vérifié avant de collecter des enregistrements. Traitez les horodatages de contenu comme des données, pas comme l'horloge. Les enregistrements plus anciens révélés par le défilement, la virtualisation, le rechargement, le cache ou un lot de résultats modifié ne doivent pas remplacer cette ancre. Continuez à évaluer les enregistrements par rapport à la fenêtre d'origine ; ne rebasez pas silencieusement la tâche sur la date de contenu la plus récente observée.
Démarrage rapide
Chaque exemple de cette compétence est délibérément composite. Adaptez son URL, ses sélecteurs et ses données à la tâche utilisateur.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('inspect example page')
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('Example page was not ready')
const result = { taskSpaceId: task.id, heading, url: info.url }
console.log(JSON.stringify(result, null, 2))
EOF
Gardez tout le travail prévisible à l'intérieur du script jusqu'à ce que la tâche soit terminée. Émettez les résultats finaux avec console.log(...).
Modèles composites
Extraire, choisir, naviguer, vérifier
Sur une page de liste ou de recherche, extrayez des candidats structurés avant de choisir. Gardez l'extraction, le choix, l'action, l'attente, la vérification et le nettoyage dans une seule invocation Bash.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('compare search results')
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('No usable result: ' + 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('Chosen result did not navigate')
const info = await page.info()
if (!('url' in info) || info.url === before) throw new Error('Navigation was not verified')
const result = { chosen: items[chosenIndex], opened: info.url }
console.log(JSON.stringify(result, null, 2))
EOF
Remplir, déclencher, attendre, relire
Enregistrez les attentes de requête/réponse avant l'action qui les déclenche, puis vérifiez l'état résultant de la page plutôt que de considérer le clic comme un succès.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('search orders')
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('Search orders').fill('pending')
await page.getByRole('button', { name: /search/i }).click()
const response = await responsePromise
const rows = await page.locator('table tbody tr').allInnerTexts()
if (!rows.length) throw new Error('Search completed but returned no visible rows')
const result = { status: response.status(), rows }
console.log(JSON.stringify(result, null, 2))
EOF
Rafraîchir les handles d'onglets, basculer, inspecter
Traitez targetId comme un handle de courte durée. Découvrez, validez et utilisez-le dans la même invocation Bash.
ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('review generated report')
const tabs = await browser.listTabs({ includeChrome: false })
const reportTab = tabs.find((tab) => tab.url.includes('/reports/'))
if (!reportTab?.targetId) throw new Error('Report tab not found: ' + 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('Wrong tab selected')
const result = { taskSpaceId: task.id, heading, url: info.url }
console.log(JSON.stringify(result, null, 2))
EOF
Carte d'exécution
page: navigation et état (goto,reload,url,title,info), localisateurs sémantiques, attentes,snapshot,screenshot,screencast,evaluate,keyboard,mouse, téléchargements et vidange d'événements.page.locator(selector): chaînage et filtrage ;first/nth/last; méthodes de clic, survol,dragTo,scrollIntoViewIfNeeded, formulaire, clavier, téléchargement, lecture d'état, collection, évaluation d'élément, capture d'écran et attente.browser:listTabs,currentTab,switchTab,openOrReuseTab,closeTab,ensureRealTab,iframeTarget.taskSpaces:list,switch,new,useOrCreate,claim,complete,handOff,takeOver,waitForAgentControl.fetch.servereffectue des requêtes côté Node ;fetch.browsereffectue des requêtes dans l'origine de la page actuelle. Utilisezcdpuniquement comme issue de secours.console.logest le canal de sortie. Utilisezconsole.log(help('page')),console.log(help('locator'))ou un autre appelhelp(name)lorsqu'une signature exacte n'est pas claire.
Règles d'exécution
page.url()est asynchrone dans ego-browser ; utilisez toujoursawait page.url(). Un prédicatpage.waitForURL(...)reçoit un objetURL, inspectez doncurl.href,url.pathnameouurl.searchParams. Il attendloadpar défaut ; utilisezwaitUntil: 'commit'uniquement lorsque vous avancez intentionnellement avant le chargement.page.waitForURL,page.waitForLoadState,page.waitForSelector, l'attente du localisateur etpage.waitForFunctionretournent une valeur fausse en cas de délai d'attente dépassé. Vérifiez le résultat ou vérifiez immédiatement l'état requis avant de continuer.- Enregistrez les attentes de requête, de réponse ou de navigation avant l'action qui les déclenche. Préférez les attentes basées sur l'état ; n'utilisez
page.waitForTimeout(...)que pour un bref ajustement visuel et maintenez-le à 2000 ms ou moins. - Préférez les localisateurs sémantiques stables. Lorsque la structure de la page est inconnue, collectez les contrôles ou candidats pertinents une fois avec
evaluateAll,allInnerTextsou une autre lecture bornée, dérivez les actions suivantes en JavaScript et continuez dans le même heredoc au lieu d'énumérer des suppositions de sélecteur entre les commandes. - Les actions sur un seul élément et les lectures requises—y compris les localisateurs CSS bruts et
xpath=bruts—sont strictes et attendent automatiquement. Pour zéro correspondance, confirmez le chargement, l'onglet actif et l'état de la modale/superposition avant de corriger le localisateur. Pour plusieurs correspondances, inspectezcount()/allInnerTexts(), affinez sémantiquement ou avecfilter(...), et n'utilisezfirst()/nth()qu'après avoir confirmé que les doublons sont légitimes. Laissez une action réussie faire avancer le script ; lisez l'état lorsqu'il détermine une branche et une fois pour les postconditions finales requises de la tâche, pas après chaque action. Un état requis déjà satisfait n'a pas besoin d'être rejoué. - En cas d'échec, utilisez une observation ciblée pour changer de stratégie de manière significative. Ne répétez pas des localisateurs ou des commandes quasi identiques ; passez à un chemin sémantique, DOM ou visuel stable basé sur les preuves.
- Préservez les transitions visibles explicitement demandées par l'utilisateur et les limites d'arrêt. Lorsqu'un clic requis peut naviguer dans l'onglet actuel ou en ouvrir un autre, cliquez une fois et résolvez le résultat à partir de
await page.url()plus unbrowser.listTabs()rafraîchi dans le même script ; ne remplacez pas le clic par une navigation directe simplement parce que sa destination est connue. N'avalez pas les échecs des actions requises.
Espaces de tâche
Un espace de tâche est un contexte de navigation isolé avec ses propres onglets qui hérite de l'état de connexion de l'utilisateur. Sélectionnez-le une fois près du début du premier script Bash avec taskSpaces.useOrCreate(nameOrId). Si une dépendance externe rend une commande ultérieure inévitable, sélectionnez le même task.id numérique retourné ou le même nom d'objectif court exact avant de continuer ; créez un nouvel espace uniquement pour un objectif utilisateur distinct. Préservez les faits déjà vérifiés entre les commandes au lieu de redémarrer la configuration.
useOrCreate réutilise ou crée des espaces appartenant à l'agent. Si l'espace correspondant appartient à l'utilisateur, il sélectionne l'espace sans le réclamer, donc le travail du navigateur rencontre l'arrêt brutal du contrôle utilisateur. Après confirmation explicite de l'utilisateur pour y travailler, utilisez taskSpaces.list() → taskSpaces.claim(id) → browser.listTabs() → browser.switchTab(targetId).
Chaque espace a ownership: 'agent' | 'agentDelegatedToUser' | 'user':
| Opération sur un espace appartenant à l'utilisateur | Comportement |
|---|---|
taskSpaces.switch | Lève une exception ; ne bascule que les espaces appartenant à l'agent |
taskSpaces.claim | Transfère la propriété à l'agent et sélectionne l'espace |
taskSpaces.handOff / complete(..., { keep: true }) | Ignore avec { done: false, skipped: 'user-owned' } |
taskSpaces.complete(..., { keep: false }) | Réclame, puis ferme l'espace |
taskSpaces.takeOver / waitForAgentControl | N'effectue aucune vérification de propriété |
Vérifiez le résultat done de handOff et complete avant de revendiquer le succès.
Traitez la complétion comme un engagement terminal séparé de l'exécution du navigateur. Terminez l'invocation Bash de travail sans complétion après avoir capturé et imprimé l'URL finale, les valeurs et autres preuves. Examinez cette sortie : chaque postcondition demandée et toute portée ou limite de couverture requise doit être prouvée, pas seulement probable. Si elles sont prouvées, exécutez une invocation Bash finale dédiée qui appelle taskSpaces.complete(nameOrId, { keep }) au plus une fois pour l'id original ou le nom exact, vérifie done et n'effectue aucun travail page ou browser. Si quoi que ce soit n'est pas satisfait ou n'est pas prouvé, continuez dans le même espace de tâche original à la place ; une correction, une nouvelle tentative ou une phase ultérieure n'est pas un nouvel objectif. keep est obligatoire. Par défaut, false ; utilisez true uniquement lorsque l'utilisateur a demandé de conserver la page terminée, doit agir manuellement dessus, ou que le résultat ne peut pas être livré sous forme d'URL, fichier, artefact ou résumé. Fermez les onglets provisoires au fur et à mesure et ne conservez que les onglets dont l'utilisateur a besoin.
Ne codez jamais en dur, ne recopiez pas manuellement et ne renommez pas un targetId en id. Obtenez-le et utilisez-le dans l'invocation Bash actuelle. Si une autre commande est véritablement nécessaire, rafraîchissez browser.listTabs() et validez les résultats de find(...) avant de basculer ou de fermer. browser.iframeTarget(...) retourne une chaîne target-id ou null, pas un objet.
Transfert de contrôle
Une erreur « l'utilisateur contrôle », « inactif » ou « non assigné » est un arrêt brutal pour l'ensemble de la tâche. Ne réessayez pas, ne contournez pas et n'appelez pas taskSpaces.takeOver automatiquement. Demandez à l'utilisateur et attendez.
Pour la connexion, un captcha ou une autre étape manuelle, terminez...