ego (lite) es solo un navegador, ego es su agente personal en todos sus dispositivos.
Únanse a la lista de espera
Español (Latinoamérica)

ego-browser

El entorno de ejecución de automatización del navegador que los agentes de IA usan para controlar la sesión real de Chromium de ego (lite).

llms.txt

ego-browser es el entorno de ejecución de automatización del navegador que ego (lite) proporciona a los agentes de IA. Se comunica mediante Chrome DevTools Protocol con la sesión real de Chromium de ego (lite) y recibe como punto de entrada un script Node.js en heredoc: el agente de IA escribe todo el flujo JS en un solo envío por stdin, todas las funciones auxiliares ya están inyectadas en el ámbito y el estado del navegador se conserva dentro de un Space.

ego-browser no está pensado para que una persona maneje un navegador manualmente y tampoco sustituye a Playwright ni a Puppeteer. Está dirigido a agentes de IA basados en LLM.

Para quién es

  • Agentes de IA para programación que necesitan controlar un navegador: Claude Code, Codex, Cursor y agentes SDK propios.
  • Equipos que construyen agentes verticales: automatizar Lark, Google Docs, Salesforce y back-offices parecidos.
  • Flujos web que se repiten: login, llenar formularios, exportar, buscar, leer tablas.
  • Quien alguna vez intentó meter el DOM completo o una página de HTML en un LLM y chocó con el límite de tokens.

Instalar

Viene con ego (lite) — ver Inicio rápido. Después de instalar, ego-browser se llama desde cualquier directorio.

La Skill también se puede instalar por separado:

npx skills add github:CitroLabs/ego-lite/skills/ego-browser

Ciclo central

El ritmo típico del agente operando una página, todo en un único heredoc:

ego-browser nodejs <<'EOF'
const task = await useOrCreateTaskSpace('buscar incidencias en GitHub')

await openOrReuseTab('https://github.com/issues', { wait: true, timeout: 20 })

cliLog(await snapshotText())

EOF
  1. Reutilizar o crear un Task Space (declarado en cada heredoc — ver Space).
  2. Abrir la página objetivo.
  3. Leer el snapshot (snapshotText()) y obtener el árbol semántico con [ref=N, loc=..., url=...].
  4. Actuar sobre la página por @N o selector CSS.
  5. Imprimir el resultado con cliLog(...).

Dentro del heredoc estás en un proceso Node.js; dentro de js(...) estás en el contexto de la página. No los mezcles.

Referencia de helpers

Todos los helpers están disponibles en el scope del script con su nombre camelCase. Sin import.

Task Space

await listTaskSpaces()
const task = await useOrCreateTaskSpace('describir la tarea')   // reutilizar o crear
await completeTaskSpace(task.name)                         // listo, mantener la pestaña
await closeTaskSpace(task.name)                            // cerrar el Space

name describe la tarea en 3 a 6 palabras, en lenguaje natural. No uses marcadores de posición.

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()        // un Task Space nuevo puede no tener ninguna pestaña

Observación

await snapshotText()                              // snapshot semántico de la página completa (por defecto)
await snapshotText({ scope: 'only_within_viewport' })
await captureScreenshot('result.png')
await drainEvents()                               // consumir la cola de eventos de navegación / red

Mouse y desplazamiento

click, doubleClick, hover y dragMouse aceptan el mismo formato de destino (píxeles CSS):

  • 'string': selector CSS o @ref. Hace clic en el centro del elemento.
  • [x, y] o {x, y}: coordenadas del viewport.
  • {selector, x, y}: offset desde la esquina superior izquierda del elemento.
  • options.label: descripción en 3 a 6 palabras; si se pasa, la acción dispara animación de resaltado.
await click('@21', { label: 'revisar el login' })
await click('button.primary', { label: 'hacer clic en Enviar' })
await click([420, 260])
await hover('@5', { label: 'pasar el mouse por el menú' })
await dragMouse([from, to], { label: 'arrastrar la tarjeta' })

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 },
)

Teclado y entrada

await typeText('hello world')
await fillInput('@2', 'user@test.com')
await pressKey('Enter')
await dispatchKey({ ... })

Archivos y red

await uploadFile('input[type="file"]', '/absolute/path/to/file.pdf')
await httpGet('https://api.example.com/data')   // GET emitido en el contexto de la página

Esperas

await wait(1)                                    // segundos
await waitForLoad()
await waitForElement('@1')
await waitForNetworkIdle()

wait() y timeout son en segundos. Solo los parámetros que terminan en Ms son milisegundos.

Ejecución en el navegador

js(source) usa Runtime.evaluate internamente y recibe una cadena. No le pases una función y argumentos al estilo Puppeteer: genera una advertencia, lo envuelve en .toString() y se pierden tanto las variables capturadas como el canal de argumentos.

Para lógica multi-paso, envuelve todo en una IIFE que devuelve una sola vez:

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' })

Salida y autodescubrimiento

cliLog(value)                  // el único canal de salida dentro de un heredoc
cliLog(help('click'))          // consultar el uso de un helper

Flujo recomendado

Empieza con snapshotText + ref / loc — conserva la semántica y evita la fragilidad de las coordenadas:

  1. Reusar o crear el Task Space.
  2. Abrir o cambiar de página (openOrReuseTab / gotoAndWait).
  3. snapshotText() para obtener el árbol [ref=N, loc=..., url=...]. Las refs se registran automáticamente en el refMap.
  4. Actuar sobre @N con click / fillInput / elementEval, o hacer una extracción de DOM en un solo js(...).
  5. cliLog(...) con el resultado final.

Otras rutas para combinar:

  • captureScreenshot + click([x, y]): layouts visuales, UIs de canvas, listas virtualizadas, páginas con accesibilidad incompleta.
  • js / elementEval / cdp: extraer DOM directo, inspeccionar el estado del navegador, o cualquier cosa que no encaja limpio en un helper estándar.

Mantén navegación, observación, scroll, extracción, filtrado, agregación y salida dentro de un único heredoc ego-browser nodejs. No pases los mismos datos a un segundo script node local.

Alcance de las refs

@N solo es válido para el refMap del último snapshotText. Cada snapshotText() reconstruye el refMap. Los números de ref vienen del backendNodeId CDP del elemento, así que el mismo elemento suele conservar el mismo número entre snapshots — pero para operar @N, N tiene que aparecer en el snapshot más reciente.

Causas habituales de Unknown ref:

  • El elemento salió del viewport.
  • El DOM volvió a renderizarse.
  • La ronda anterior usó scope: 'only_within_viewport' y la actual no cubre al elemento.

Cuando necesitas referenciar de forma estable el mismo elemento a lo largo de varias rondas, usa el loc=... del snapshot o escribe un selector CSS. Es también la base de la acumulación de Experience — ver Skills.

Área de trabajo de la Skill

ego-browser no gestiona por sí solo la Experience modificable del agente de IA. De forma predeterminada, carga extensiones de funciones auxiliares y la Experience adquirida en cada sitio desde el paquete de la Skill del repositorio:

../../skills/ego-browser

Se puede sobrescribir con variable de entorno:

EGO_BROWSER_AGENT_WORKSPACE=/path/to/ego-browser ego-browser nodejs <<'EOF'
cliLog(await siteSkills())
EOF

La Experience por sitio en learnings/ está siempre activa y se lee en cada llamada a una función auxiliar. El modelo de escritura y descubrimiento de Experience está en Skills.

Validar la experiencia aprendida:

npm run validate:learnings

Estructura de carpetas

package/ego-browser/
├── src/                      # browser-runtime / helpers / run.js
│   ├── browser-runtime.js    # puente ego runtime en el lado del navegador
│   ├── helpers.js            # helpers expuestos al script del agente
│   ├── run.js                # entrada CLI (ejecuta stdin)
│   └── learning/             # índice de experiencia, validación de dominio, validación de formato
├── artifacts/ego-browser/    # resultado de build; npm bin apunta acá
└── test/                     # pruebas unitarias

skills/ego-browser/
├── SKILL.md / SKILL.zh.md    # punto de entrada del agente
└── learnings/                # directorio de experiencia por sitio

Notas

  • snapshotText() por defecto usa scope: 'full_page' y cubre toda la página. Solo pasa 'only_within_viewport' si de verdad necesitas únicamente el área visible.
  • js() devuelve directo el resultado de la expresión; no lo vuelvas a JSON.parse(...).
  • Cuando escribas regex dentro de un template string de js(), duplica las contrabarras (\\d, \\s) o usa String.raw.
  • Un return en el nivel superior se envuelve automáticamente en IIFE. Un return dentro de un callback anidado puede disparar lo mismo, así que escribe las expresiones complejas como (() => { ... })() de entrada.
  • Cuando el usuario pide explícitamente ego-browser, el entorno de ejecución ya está listo. No hagas una comprobación previa (which ego-browser / node -v / volcado de ayuda) salvo que una primera ejecución realmente falle.