ego (lite) es solo un navegador, ego es tu agente personal en todos tus dispositivos.
Únete a la lista de espera

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) ofrece a los agentes de IA. Se comunica mediante Chrome DevTools Protocol con la sesión real de Chromium dentro de ego (lite) y recibe un script heredoc de Node.js como punto de entrada: el agente escribe todo el flujo JS en un único envío por stdin, todos los helpers ya están inyectados en el ámbito del script y el estado del navegador se mantiene dentro de un Space.

ego-browser no está diseñado para que los humanos manejen un navegador manualmente y no 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 personalizados.
  • Equipos que crean agentes verticales: automatizan Lark, Google Docs, Salesforce y back office similares.
  • Repetir flujos web fijos: iniciar sesión, completar, exportar, buscar, leer tablas.
  • Cualquiera que haya intentado introducir un DOM completo o una página HTML en un LLM y se haya topado con el límite de tokens.

Instalar

Se incluye con ego (lite); consulta Inicio rápido. Después de instalarlo, ejecuta ego-browser desde cualquier directorio.

También puedes instalar la Skill por separado:

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

Bucle central

El ritmo típico de los agentes que dirigen una página: todo en un solo documento 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. Reutiliza o crea un Task Space (decláralo en cada heredoc; consulta Space).
  2. Abre la página de destino.
  3. Lee el Snapshot (snapshotText()) para obtener un árbol semántico con [ref=N, loc=..., url=...].
  4. Actúa en la página mediante una ref @N o un selector CSS.
  5. Imprime el resultado final 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 ayuda

Cada helper está disponible en el ámbito del script por su nombre camelCase. No se necesita ningún import.

Task Space

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

name debe ser una descripción de la tarea en lenguaje natural de 3 a 6 palabras. 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 (predeterminado)
await snapshotText({ scope: 'only_within_viewport' })
await captureScreenshot('result.png')
await drainEvents()                               // consumir la cola de eventos de navegación y red

Ratón 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 de la ventana gráfica.
  • {selector, x, y}: desplazamiento relativo desde la parte superior izquierda del elemento.
  • options.label: descripción de 3 a 6 palabras. Pásalo y la acción desencadenará un resalte visual.
await click('@21', { label: 'comprobar el estado de inicio de sesión' })
await click('button.primary', { label: 'hacer clic en Enviar' })
await click([420, 260])
await hover('@5', { label: 'mostrar el menú al pasar el puntero' })
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')   // solicitud GET emitida en el contexto de la página

Espera

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

wait() y timeout están en segundos. Sólo los parámetros que terminan en Ms son milisegundos.

Ejecución del navegador

js(source) usa Runtime.evaluate internamente y recibe una cadena. No le pases una función y argumentos como harías con Puppeteer: la función se convierte mediante .toString(), se pierden las variables del cierre léxico y desaparece el canal de argumentos.

Para una lógica de varios pasos, envuélvala en un IIFE que devuelva una 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' })

Producción y autodescubrimiento

cliLog(value)                  // único canal de salida de un heredoc
cliLog(help('click'))          // consultar cómo se usa una función auxiliar

Flujo de trabajo recomendado

Empieza con snapshotText y ref/loc: mantiene intacta la semántica y evita la fragilidad de las coordenadas:

  1. Reutiliza o crea el Task Space.
  2. Abre la página o cambia a ella (openOrReuseTab / gotoAndWait).
  3. snapshotText() para obtener el árbol [ref=N, loc=..., url=...]. Las referencias se registran en refMap automáticamente.
  4. Actúa sobre @N con click / fillInput / elementEval, o realiza una extracción del DOM de una sola vez dentro de js(...).
  5. cliLog(...) el resultado final.

Otros caminos útiles para combinar:

  • captureScreenshot + click([x, y]): diseños visuales, UI basadas en lienzo, listas virtuales, páginas con accesibilidad incompleta.
  • js / elementEval / cdp: extrae DOM directamente, inspecciona el estado del navegador o cualquier cosa que no se ajuste limpiamente a un asistente estándar.

Mantén la navegación, la observación, el desplazamiento, la extracción, el filtrado, la agregación y la salida dentro de un único heredoc ego-browser nodejs. No canalices los datos a través de un segundo script node local.

Alcance de la referencia

@N solo es válido contra el refMap del snapshotText más reciente. Cada snapshotText() reconstruye el refMap. Los números de referencia provienen del CDP backendNodeId del elemento, por lo que el mismo elemento generalmente lleva el mismo número en todas las instantáneas, pero para que @N sea operable, N debe aparecer en la salida de la instantánea más reciente.

Causas comunes de Unknown ref:

  • El elemento salió de la ventana gráfica.
  • El DOM volvió a renderizarse.
  • Una ronda anterior usó scope: 'only_within_viewport' y la siguiente ronda no cubrió el elemento.

Cuando necesites una referencia estable al mismo elemento durante varias rondas, usa el selector loc=... del Snapshot o escribe directamente un selector CSS. Esta es también la base de la experiencia acumulada; consulta 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 las extensiones de las funciones auxiliares y la Experience adquirida en cada sitio desde el paquete de la Skill del repositorio:

../../skills/ego-browser

Anular mediante env var:

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

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

Validar la experiencia aprendida:

npm run validate:learnings

Diseño del directorio

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

Notas

  • snapshotText() usa scope: 'full_page' de forma predeterminada. Pasa 'only_within_viewport' solo cuando necesites limitarlo al área visible.
  • js() devuelve el resultado de la evaluación directamente. No lo hagas JSON.parse(...) otra vez.
  • Al escribir expresiones regulares dentro de una cadena de plantilla js(), duplique las barras invertidas (\\d, \\s) o cambie a String.raw.
  • Un return de nivel superior se incluye automáticamente en un IIFE. Un return dentro de una devolución de llamada anidada también puede desencadenar eso, así que escribe expresiones complejas como (() => { ... })() por adelantado.
  • Cuando el usuario haya solicitado explícitamente ego-browser, el entorno de ejecución estará listo. No hagas una comprobación previa con which ego-browser, node -v o un volcado de ayuda; hazla solo si la primera ejecución genera un error.