
Automatización de navegadores, rastreo web y control de dispositivos iOS + Android para agentes de IA. Instantáneas CDP nativas de Zig y eficientes en tokens, grabación de HAR, cliente nativo del protocolo de cable de adb y un fetcher independiente.
<p align="center">
<img src="https://assets.kitploit.com/production/public/readmes/13639/7915bba5fb25229c11f59482cfcd4a0eaf2945ee066dbf5cec395df880b53268.png" alt="Kuri" width="200" />
</p>
<h1 align="center">Kuri 🌰</h1>
<p align="center">
<a href="https://kuri.trilok.ai/download/latest.json"><img src="https://img.shields.io/badge/stable-v0.6.1-brightgreen?style=flat-square" alt="Stable release"></a>
<a href="https://github.com/justrach/kuri/blob/main/LICENSE"><img src="https://img.shields.io/github/license/justrach/kuri?style=flat-square" alt="License"></a>
<img src="https://img.shields.io/badge/zig-0.16.0-f7a41d?style=flat-square" alt="Zig">
<img src="https://img.shields.io/badge/node__modules-0_files-brightgreen?style=flat-square" alt="node_modules">
<img src="https://img.shields.io/badge/status-stable-brightgreen?style=flat-square" alt="status">
</p>
## Instalación
```sh
curl -fsSL https://kuri.trilok.ai/download | sh
```
macOS arm64/x86_64 y Linux x86_64/arm64. Binario único, sin dependencias de runtime.
Descargas directas: [macOS arm64](https://kuri.trilok.ai/download/v0.6.1/kuri-v0.6.1-aarch64-macos.tar.gz) · [macOS x86_64](https://kuri.trilok.ai/download/v0.6.1/kuri-v0.6.1-x86_64-macos.tar.gz) · [Linux x86_64](https://kuri.trilok.ai/download/v0.6.1/kuri-v0.6.1-x86_64-linux.tar.gz) · [Linux arm64](https://kuri.trilok.ai/download/v0.6.1/kuri-v0.6.1-aarch64-linux.tar.gz)
---
**Automatización de navegador y web crawling para agentes de IA. Escrito en Zig. Cero Node.js.**
Automatización CDP · Snapshots de A11y · Grabación HAR · Fetcher autónomo · Navegador de terminal interactivo · CLI agéntico · Pruebas de seguridad · Control de dispositivos iOS + Android
[Inicio rápido](#-quick-start) · [Benchmarks](#-benchmarks) · [kuri-agent](#-kuri-agent) · [Pruebas de seguridad](#-security-testing) · [API](#-http-api) · [Skills](#-skills) · [Changelog](https://github.com/justrach/kuri/blob/main/CHANGELOG.md)
> **Por qué los equipos cambian a Kuri:** las compilaciones actuales `ReleaseFast` para Apple Silicon se mantienen por debajo de 2 MB por binario, y una nueva ejecución de Google Flights el 2026-04-23 midió **3.392 tokens** para un ciclo completo de `kuri-agent` (`go→snap→click→snap→eval`). Los deltas entre herramientas deberían volver a medirse en el mismo entorno antes de citar un porcentaje.
---
## Por qué Kuri gana para agentes
La mayoría de las herramientas de navegador fueron creadas para ingenieros de QA. Kuri está creado para ciclos de agentes: leer la página, mantener bajo el costo en tokens, actuar sobre refs estables y seguir adelante.
- **135 endpoints HTTP** — paridad total con agent-browser y browser-use, desde inspección de React hasta Core Web Vitals.
- **7-12% menos tokens** que agent-browser en páginas reales gracias al formato de refs `@eN` y al renderizado sin prefijos.
- **Observaciones 44x más ligeras** con `/page/state` (48 tokens) frente al snapshot completo (2.124 tokens) para la misma página de Google Flights.
- **Ejecución por lotes** — `POST /batch` envía N comandos en una sola llamada HTTP, eliminando N-1 round-trips y N-1 turnos del LLM.
- **Compatible con React** — eventos de mouse CDP confiables y eventos de teclado por carácter disparan `onClick` y `onChange` de React 18/19.
### Tokens de snapshot: Google Flights `SIN → TPE`
Nueva ejecución el 2026-05-24 en este workspace, medida con `wc -c` y aproximación `chars/4`.
| Herramienta / Modo | Chars | ~Tokens | Nota |
|---|---:|---:|---|
| `kuri snap` (completo) | 8.499 | **2.124** | Todos los nodos + refs interactivos |
| `kuri snap` (solo interactivos) | ~3.000 | **~750** | Mejor para ciclos de agentes |
| `kuri /page/state` | 190 | **48** | Observación ligera (url, title, scroll%, counts) |
| agent-browser snap (estimado) | ~9.183 | **~2.295** | Sobrecarga del formato `[ref=e0]` |
### Eficiencia de tokens: kuri vs agent-browser
| Página | tokens de kuri | tokens de agent-browser | Ahorro |
|---|---:|---:|---|
| example.com | 40 | 35 | -13% (página trivial, agent-browser omite la raíz) |
| Hacker News | 386 | ~440 | **12% menos** |
| Google Flights SIN→TPE | 2.124 | ~2.295 | **7% menos** |
El ahorro proviene del formato compacto de kuri:
- refs `@e0` (3 chars) vs `[ref=e0]` (9 chars)
- Sin prefijo `- ` por línea (ahorra 2 chars × cantidad de líneas)
- Misma indentación, mismo filtrado de nodos
### Costo del flujo completo: `go → snap → click → snap → eval`
| Herramienta | Tokens por ciclo |
|---|---:|
| **kuri-agent** | **~3.400** |
| Con `/page/state` en lugar del segundo snap | **~1.700** |
| Con `POST /batch` (todo en una llamada) | **~1.700** (mismos tokens, 1 llamada HTTP en lugar de 5) |
### kuri vs libretto
[libretto](https://github.com/saffron-health/libretto) (Playwright + Node) es el competidor más cercano en costo de tokens por paso. Medido cara a cara el 2026-07-04 — mismo Chrome, misma pestaña, conteos reales de `tiktoken` `o200k_base` (metodología completa y reproducción: **[benchmarks/libretto_comparison.md](https://github.com/justrach/kuri/blob/main/benchmarks/libretto_comparison.md)**). La división honesta:
| Eje | Ganador | Detalle |
|---|---|---|
| Latencia por llamada | **kuri** | 4–117 ms vs 1.344–1.500 ms (**13–376× más rápido** — servidor persistente vs Node por comando) |
| Tokens de snapshot, página típica | **kuri** | simple 61 vs 151 (2,5×), artículo 265 vs 363 (1,37×) — gramática más ajustada |
| Tokens de snapshot, lista grande | empate | kuri por defecto 4.424 vs 813 — kuri emite las 259 refs, libretto trunca por defecto. Con `limit=5` kuri renderiza 555 tokens (**1,46× por debajo de libretto**), 34 refs + marcadores `… +45 more` |
| Trayectoria (feed, 9 clics) | **kuri**, apenas | 898 vs 939 tokens (base `limit=5` + bucle de diff vs bucle exec) — paridad a ligera ventaja; la pérdida de 5,1× de la mañana era la base sin truncar |
| Ejecuciones repetidas | **libretto** | compila trayectorias a un script de Playwright → repeticiones de 0 tokens; kuri vuelve a pagar el bucle en cada ejecución |
**Lo que kuri ganó al estudiar libretto** (todo incluido en esta versión): un bucle diff-first (`take_snapshot_diff`, ~38 tokens/paso); un diff adaptativo que recurre a un snapshot completo con un encabezado `! page replaced` en la navegación; líneas de eliminación solo de identidad; capturas de pantalla escritas a disco (se devuelve la ruta, los bytes nunca entran al contexto); `get_page_state` sobre MCP; y — tras reescribir `parseA11yNodes` como un verdadero recorrido DFS del árbol — **truncado de listas opcional** (`/snapshot?limit=N`, una línea `… +K more` por ejecución limitada), **recaptura con alcance** (`scope=@ref`) e **indentación jerárquica**, también expuestos como `uid`/`limit` en el `take_snapshot` de MCP. La trayectoria de feed de 9 clics que costaba 44.285 tokens con re-snapshots completos ingenuos cuesta **898** con una base truncada + diffs — 49× más barato, y por encima de los 939 de libretto.
> Las tablas anteriores más antiguas usan una aproximación de tokens `chars/4`; la comparación con libretto usa conteos reales de `tiktoken`. Vuelve a medir los números entre herramientas en tu propio entorno antes de citar un porcentaje.
### Tamaño del binario y memoria
Medido en Apple M4 Pro, macOS 26.4.1. Los binarios actuales se compilaron con `-Doptimize=ReleaseFast`.
| Binario | Tamaño actual |
|---|---:|
| `kuri` | 1.093.840 B (1,04 MiB) |
| `kuri-agent` | 629.904 B (615 KiB) |
| `kuri-browse` | 1.089.120 B (1,04 MiB) |
| `kuri-fetch` | 2.063.488 B (1,97 MiB) |
### El RSS se mantuvo estable durante la migración a Zig 0.16
Medido contra la compilación actual `v0.4.3` `ReleaseFast` con `/usr/bin/time -l`.
| Comando | RSS máximo medio de `v0.4.3` |
|---|---:|
| `kuri-fetch --version` | ~2,45 MiB |
| `kuri-browse --version` | ~2,45 MiB |
| `kuri-fetch --quiet --dump markdown http://example.com/` | ~9,17 MiB |
## El problema
Toda herramienta de automatización de navegador arrastra Playwright (~300 MB), un runtime de Node.js y una cascada de dependencias de npm. Tu agente de IA solo quiere leer una página, hacer clic en un botón y seguir adelante.
**Kuri es un único binario de Zig.** Cuatro modos, cero runtime:
```
kuri → CDP server (Chrome automation, a11y snapshots, HAR)
kuri-fetch → standalone fetcher (no Chrome, QuickJS for JS, ~2 MB)
kuri-browse → interactive terminal browser (navigate, follow links, search)
kuri-agent → agentic CLI (scriptable Chrome automation + security testing)
```
---
## 📦 Instalación
### Instalación en una línea (macOS / Linux)
```sh
curl -fsSL https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/install.sh | sh
```
Detecta tu plataforma, descarga el binario correcto y lo instala en `~/.local/bin`.
Las descargas provienen de la rama `release-channel` autogestionada de Kuri. Los binarios de macOS están firmados localmente con un certificado de Developer ID. Los recursos de GitHub Release reflejan estos mismos tarballs.
### bun / npm
```sh
bun install -g kuri-agent
# or: npm install -g kuri-agent
```
Descarga el binario nativo correcto para tu plataforma en el momento de la instalación.
### Canal de versiones
Los binarios estables de Kuri se encuentran en la rama `release-channel` y se sirven directamente desde las URL raw de GitHub.
- Instalador estable: `https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/install.sh`
- Manifiesto estable: `https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/latest.json`
- Vista de la rama: `https://github.com/justrach/kuri/tree/release-channel/stable`
- Patrón de descarga directa: `https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/<version>/kuri-<version>-<target>.tar.gz`
### Manual
Descarga el tarball para tu plataforma desde el [manifiesto de la versión estable](https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/latest.json) o desde la [página de GitHub Releases](https://github.com/justrach/kuri/releases) y descomprímelo en tu `$PATH`.
URL de instalación estable:
```sh
curl -fsSL https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/install.sh | sh
```
El manifiesto incluye URLs exactas de los recursos más sumas de verificación SHA-256 para `aarch64-linux`, `x86_64-linux`, `aarch64-macos` y `x86_64-macos`.
### Compatibilidad de plataformas
| Plataforma | Estado |
|---|---|
| macOS (`aarch64`, `x86_64`) | Binarios precompilados, firmados + notarizados |
| Linux (`aarch64`, `x86_64`) | Binarios precompilados |
| Windows (`x86_64`) | **Experimental — solo compilación cruzada.** `zig build -Dtarget=x86_64-windows-gnu` está verificado en CI, pero la automatización de Chrome, la demonización, el apagado basado en señales, la grabación de HAR y el almacén de autenticación respaldado por archivos están todos stubbeados con `error.UnsupportedOnWindows` en tiempo de ejecución. Usa **WSL2** si necesitas el conjunto de características real. Registrado en [#153](https://github.com/justrach/kuri/issues/153). |
Kuri se apoya en primitivas POSIX (`fork`, `clock_gettime`, sockets sin procesar) en varios puntos, por lo que un port nativo completo a Windows es trabajo real. La línea base a nivel de compilación anterior permite que las rutas `--version`/`--help` y las operaciones puramente en memoria se ejecuten; las partes complicadas (Chrome, sockets, daemonize) necesitan implementaciones reales de Win32 antes de salir de la lista de stubs. Si quieres encargarte de alguna de ellas, +1 en [#153](https://github.com/justrach/kuri/issues/153) o abre un PR.
### Compilar desde el código fuente
Requiere [Zig ≥ 0.16.0](https://ziglang.org/download/).
```bash
git clone https://github.com/justrach/kuri.git
cd kuri
zig build -Doptimize=ReleaseFast
# Binaries in zig-out/bin/: kuri kuri-agent kuri-fetch kuri-browse
```
---
## ⚡ Inicio rápido
**Requisitos:** [Zig ≥ 0.16.0](https://ziglang.org/download/) · Chrome/Chromium (para el modo CDP)
```bash
git clone https://github.com/justrach/kuri.git
cd kuri
zig build # build everything
zig build test # run 252+ tests
# CDP mode — launches Chrome automatically
./zig-out/bin/kuri
# Standalone mode — no Chrome needed
./zig-out/bin/kuri-fetch https://example.com
# Interactive browser — browse from your terminal
./zig-out/bin/kuri-browse https://example.com
# Experimental standalone browser runtime — separate build, not production
(cd kuri-browser && zig build run -- render https://example.com)
(cd kuri-browser && zig build run -- bench --offline)
```
### Primera ejecución, ruta más corta
```bash
# start the server; if CDP_URL is unset, kuri launches managed Chrome for you
./zig-out/bin/kuri
# discover tabs from that managed browser
curl -s http://127.0.0.1:8080/discover
# inspect the discovered tab list
curl -s http://127.0.0.1:8080/tabs
```
### Bucle de agente basado en sesión
Para el uso HTTP estilo agente, se prefiere una cabecera de sesión junto con `/tab/new`, `/page/info` y `/snapshot` en lugar de repetir `tab_id` en cada llamada.
```bash
SESSION=hn-demo
BASE=http://127.0.0.1:8080
curl -s -H "X-Kuri-Session: $SESSION" \
"$BASE/tab/new?url=https%3A%2F%2Fnews.ycombinator.com"
curl -s -H "X-Kuri-Session: $SESSION" "$BASE/page/info"
SNAP=$(curl -s -H "X-Kuri-Session: $SESSION" "$BASE/snapshot?filter=interactive&format=compact")
MORE_REF=$(printf '%s' "$SNAP" | python3 -c 'import re,sys; print(re.search(r"\"More\" @(e\\d+)", sys.stdin.read()).group(1))')
curl -s -H "X-Kuri-Session: $SESSION" "$BASE/action?action=click&ref=$MORE_REF"
curl -s -H "X-Kuri-Session: $SESSION" "$BASE/page/info"
```
También hay un wrapper experimental ligero en `tools/kuri_harness.py` si quieres helpers de Python sobre la misma superficie HTTP.
Si ya tienes Chrome ejecutándose con depuración remota, configura `CDP_URL` con el endpoint WebSocket o HTTP:
```bash
CDP_URL=ws://127.0.0.1:9222/devtools/browser/... ./zig-out/bin/kuri
# or
CDP_URL=http://127.0.0.1:9222 ./zig-out/bin/kuri
```
### Navega por vercel.com en 4 comandos
```bash
# 1. Discover Chrome tabs
curl -s http://localhost:8080/discover
# → {"discovered":1,"total_tabs":1}
# 2. Get tab ID
curl -s http://localhost:8080/tabs
# → [{"id":"ABC123","url":"chrome://newtab/","title":"New Tab"}]
# 3. Navigate
curl -s "http://localhost:8080/navigate?tab_id=ABC123&url=https://vercel.com"
# 4. Get accessibility snapshot (token-optimized for LLMs)
curl -s "http://localhost:8080/snapshot?tab_id=ABC123&filter=interactive"
# → [{"ref":"e0","role":"link","name":"VercelLogotype"},
# {"ref":"e1","role":"button","name":"Ask AI"}, ...]
```
---
## 🌐 API HTTP
Todos los endpoints devuelven JSON. Autenticación opcional mediante la variable de entorno `KURI_SECRET`. **135 endpoints** — paridad total con agent-browser y browser-use.
### Núcleo
| Ruta | Descripción |
|------|-------------|
| `GET /health` | Estado del servidor, número de pestañas, versión |
| `GET /tabs` | Lista todas las pestañas registradas |
| `GET /discover` | Autodescubre pestañas de Chrome vía CDP |
| `GET /tab/current` | Obtiene o establece la pestaña actual para un `X-Kuri-Session` |
| `GET /page/info` | URL/título/estado de carga/viewport/scroll en vivo de la pestaña activa |
| `GET /page/state` | Observación compacta de la página: url, título, % de scroll, viewport, recuento de formularios/enlaces/imágenes/inputs |
| `POST /batch` | Ejecuta múltiples comandos en una sola llamada HTTP — devuelve un array de resultados |
| `GET /browdie` | 🌰 (easter egg) |
### Control del navegador
| Ruta | Parámetros | Descripción |
|------|--------|-------------|
| `GET /navigate` | `tab_id`, `url` | Navega la pestaña a la URL |
| `GET /tab/new` | `url`, `activate`, `wait` | Crea una nueva pestaña y opcionalmente la hidrata/establece como actual |
| `GET /tab/close` | `tab_id` | Cierra una pestaña |
| `GET /window/new` | `url`, `activate`, `wait` | Crea un nuevo objetivo de ventana/pestaña |
| `GET /snapshot` | `tab_id`, `filter`, `format` | Snapshot del árbol de accesibilidad con refs `eN`. Usa `filter=interactive&format=compact` para bucles de agente con bajo consumo de tokens. |
| `GET /text` | `tab_id` | Extrae el texto de la página |
| `GET /screenshot` | `tab_id`, `format`, `quality`, `save` | Captura una screenshot (base64); `save=true` escribe el PNG en `STATE_DIR/screenshots` y devuelve `{path,bytes}` en su lugar |
| `GET /screenshot/annotated` | `tab_id` | Screenshot con etiquetas numeradas de elementos |
| `GET /screenshot/diff` | `tab_id`, `baseline` | Diff visual entre la screenshot actual y la de referencia |
| `GET /action` | `tab_id`, `ref`, `action`, `value` | Click/type/fill/select/scroll/hover/dblclick/check/uncheck/blur por ref |
| `GET /evaluate` | `tab_id`, `expression` | Ejecuta JavaScript |
| `GET /evalhandle` | `tab_id`, `expression` | Ejecuta JS, devuelve un handle objectId (no el valor) |
| `GET /close` | `tab_id` | Cierra la pestaña + limpieza |
| `GET /bringtofront` | `tab_id` | Trae la pestaña al frente |
### Acciones
| Ruta | Parámetros | Descripción |
|------|--------|-------------|
| `GET /clear` | `ref` | Limpia el valor del campo de entrada |
| `GET /selectall` | `ref` | Selecciona todo el texto en input/contenteditable |
| `GET /setvalue` | `ref`, `value` | Establece el valor del input directamente (omite los eventos de teclado) |
| `GET /dispatch` | `ref`, `type` | Despacha un evento DOM personalizado en el elemento |
| `GET /boundingbox` | `ref` | Obtiene el rectángulo delimitador del elemento (x, y, width, height, centerX, centerY) |
| `GET /getattribute` | `ref`, `name` | Obtiene el atributo del elemento por nombre |
| `GET /inputvalue` | `ref` | Obtiene el valor actual del elemento de entrada |
| `GET /element/state` | `ref`, `check` | Booleano rápido: `exists`, `visible`, `enabled`, `checked` |
| `GET /find-element` | `text`/`role`/`label`/`placeholder`/`testid` | Localizador semántico — encuentra un elemento sin snapshot |
| `GET /highlight` | `ref` o `selector` | Resalta el elemento con una superposición |
### Ratón y táctil
| Ruta | Parámetros | Descripción |
|------|--------|-------------|
| `GET /mouse/move` | `x`, `y` | Mueve el ratón a las coordenadas |
| `GET /mouse/down` | `x`, `y`, `button` | Pulsa el botón del ratón |
| `GET /mouse/up` | `x`, `y`, `button` | Suelta el botón del ratón |
| `GET /mouse/wheel` | `x`, `y`, `deltaX`, `deltaY` | Scroll con la rueda del ratón |
| `GET /tap` | `x`, `y` | Toque táctil (touchStart + touchEnd) |
| `GET /swipe` | `startX`, `startY`, `endX`, `endY` | Gesto de deslizamiento táctil |
| `GET /drag` | `src_ref`, `tgt_ref` | Arrastra el elemento hasta el objetivo |
### Teclado
| Ruta | Parámetros | Descripción |
|------|--------|-------------|
| `GET /keyboard/type` | `tab_id`, `text` | Escribe texto mediante eventos de teclado |
| `GET /keyboard/inserttext` | `tab_id`, `text` | Inserta texto directamente |
| `GET /keydown` | `tab_id`, `key` | Evento de tecla pulsada |
| `GET /keyup` | `tab_id`, `key` | Evento de tecla liberada |
### Extracción de contenido
| Ruta | Descripción |
|------|-------------|
| `GET /markdown` | Convierte la página a Markdown |
| `GET /links` | Extrae todos los enlaces |
| `GET /dom/query` | Consulta con selector CSS |
| `GET /dom/html` | Obtiene el HTML del elemento |
| `GET /dom/attributes` | Obtiene los atributos del elemento |
| `GET /pdf` | Imprime la página a PDF |
| `GET /find` | Búsqueda de texto en la página |
### Esperas
| Ruta | Parámetros | Descripción |
|------|--------|-------------|
| `GET /wait` | `selector`, `text`, `url`, `state`, `visible`, `timeout` | Espera a un selector/texto/patrón de URL/networkidle/estado de carga |
| `GET /wait/function` | `expression`, `timeout` | Espera a que una expresión JS arbitraria sea verdadera |
| `GET /wait/download` | `timeout` | Espera a que se complete la descarga de un archivo |
### Manejo de diálogos
| Ruta | Descripción |
|------|-------------|
| `GET /dialog/auto` | Maneja automáticamente todos los diálogos JS (aceptar o descartar) |
| `GET /dialog/accept` | Acepta el diálogo actual (con texto de prompt opcional) |
| `GET /dialog/dismiss` | Descarta el diálogo actual |
### Red y HAR
| Ruta | Descripción |
|------|-------------|
| `GET /har/start` | Inicia la grabación del tráfico de red |
| `GET /har/stop` | Detiene + devuelve HAR 1.2 JSON |
| `GET /har/status` | Estado de grabación + número de entradas |
| `GET /har/replay` | Mapa de API con fragmentos de código curl/fetch/python |
| `GET /cookies` | Obtiene las cookies |
| `GET /cookies/set` | Establece cookies |
| `GET /cookies/delete` | Elimina cookies |
| `GET /cookies/clear` | Borra todas las cookies |
| `GET /headers` | Establece cabeceras de petición personalizadas |
| `GET /intercept/start` | Inicia la interceptación de peticiones |
| `GET /intercept/stop` | Detiene la interceptación de peticiones |
| `GET /intercept/requests` | Lista las peticiones interceptadas |
| `GET /request/detail` | Obtiene el cuerpo de la respuesta para un ID de petición |
| `GET /response/body` | Obtiene una URL y devuelve el cuerpo de la respuesta |
| `GET /network` | Estadísticas del tráfico de red |
| `GET /download` | Dispara la descarga de un archivo |
### Navegación y estado
| Ruta | Descripción |
|------|-------------|
| `GET /back` | Atrás en el navegador |
| `GET /forward` | Adelante en el navegador |
| `GET /reload` | Recarga la página |
| `GET /stop` | Detiene la carga de la página |
| `GET /pushstate` | Navegación SPA vía history.pushState |
| `GET /storage/local` | Obtiene/establece localStorage |
| `GET /storage/session` | Obtiene/establece sessionStorage |
| `GET /storage/local/clear` | Borra localStorage |
| `GET /storage/session/clear` | Borra sessionStorage |
| `GET /session/save` | Guarda la sesión del navegador |
| `GET /session/load` | Restaura la sesión del navegador |
| `GET /session/list` | Lista las sesiones guardadas |
| `GET /setcontent` | Establece el HTML de la página directamente (POST) |
### Perfiles de autenticación
| Ruta | Descripción |
|------|-------------|
| `GET /auth/profile/save` | Guarda cookies + storage como un perfil de autenticación con nombre |
| `GET /auth/profile/load` | Restaura un perfil de autenticación con nombre en una pestaña |
| `GET /auth/profile/list` | Lista los perfiles de autenticación guardados |
| `GET /auth/profile/delete` | Elimina un perfil de autenticación guardado |
| `GET /auth/extract` | Extrae tokens de autenticación (JWT, cookies, cabeceras) |
| `GET /set/credentials` | Establece credenciales de autenticación básica HTTP |
En macOS, los secretos de los perfiles de autenticación se almacenan en el Keychain del usuario.
### Emulación
| Ruta | Parámetros | Descripción |
|------|--------|-------------|
| `GET /emulate` | tipo de dispositivo, tamaño de pantalla | Emulación de dispositivo |
| `GET /set/viewport` | `width`, `height` | Establece el tamaño del viewport |
| `GET /set/useragent` | `ua` | Establece el user agent |
| `GET /set/media` | `media` | Emula el tipo de medio |
| `GET /set/offline` | `offline` | Activa/desactiva el modo offline |
| `GET /geolocation` | `lat`, `lng` | Sobrescribe la geolocalización |
| `GET /timezone` | `timezone` | Sobrescribe la zona horaria (p. ej. `America/New_York`) |
| `GET /locale` | `locale` | Sobrescribe la configuración regional (p. ej. `en-US`) |
| `GET /permissions` | `name`, `state` | Concede/deniega permisos (geolocalización, notificaciones, portapapeles) |
### Scripts e inyección
| Ruta | Descripción |
|------|-------------|
| `GET /script/inject` | Inyecta JavaScript en la página (persiste entre navegaciones) |
| `GET /initscript/remove` | Elimina un script de inicialización previamente inyectado |
| `GET /addstyle` | Inyecta una hoja de estilos CSS |
| `GET /expose` | Expone una función con nombre al contexto JS de la página |
### Inspección de React
| Ruta | Descripción |
|------|-------------|
| `GET /react/tree` | Árbol de componentes React vía el hook de DevTools |
| `GET /react/inspect` | Props y estado de un componente React |
| `GET /react/renders` | Seguimiento de renders de React (iniciar/detener) |
| `GET /react/suspense` | Estado de los límites de Suspense de React |
### Grabación y rendimiento
| Ruta | Descripción |
|------|-------------|
| `GET /recording/start` | Graba las acciones del usuario (click, input, navigate) |
| `GET /recording/stop` | Detiene la grabación + devuelve el registro de acciones |
| `GET /vitals` | Core Web Vitals (LCP, CLS, FID, TTFB, FCP, domInteractive) |
| `GET /perf/lcp` | Tiempo de Largest Contentful Paint |
| `GET /trace/start` | Inicia el trazado de rendimiento |
| `GET /trace/stop` | Detiene el trazado |
| `GET /profiler/start` | Inicia el perfilador de JS |
| `GET /profiler/stop` | Detiene el perfilador |
### Depuración
| Ruta | Descripción |
|------|-------------|
| `GET /debug/enable` | Activa el HUD de depuración en la página y el modo de congelación opcional |
| `GET /debug/disable` | Desactiva el HUD de depuración en la página |
| `GET /inspect` | Inspección de elementos |
| `GET /errors` | Recopila errores de JS |
| `GET /console` | Lee los registros de la consola |
| `GET /frames` | Lista los frames de la página |
| `GET /frame` | Cambia al contexto del iframe por nombre o URL |
| `GET /mainframe` | Vuelve al frame principal |
| `GET /diff/snapshot` | Diff compacto `+`/`~`/`-` respecto a la llamada anterior para esta pestaña — el bucle de acciones eficiente en tokens (alias de `/snapshot/changes`). Recurre a un snapshot completo con una cabecera `! page replaced` en caso de cambio masivo. |
| `GET /diff/url` | Compara dos URLs en paralelo (navigate, snapshot, diff) |
### Streaming
| Ruta | Descripción |
|------|-------------|
| `GET /screencast/start` | Inicia la grabación de pantalla |
| `GET /screencast/stop` | Detiene la grabación de pantalla |
| `GET /video/start` | Inicia la captura de vídeo |
| `GET /video/stop` | Detiene la captura de vídeo |
| `GET /ws/start` | Inicia el túnel WebSocket |
| `GET /ws/stop` | Detiene el túnel WebSocket |
### Bucle amigable para agentes
El bucle de servidor con menor fricción es:
1. `GET /tab/new?url=...`
2. `GET /page/state` (ligero) o `GET /snapshot?filter=interactive&format=compact` (completo)
3. `GET /action?action=click&ref=eN`
4. Repetir — o usar `POST /batch` para operaciones de varios pasos en una sola llamada
Los parámetros de consulta `url` y `expression` se decodifican en porcentaje. Envía `X-Kuri-Session: my-agent` para persistir el contexto de la pestaña en el lado del servidor.
---
## 🧠 Skills
El repositorio incluye un área de skills extensible por el usuario:
- `skills/kuri-skill.md` es la skill base de agente HTTP de Kuri
- `skills/custom/` está reservado para tus propias skills específicas del proyecto
- `skills/custom/hackernews-page-2.md` es un ejemplo concreto de skill personalizada
- `.claude/skills/kuri-server/SKILL.md` se mantiene sincronizado para las skills de repositorio al estilo Claude
La skill base ahora también explica qué ruta de navegador usar:
- `kuri` API HTTP: automatización de Chrome/CDP en producción con sesiones, snapshots, acciones, HAR, cookies y screenshots
- `kuri-fetch`: extracción de texto/fetch independiente sin Chrome
- `kuri-browse`: navegación interactiva en terminal
- `kuri-agent`: automatización CLI programable contra el servidor Kuri
- `kuri-browser/`: runtime de navegador nativo en Zig experimental y separado para trabajo de paridad
Para la CLI del navegador experimental:
```bash
cd kuri-browser
zig build run -- render https://news.ycombinator.com --selector ".titleline a" --dump text
zig build run -- render https://todomvc.com/examples/react/dist/ --js --wait-eval "document.querySelectorAll('.todo-list li').length >= 1"
zig build run -- parity --offline
zig build run -- bench --offline
zig build run -- serve-cdp --port 9333
```
`kuri-browser serve-cdp` expone el descubrimiento HTTP al estilo de Chrome además de un enrutador JSON-RPC WebSocket mínimo para pruebas de humo del protocolo. La evaluación en tiempo de ejecución devuelve objetos remotos CDP con forma de V8 respaldados por QuickJS; esto no añade una dependencia de V8 y todavía no es totalmente compatible con Playwright/Puppeteer.
Las capturas de pantalla en `kuri-browser` actualmente delegan al renderizador principal de Kuri/CDP. Inicia `./zig-out/bin/kuri` primero, luego:
```bash
cd kuri-browser
zig build run -- screenshot https://example.com --out example.jpg --compress --kuri-base http://127.0.0.1:8080
```
`--compress` captura una línea base PNG y un candidato JPEG, escribe el archivo más pequeño e informa el ahorro en bytes. Medición local actual en `https://example.com`: `20,523` bytes PNG a `18,183` bytes JPEG calidad 50, ahorrando `2,340` bytes o `11%`.
### Avanzado
| Ruta | Descripción |
|------|-------------|
| `GET /diff/snapshot` | Delta compacto `+`/`~`/`-` respecto a la instantánea anterior (bucle de acción del agente) |
| `GET /emulate` | Emulación de dispositivo |
| `GET /geolocation` | Establecer geolocalización |
| `POST /upload` | Carga de archivos |
| `GET /script/inject` | Inyectar JavaScript |
| `GET /intercept/start` | Iniciar intercepción de solicitudes |
| `GET /intercept/stop` | Detener intercepción |
| `GET /screenshot/annotated` | Captura de pantalla con anotaciones de elementos |
| `GET /screenshot/diff` | Diferencia visual entre capturas de pantalla |
| `GET /screencast/start` | Iniciar screencast |
| `GET /screencast/stop` | Detener screencast |
| `GET /video/start` | Iniciar grabación de video |
| `GET /video/stop` | Detener grabación de video |
| `GET /console` | Obtener mensajes de consola |
| `GET /stop` | Detener la carga de la página |
| `GET /get` | Obtención HTTP directa (del lado del servidor) |
| `GET /scrollintoview` | Desplazar un elemento referenciado a la vista |
| `GET /drag` | Arrastrar de una referencia a otra |
| `GET /keyboard/type` | Escribir texto con eventos de tecla |
| `GET /keyboard/inserttext` | Insertar texto directamente |
| `GET /keydown` | Despachar un evento keydown |
| `GET /keyup` | Despachar un evento keyup |
| `GET /wait` | Esperar al estado listo o a condiciones de elementos |
| `GET /tab/close` | Cerrar una pestaña |
| `GET /highlight` | Resaltar un elemento por referencia o selector |
| `GET /errors` | Obtener errores de página/runtime |
| `GET /set/offline` | Alternar emulación de red sin conexión |
| `GET /set/media` | Establecer características de medios emuladas |
| `GET /set/credentials` | Establecer credenciales de autenticación básica HTTP |
| `GET /find` | Buscar coincidencias de texto en la página actual |
| `GET /trace/start` | Iniciar el trazado de Chrome |
| `GET /trace/stop` | Detener el trazado y devolver los datos de trazado |
| `GET /profiler/start` | Iniciar el perfilador de JS |
| `GET /profiler/stop` | Detener el perfilador de JS |
| `GET /inspect` | Inspeccionar un elemento o el estado de la página |
| `GET /set/viewport` | Establecer el tamaño del viewport |
| `GET /set/useragent` | Sobrescribir el user agent |
| `GET /dom/attributes` | Obtener atributos de elementos |
| `GET /frames` | Listar el árbol de frames |
| `GET /network` | Inspeccionar el estado/solicitudes de red |
---
## 🛡️ Sigilo y evasión de bots
Kuri aplica parches anti-detección automáticamente al iniciar — no se necesita configuración manual.
### Qué se aplica
- **`Page.addScriptToEvaluateOnNewDocument`** — los parches de sigilo se ejecutan antes que cualquier JS de la página
- **navigator.webdriver = false** — oculta el indicador de automatización a nivel de Chromium (`--disable-blink-features=AutomationControlled`)
- **Suplantación de WebGL/Canvas/AudioContext** — derrota la detección basada en huellas digitales
- **Rotación de UA** — 5 user agents realistas de Chrome/Safari/Firefox
- **chrome.csi/chrome.loadTimes** — stubs para comprobaciones específicas de Akamai
### Detección de bloqueo de bots
Navigate detecta bloqueos automáticamente y devuelve un fallback estructurado:
```bash
curl -s "http://localhost:8080/navigate?tab_id=ABC&url=https://protected-site.com"
# If blocked:
```
---
[Read more](https://github.com/justrach/kuri)