Назад к обновлениям
New releaseJul 25, 2026

kuri v0.4.12

Автоматизация браузера, веб-скрапинг и управление устройствами iOS и Android для AI-агентов. Zig-native, эффективные по токенам CDP-снимки, запись HAR, нативный клиент adb wire-protocol и автономный fetcher.

Поделиться

Kuri

Kuri 🌰

Stable release License Zig node_modules status

Установка```sh

curl -fsSL https://kuri.trilok.ai/download | sh

macOS arm64/x86_64 и Linux x86_64/arm64. Один бинарный файл, без зависимостей времени выполнения.

Прямые загрузки: [macOS arm64](https://kuri.trilok.ai/download/v0.6.0/kuri-v0.6.0-aarch64-macos.tar.gz) · [macOS x86_64](https://kuri.trilok.ai/download/v0.6.0/kuri-v0.6.0-x86_64-macos.tar.gz) · [Linux x86_64](https://kuri.trilok.ai/download/v0.6.0/kuri-v0.6.0-x86_64-linux.tar.gz) · [Linux arm64](https://kuri.trilok.ai/download/v0.6.0/kuri-v0.6.0-aarch64-linux.tar.gz)

---

**Автоматизация браузера и веб-краулинг для ИИ-агентов. Написано на Zig. Ноль Node.js.**

Автоматизация CDP · A11y-снимки · Запись HAR · Автономный загрузчик · Интерактивный браузер в терминале · Агентный CLI · Тестирование безопасности · Управление устройствами iOS и Android

[Быстрый старт](#-quick-start) · [Бенчмарки](#-benchmarks) · [kuri-agent](#-kuri-agent) · [Тестирование безопасности](#-security-testing) · [API](#-http-api) · [Навыки](#-skills) · [Журнал изменений](https://github.com/justrach/kuri/blob/HEAD/CHANGELOG.md)

> **Почему команды переходят на Kuri:** текущие сборки `ReleaseFast` для Apple Silicon остаются менее 2 МБ на бинарник, а свежий повторный прогон Google Flights от 2026-04-23 показал **3 392 токена** на полный цикл `kuri-agent` (`go→snap→click→snap→eval`). Межинструментальные расхождения стоит перепроверять в том же окружении, прежде чем приводить проценты.

---

## Почему Kuri выигрывает для агентов

Большинство инструментов для работы с браузером создавалось для QA-инженеров. Kuri создан для агентных циклов: прочитал страницу, удерживай низкую стоимость токенов, действуй по стабильным рефам и двигайся дальше.

- **135 HTTP-эндпоинтов** — полная совместимость с agent-browser и browser-use, от инспекции React до Core Web Vitals.
- **На 7-12% меньше токенов**, чем у agent-browser, на реальных страницах благодаря формату рефов `@eN` и рендерингу без префиксов.
- **В 44 раза более лёгкие наблюдения** с `/page/state` (48 токенов) против полного снимка (2 124 токена) для той же страницы Google Flights.
- **Пакетное выполнение** — `POST /batch` отправляет N команд за один HTTP-вызов, устраняя N-1 лишних сетевых запросов и N-1 обращений к LLM.
- **Совместимость с React** — достоверные события мыши через CDP и посимвольные события клавиатуры вызывают `onClick` и `onChange` в React 18/19.

### Токены снимков: Google Flights `SIN → TPE`

Свежий повторный прогон от 2026-05-24 в этом воркспейсе, замеренный с помощью `wc -c` и аппроксимации `chars/4`.

| Инструмент / Режим | Символы | ~Токены | Примечание |
|---|---:|---:|---|
| `kuri snap` (полный) | 8,499 | **2,124** | Все узлы + интерактивные рефы |
| `kuri snap` (только интерактивные) | ~3,000 | **~750** | Лучше всего для агентных циклов |
| `kuri /page/state` | 190 | **48** | Лёгкое наблюдение (url, заголовок, scroll%, счётчики) |
| agent-browser snap (оценка) | ~9,183 | **~2,295** | Накладные расходы формата `[ref=e0]` |

### Эффективность токенов: kuri против agent-browser

| Страница | токены kuri | токены agent-browser | Экономия |
|---|---:|---:|---|
| example.com | 40 | 35 | -13% (тривиальная страница, agent-browser пропускает корень) |
| Hacker News | 386 | ~440 | **на 12% меньше** |
| Google Flights SIN→TPE | 2,124 | ~2,295 | **на 7% меньше** |

Экономия достигается за счёт компактного формата kuri:
- рефы `@e0` (3 символа) вместо `[ref=e0]` (9 символов)
- нет префикса `- ` в каждой строке (экономия 2 символа × количество строк)
- та же вложенность, та же фильтрация узлов

### Стоимость полного цикла: `go → snap → click → snap → eval`

| Инструмент | Токенов за цикл |
|---|---:|
| **kuri-agent** | **~3,400** |
| С `/page/state` вместо второго snap | **~1,700** |
| С `POST /batch` (всё за один вызов) | **~1,700** (те же токены, 1 HTTP-вызов вместо 5) |

### kuri против libretto

[libretto](https://github.com/saffron-health/libretto) (Playwright + Node) — ближайший конкурент по стоимости токенов на шаг. Замерено в прямом сравнении 2026-07-04 — тот же Chrome, та же вкладка, реальные подсчёты `tiktoken` `o200k_base` (полная методология и воспроизведение: **[benchmarks/libretto_comparison.md](https://github.com/justrach/kuri/blob/HEAD/benchmarks/libretto_comparison.md)**). Честное распределение:

| Критерий | Победитель | Детали |
|---|---|---|
| Задержка на вызов | **kuri** | 4–117 мс против 1,344–1,500 мс (**в 13–376× быстрее** — постоянный сервер против Node на каждую команду) |
| Токены снимка, типовая страница | **kuri** | простая 61 против 151 (2.5×), статья 265 против 363 (1.37×) — более компактная грамматика |
| Токены снимка, большой список | разделились | по умолчанию kuri 4,424 против 813 — kuri выводит все 259 рефов, libretto по умолчанию обрезает. С `limit=5` kuri выдаёт 555 токенов (**в 1.46× меньше, чем libretto**), 34 рефа + маркеры `… +45 more` |
| Траектория (лента, 9 кликов) | **kuri**, с трудом | 898 против 939 токенов (база `limit=5` + цикл диффов против цикла выполнений) — примерное равенство или лёгкое преимущество; утренний проигрыш 5.1× был из-за необрезанной базы |
| Повторные запуски | **libretto** | компилирует траектории в Playwright-скрипт → воспроизведение за 0 токенов; kuri каждый раз заново оплачивает цикл |

**Что Kuri почерпнул, изучив libretto** (всё вошло в этот релиз): цикл «сначала дифф» (`take_snapshot_diff`, ~38 токенов/шаг); адаптивный дифф, который при навигации переключается на полный снимок с заголовком `! page replaced`; строки удаления только по идентификаторам; скриншоты сохраняются на диск (возвращается путь, байты никогда не попадают в контекст); `get_page_state` через MCP; и — после переписывания `parseA11yNodes` как настоящего DFS-обхода дерева — **опциональное усечение списка** (`/snapshot?limit=N`, одна строка `… +K more` на каждый ограниченный запуск), **повторный захват в заданной области** (`scope=@ref`) и **иерархические отступы**, также доступные как `uid`/`limit` в MCP `take_snapshot`. Траектория из 9 кликов по ленте, которая стоила 44,285 токенов при наивных полных повторных снимках, теперь стоит **898** с усечённой базой + диффами — в 49× дешевле, и это меньше 939 у libretto.

> Таблицы выше используют аппроксимацию токенов `chars/4`; сравнение с libretto использует реальные подсчёты `tiktoken`. Перепроверяйте межинструментальные числа в своём окружении, прежде чем приводить проценты.

### Размер бинарника и память

Замерено на Apple M4 Pro, macOS 26.4.1. Текущие бинарники собраны с `-Doptimize=ReleaseFast`.

| Бинарник | Текущий размер |
|---|---:|
| `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) |

### RSS остался почти неизменным при переходе на Zig 0.16

Замерено на текущей сборке `v0.4.3` `ReleaseFast` с помощью `/usr/bin/time -l`.

| Команда | Средний максимум RSS в `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 |

## Проблема

Любой инструмент автоматизации браузера тащит за собой Playwright (~300 МБ), среду выполнения Node.js и каскад npm-зависимостей. Ваш ИИ-агент просто хочет прочитать страницу, нажать кнопку и двигаться дальше.
**Kuri — это один бинарный файл на Zig.** Четыре режима, ноль рантайма:```
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)

📦 Установка

Установка одной командой (macOS / Linux)```sh

curl -fsSL https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/install.sh | sh

Определяет вашу платформу, загружает нужный бинарный файл, устанавливает в `~/.local/bin`.
Загрузки выполняются из самостоятельно управляемой ветки `release-channel` репозитория Kuri. Бинарные файлы для macOS подписаны локально сертификатом Developer ID. Релизные артефакты GitHub Release содержат те же самые tar-архивы.

### bun / npm```sh
bun install -g kuri-agent
# or: npm install -g kuri-agent

Загружает правильный нативный бинарный файл для вашей платформы при установке.

Канал релизов

Стабильные бинарные файлы Kuri находятся в ветке release-channel и обслуживаются напрямую через raw-URL-адреса GitHub.

  • Стабильный установщик: https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/install.sh
  • Стабильный манифест: https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/latest.json
  • Просмотр ветки: https://github.com/justrach/kuri/tree/release-channel/stable
  • Шаблон прямой загрузки: https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/<version>/kuri-<version>-<target>.tar.gz

Ручная установка

Загрузите tar-архив для вашей платформы из стабильного манифеста релизов или со страницы релизов GitHub и распакуйте его в ваш $PATH.

Стабильный URL установки:```sh curl -fsSL https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/install.sh | sh

Манифест включает точные URL-адреса ресурсов и контрольные суммы SHA-256 для `aarch64-linux`, `x86_64-linux`, `aarch64-macos` и `x86_64-macos`.

### Поддержка платформ

| Платформа | Статус |
|---|---|
| macOS (`aarch64`, `x86_64`) | Готовые бинарники, подписаны и нотаризованы |
| Linux (`aarch64`, `x86_64`) | Готовые бинарники |
| Windows (`x86_64`) | **Экспериментально — только кросскомпиляция.** `zig build -Dtarget=x86_64-windows-gnu` проверяется в CI, но автоматизация Chrome, демонизация, завершение по сигналу, запись HAR и хранилище аутентификации на основе файлов заглушены через `error.UnsupportedOnWindows` во время выполнения. Используйте **WSL2**, если вам нужен полный набор функций. Отслеживается в [#153](https://github.com/justrach/kuri/issues/153). |

Kuri опирается на POSIX-примитивы (`fork`, `clock_gettime`, сырые сокеты) в нескольких местах, поэтому полноценный нативный порт для Windows — это серьёзная работа. Указанный выше уровень компиляции позволяет запускать пути `--version`/`--help` и чистые операции в памяти; сложные части (Chrome, сокеты, демонизация) требуют полноценных реализаций Win32, прежде чем их можно будет убрать из списка заглушек. Если вы хотите взяться за что-то из этого, +1 к [#153](https://github.com/justrach/kuri/issues/153) или откройте PR.

### Сборка из исходников

Требуется [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

⚡ Быстрый старт

Требования: Zig ≥ 0.16.0 · Chrome/Chromium (для 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)

### Первый запуск, кратчайший путь```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

Цикл агента с приоритетом сессии

Для HTTP-использования в стиле агента отдавайте предпочтение заголовку сессии вместе с /tab/new, /page/info и /snapshot вместо повторения tab_id при каждом вызове.```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"

Также есть тонкая экспериментальная обёртка в `tools/kuri_harness.py`, если вам нужны Python-хелперы поверх того же HTTP-интерфейса.

Если у вас уже запущен Chrome с удалённой отладкой, задайте `CDP_URL`, указав WebSocket- или HTTP-endpoint:```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

Просматривайте vercel.com в 4 команды```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"}, ...]

---

## 🌐 HTTP API

Все конечные точки возвращают JSON. Необязательная аутентификация через переменную окружения `KURI_SECRET`. **135 эндпоинтов** — полное соответствие agent-browser и browser-use.

### Core

| Path | Description |
|------|-------------|
| `GET /health` | Статус сервера, количество вкладок, версия |
| `GET /tabs` | Список всех зарегистрированных вкладок |
| `GET /discover` | Автоматическое обнаружение вкладок Chrome через CDP |
| `GET /tab/current` | Получить или установить текущую вкладку для `X-Kuri-Session` |
| `GET /page/info` | Актуальные URL/заголовок/ready-state/viewport/прокрутка для активной вкладки |
| `GET /page/state` | Компактное наблюдение за страницей: url, заголовок, scroll%, viewport, количество форм/ссылок/изображений/полей ввода |
| `POST /batch` | Выполняет несколько команд за один HTTP-вызов — возвращает массив результатов |
| `GET /browdie` | 🌰 (пасхальное яйцо) |

### Browser Control

| Path | Params | Description |
|------|--------|-------------|
| `GET /navigate` | `tab_id`, `url` | Переход вкладки по URL |
| `GET /tab/new` | `url`, `activate`, `wait` | Создать новую вкладку и при необходимости инициализировать/установить её текущей |
| `GET /tab/close` | `tab_id` | Закрыть вкладку |
| `GET /window/new` | `url`, `activate`, `wait` | Создать новое окно/цель вкладки |
| `GET /snapshot` | `tab_id`, `filter`, `format` | Снимок дерева доступности (a11y) со ссылками `eN`. Используйте `filter=interactive&format=compact` для агентных циклов с низким расходом токенов. |
| `GET /text` | `tab_id` | Извлечь текст страницы |
| `GET /screenshot` | `tab_id`, `format`, `quality`, `save` | Сделать скриншот (base64); при `save=true` PNG сохраняется в `STATE_DIR/screenshots`, вместо этого возвращается `{path,bytes}` |
| `GET /screenshot/annotated` | `tab_id` | Скриншот с нумерованными метками элементов |
| `GET /screenshot/diff` | `tab_id`, `baseline` | Визуальное различие между текущим и базовым скриншотами |
| `GET /action` | `tab_id`, `ref`, `action`, `value` | Клик/ввод/заполнение/выбор/прокрутка/hover/двойной клик/установка/снятие флажка/снятие фокуса по ref |
| `GET /evaluate` | `tab_id`, `expression` | Выполнить JavaScript |
| `GET /evalhandle` | `tab_id`, `expression` | Выполнить JS, вернуть handle objectId (не значение) |
| `GET /close` | `tab_id` | Закрыть вкладку + очистка |
| `GET /bringtofront` | `tab_id` | Вывести вкладку на передний план |

### Actions

| Path | Params | Description |
|------|--------|-------------|
| `GET /clear` | `ref` | Очистить значение поля ввода |
| `GET /selectall` | `ref` | Выбрать весь текст в поле ввода/contenteditable |
| `GET /setvalue` | `ref`, `value` | Установить значение поля напрямую (в обход событий клавиатуры) |
| `GET /dispatch` | `ref`, `type` | Отправить пользовательское DOM-событие на элемент |
| `GET /boundingbox` | `ref` | Получить ограничивающий прямоугольник элемента (x, y, width, height, centerX, centerY) |
| `GET /getattribute` | `ref`, `name` | Получить атрибут элемента по имени |
| `GET /inputvalue` | `ref` | Получить текущее значение элемента ввода |
| `GET /element/state` | `ref`, `check` | Быстрая проверка: `exists`, `visible`, `enabled`, `checked` |
| `GET /find-element` | `text`/`role`/`label`/`placeholder`/`testid` | Семантический локатор — поиск элемента без снимка |
| `GET /highlight` | `ref` or `selector` | Подсветить элемент оверлеем |

### Mouse & Touch

| Path | Params | Description |
|------|--------|-------------|
| `GET /mouse/move` | `x`, `y` | Переместить мышь в координаты |
| `GET /mouse/down` | `x`, `y`, `button` | Нажать кнопку мыши |
| `GET /mouse/up` | `x`, `y`, `button` | Отпустить кнопку мыши |
| `GET /mouse/wheel` | `x`, `y`, `deltaX`, `deltaY` | Прокрутка колесом мыши |
| `GET /tap` | `x`, `y` | Касание (touchStart + touchEnd) |
| `GET /swipe` | `startX`, `startY`, `endX`, `endY` | Жест смахивания |
| `GET /drag` | `src_ref`, `tgt_ref` | Перетащить элемент к цели |

### Keyboard

| Path | Params | Description |
|------|--------|-------------|
| `GET /keyboard/type` | `tab_id`, `text` | Ввести текст через клавиатурные события |
| `GET /keyboard/inserttext` | `tab_id`, `text` | Вставить текст напрямую |
| `GET /keydown` | `tab_id`, `key` | Событие нажатия клавиши |
| `GET /keyup` | `tab_id`, `key` | Событие отпускания клавиши |

### Content Extraction

| Path | Description |
|------|-------------|
| `GET /markdown` | Преобразовать страницу в Markdown |
| `GET /links` | Извлечь все ссылки |
| `GET /dom/query` | Запрос CSS-селектора |
| `GET /dom/html` | Получить HTML элемента |
| `GET /dom/attributes` | Получить атрибуты элемента |
| `GET /pdf` | Вывести страницу в PDF |
| `GET /find` | Поиск текста на странице |

### Waiting

| Path | Params | Description |
|------|--------|-------------|
| `GET /wait` | `selector`, `text`, `url`, `state`, `visible`, `timeout` | Ожидание селектора/текста/шаблона URL/networkidle/состояния загрузки |
| `GET /wait/function` | `expression`, `timeout` | Ожидание, пока произвольное JS-выражение не станет истинным |
| `GET /wait/download` | `timeout` | Ожидание завершения загрузки файла |

### Dialog Handling

| Path | Description |
|------|-------------|
| `GET /dialog/auto` | Автоматическая обработка всех JS-диалогов (принять или отклонить) |
| `GET /dialog/accept` | Принять текущий диалог (с необязательным текстом подсказки) |
| `GET /dialog/dismiss` | Отклонить текущий диалог |

### Network & HAR

| Path | Description |
|------|-------------|
| `GET /har/start` | Начать запись сетевого трафика |
| `GET /har/stop` | Остановить и вернуть HAR 1.2 JSON |
| `GET /har/status` | Состояние записи + количество записей |
| `GET /har/replay` | Карта API с примерами кода curl/fetch/python |
| `GET /cookies` | Получить куки |
| `GET /cookies/set` | Установить куки |
| `GET /cookies/delete` | Удалить куки |
| `GET /cookies/clear` | Очистить все куки |
| `GET /headers` | Установить пользовательские заголовки запросов |
| `GET /intercept/start` | Начать перехват запросов |
| `GET /intercept/stop` | Остановить перехват запросов |
| `GET /intercept/requests` | Список перехваченных запросов |
| `GET /request/detail` | Получить тело ответа для ID запроса |
| `GET /response/body` | Загрузить URL и вернуть тело ответа |
| `GET /network` | Статистика сетевого трафика |
| `GET /download` | Инициировать загрузку файла |

### Navigation & State

| Path | Description |
|------|-------------|
| `GET /back` | Назад в браузере |
| `GET /forward` | Вперёд в браузере |
| `GET /reload` | Перезагрузить страницу |
| `GET /stop` | Остановить загрузку страницы |
| `GET /pushstate` | SPA-навигация через history.pushState |
| `GET /storage/local` | Получить/установить localStorage |
| `GET /storage/session` | Получить/установить sessionStorage |
| `GET /storage/local/clear` | Очистить localStorage |
| `GET /storage/session/clear` | Очистить sessionStorage |
| `GET /session/save` | Сохранить сессию браузера |
| `GET /session/load` | Восстановить сессию браузера |
| `GET /session/list` | Список сохранённых сессий |
| `GET /setcontent` | Установить HTML страницы напрямую (POST) |

### Auth Profiles

| Path | Description |
|------|-------------|
| `GET /auth/profile/save` | Сохранить куки + хранилище как именованный профиль аутентификации |
| `GET /auth/profile/load` | Восстановить именованный профиль аутентификации во вкладке |
| `GET /auth/profile/list` | Список сохранённых профилей аутентификации |
| `GET /auth/profile/delete` | Удалить сохранённый профиль аутентификации |
| `GET /auth/extract` | Извлечь токены аутентификации (JWT, cookies, headers) |
| `GET /set/credentials` | Установить учётные данные HTTP Basic Auth |

На macOS секреты профилей аутентификации хранятся в пользовательской связке ключей (Keychain).

### Emulation

| Path | Params | Description |
|------|--------|-------------|
| `GET /emulate` | device type, screen size | Эмуляция устройства |
| `GET /set/viewport` | `width`, `height` | Установить размер области просмотра |
| `GET /set/useragent` | `ua` | Установить user agent |
| `GET /set/media` | `media` | Эмулировать тип медиа |
| `GET /set/offline` | `offline` | Переключить автономный режим |
| `GET /geolocation` | `lat`, `lng` | Переопределить геолокацию |
| `GET /timezone` | `timezone` | Переопределить часовой пояс (например, `America/New_York`) |
| `GET /locale` | `locale` | Переопределить локаль (например, `en-US`) |
| `GET /permissions` | `name`, `state` | Разрешить/запретить разрешения (геолокация, уведомления, буфер обмена) |

### Scripts & Injection

| Path | Description |
|------|-------------|
| `GET /script/inject` | Внедрить JavaScript в страницу (сохраняется при переходах) |
| `GET /initscript/remove` | Удалить ранее внедрённый init-скрипт |
| `GET /addstyle` | Внедрить CSS-таблицу стилей |
| `GET /expose` | Предоставить именованную функцию в JS-контексте страницы |

### React Inspection

| Path | Description |
|------|-------------|
| `GET /react/tree` | Дерево React-компонентов через хук DevTools |
| `GET /react/inspect` | Свойства и состояние React-компонента |
| `GET /react/renders` | Отслеживание рендеринга React (запуск/остановка) |
| `GET /react/suspense` | Статус границы React Suspense |

### Recording & Performance

| Path | Description |
|------|-------------|
| `GET /recording/start` | Запись действий пользователя (клик, ввод, навигация) |
| `GET /recording/stop` | Остановить запись и вернуть журнал действий |
| `GET /vitals` | Основные показатели Web Vitals (LCP, CLS, FID, TTFB, FCP, domInteractive) |
| `GET /perf/lcp` | Время Largest Contentful Paint |
| `GET /trace/start` | Начать трассировку производительности |
| `GET /trace/stop` | Остановить трассировку |
| `GET /profiler/start` | Запустить JS-профилировщик |
| `GET /profiler/stop` | Остановить профилировщик |

### Debugging

| Path | Description |
|------|-------------|
| `GET /debug/enable` | Включить внутристраничный отладочный HUD и необязательный режим заморозки |
| `GET /debug/disable` | Отключить внутристраничный отладочный HUD |
| `GET /inspect` | Инспекция элемента |
| `GET /errors` | Собрать JS-ошибки |
| `GET /console` | Читать логи консоли |
| `GET /frames` | Список фреймов страницы |
| `GET /frame` | Переключиться в контекст iframe по имени или URL |
| `GET /mainframe` | Вернуться в главный фрейм |
| `GET /diff/snapshot` | Компактный diff `+`/`~`/`-` по сравнению с предыдущим вызовом для этой вкладки — эффективный по токенам цикл действий (алиас `/snapshot/changes`). При массовых изменениях возвращается полный снимок с заголовком `! page replaced`. |
| `GET /diff/url` | Сравнить два URL бок о бок (навигация, снимок, diff) |

### Streaming

| Path | Description |
|------|-------------|
| `GET /screencast/start` | Начать запись экрана |
| `GET /screencast/stop` | Остановить запись экрана |
| `GET /video/start` | Начать захват видео |
| `GET /video/stop` | Остановить захват видео |
| `GET /ws/start` | Запуск WebSocket-туннеля |
| `GET /ws/stop` | Остановка WebSocket-туннеля |

### Agent-friendly loop

Самый простой серверный цикл:

1. `GET /tab/new?url=...`
2. `GET /page/state` (лёгкий) или `GET /snapshot?filter=interactive&format=compact` (полный)
3. `GET /action?action=click&ref=eN`
4. Повторяйте — или используйте `POST /batch` для многошаговых операций за один вызов

Параметры запроса `url` и `expression` декодируются в percent-encoding. Отправьте `X-Kuri-Session: my-agent`, чтобы сохранять контекст вкладки на стороне сервера.
---

## 🧠 Навыки

Репозиторий включает расширяемую пользователем область навыков:

- `skills/kuri-skill.md` — базовый навык Kuri для HTTP-агента
- `skills/custom/` — зарезервирована для ваших собственных навыков проекта
- `skills/custom/hackernews-page-2.md` — конкретный пример пользовательского навыка
- `.claude/skills/kuri-server/SKILL.md` — остаётся синхронизированным для навыков репозитория в стиле Claude

Базовый навык теперь также объясняет, какой путь к браузеру использовать:

- `kuri` HTTP API: продакшн-автоматизация Chrome/CDP с сессиями, снимками, действиями, HAR, куками и скриншотами
- `kuri-fetch`: автономное извлечение fetch/текста без Chrome
- `kuri-browse`: интерактивный просмотр в терминале
- `kuri-agent`: скриптовая CLI-автоматизация через сервер Kuri
- `kuri-browser/`: экспериментальная отдельная Zig-нативная среда выполнения браузера для работы над паритетом

Для экспериментального CLI браузера:```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 предоставляет обнаружение HTTP в стиле Chrome и минимальный роутер WebSocket JSON-RPC для дымовых тестов протокола. Выполнение eval в рантайме возвращает удалённые объекты CDP в формате V8 на базе QuickJS; это не добавляет зависимость от V8 и пока не является полной совместимостью с Playwright/Puppeteer.

Скриншоты в kuri-browser в настоящее время делегируются основному рендереру Kuri/CDP. Сначала запустите ./zig-out/bin/kuri, затем:```bash cd kuri-browser zig build run -- screenshot https://example.com --out example.jpg --compress --kuri-base http://127.0.0.1:8080

`--compress` создаёт эталонный PNG и кандидат JPEG, записывает файл меньшего размера и сообщает о сэкономленных байтах. Текущее локальное измерение на `https://example.com`: `20,523` байт PNG против `18,183` байт JPEG качеством 50, экономия `2,340` байт или `11%`.

### Расширенные

| Path | Описание |
|------|-------------|
| `GET /diff/snapshot` | Компактная дельта `+`/`~`/`-` по сравнению с предыдущим снимком (цикл действий агента) |
| `GET /emulate` | Эмуляция устройства |
| `GET /geolocation` | Установка геолокации |
| `POST /upload` | Загрузка файла |
| `GET /script/inject` | Внедрение JavaScript |
| `GET /intercept/start` | Запуск перехвата запросов |
| `GET /intercept/stop` | Остановка перехвата |
| `GET /screenshot/annotated` | Снимок экрана с аннотациями элементов |
| `GET /screenshot/diff` | Визуальная разница между снимками экрана |
| `GET /screencast/start` | Запуск скринкаста |
| `GET /screencast/stop` | Остановка скринкаста |
| `GET /video/start` | Запуск записи видео |
| `GET /video/stop` | Остановка записи видео |
| `GET /console` | Получить сообщения консоли |
| `GET /stop` | Остановить загрузку страницы |
| `GET /get` | Прямой HTTP-запрос (на стороне сервера) |
| `GET /scrollintoview` | Прокрутить страницу до указанного элемента |
| `GET /drag` | Перетаскивание от одного ref к другому |
| `GET /keyboard/type` | Ввод текста с событиями клавиш |
| `GET /keyboard/inserttext` | Вставка текста напрямую |
| `GET /keydown` | Отправка события keydown |
| `GET /keyup` | Отправка события keyup |
| `GET /wait` | Ожидание готовности страницы или состояния элемента |
| `GET /tab/close` | Закрыть вкладку |
| `GET /highlight` | Подсветить элемент по ref или селектору |
| `GET /errors` | Получить ошибки страницы или времени выполнения |
| `GET /set/offline` | Переключение эмуляции офлайн-сети |
| `GET /set/media` | Установка эмулируемых медиа-функций |
| `GET /set/credentials` | Установка учётных данных HTTP Basic Auth |
| `GET /find` | Поиск совпадений текста на текущей странице |
| `GET /trace/start` | Запуск трассировки Chrome |
| `GET /trace/stop` | Остановка трассировки и возврат данных трассировки |
| `GET /profiler/start` | Запуск JS-профилировщика |
| `GET /profiler/stop` | Остановка JS-профилировщика |
| `GET /inspect` | Проверка элемента или состояния страницы |
| `GET /set/viewport` | Установка размера окна просмотра |
| `GET /set/useragent` | Переопределение user agent |
| `GET /dom/attributes` | Получить атрибуты элемента |
| `GET /frames` | Список дерева фреймов |
| `GET /network` | Проверить состояние сети/запросы |

---

## 🛡️ Стелс и обход ботов

Kuri автоматически применяет патчи против обнаружения при запуске — ручная настройка не требуется.

### Что применяется

- **`Page.addScriptToEvaluateOnNewDocument`** — стелс-патчи выполняются до любого JS на странице
- **navigator.webdriver = false** — скрывает флаг автоматизации на уровне Chromium (`--disable-blink-features=AutomationControlled`)
- **WebGL/Canvas/AudioContext спуфинг** — обходит детектирование на основе отпечатков
- **Ротация UA** — 5 реалистичных user agents Chrome/Safari/Firefox
- **chrome.csi/chrome.loadTimes** — заглушки для проверок Akamai

### Определение блокировки ботов

Navigate автоматически определяет блокировки и возвращает структурированный запасной ответ:```bash
curl -s "http://localhost:8080/navigate?tab_id=ABC&url=https://protected-site.com"
# If blocked:
# {"blocked":true,"blocker":"akamai","ref_code":"0.7d...",
#  "fallback":{"suggestions":["Open URL directly in browser","Use KURI_PROXY"]}}
# If ok: normal CDP response

Обнаруживает: Akamai, Cloudflare, PerimeterX, DataDome, обычную капчу.

Поддержка прокси```bash

KURI_PROXY=socks5://user:pass@residential-proxy:1080 ./zig-out/bin/kuri KURI_PROXY=http://proxy:8080 ./zig-out/bin/kuri

### Проверенные сайты

| Site | Защита | Результат |
|------|-----------|--------|
| Singapore Airlines | Akamai WAF | ✅ Обход (до v0.4.0 было заблокировано) |
| Shopee SG | Собственная антифрод-защита | ✅ Страница загружается, перенаправляет на вход |
| Google Flights | Нет | ✅ Полное взаимодействие |
| Booking.com | PerimeterX | ⚠️ Требуется прокси |

---

## 🔧 kuri-fetch

Автономный HTTP-загрузчик — без Chrome, без Playwright, без npm. Поставляется в виде бинарного файла размером ~2 МБ со встроенным QuickJS для выполнения JS.```bash
zig build fetch    # build + run

# Default: convert to Markdown
kuri-fetch https://example.com

# Extract links
kuri-fetch -d links https://news.ycombinator.com

# Structured JSON output
kuri-fetch --json https://example.com

# Execute inline scripts via QuickJS
kuri-fetch --js https://example.com

# Write to file, quiet mode
kuri-fetch -o page.md -q https://example.com

# Pipe-friendly: content → stdout, status → stderr
kuri-fetch -d text https://example.com | wc -w

Возможности

  • 5 режимов выводаmarkdown, html, links, text, json
  • Движок JavaScript QuickJS--js выполняет встроенные теги <script>
  • Заглушки DOMdocument.querySelector, getElementById, window.location, document.title, console.log, setTimeout (в стиле SSR)
  • Защита от SSRF — блокирует приватные IP-адреса, метаданные-эндпоинты, схемы, отличные от HTTP
  • Цветной вывод — учитывает NO_COLOR, TERM=dumb, --no-color, определение TTY
  • Вывод в файл-o / --output с количеством байт и сводкой по времени
  • Пользовательский UA — флаг --user-agent
  • Тихий режим-q подавляет статус в stderr

🌐 kuri-browse

Интерактивный браузер в терминале — просматривайте веб-страницы прямо из терминала. Chrome не нужен.```bash zig build browse # build + run

kuri-browse https://example.com

Please provide the Markdown content to translate.```
🌰 kuri-browse — terminal browser
→ loading https://example.com

# Example Domain
This domain is for use in documentation examples...
Learn more [1]

───── Links ─────
  [1] https://iana.org/domains/example

✓ 528 bytes, 1 links (133ms)
[nav] https://example.com> 1     ← type 1 to follow the link

Команды

КомандаДействие
<number>Перейти по ссылке [N]
<url>Перейти (если содержит .)
:go <url>Перейти по URL
:back, :bНазад в истории
:forward, :fВперёд
:reload, :rПовторно загрузить текущую страницу
:links, :lПоказать индекс ссылок
/<term>Искать на странице (подсвечивает совпадения)
:search <t>Искать на странице
:n, :nextПовторно подсветить результаты поиска
:historyПоказать историю навигации
:help, :hПоказать все команды
:quit, :qВыйти

Возможности

  • Цветной рендеринг Markdown — заголовки, ссылки, блоки кода, жирный текст, цитаты
  • Нумерованные ссылки — каждая ссылка получает [N], введите номер, чтобы перейти по ней
  • История навигации — назад/вперёд как в настоящем браузере
  • Поиск по странице/term подсвечивает все совпадения
  • Разрешение относительных URL — естественно переходит по ссылкам между страницами
  • Умная фильтрация — пропускает ссылки javascript: и mailto:

🤖 kuri-agent

Скриптовый CLI для автоматизации Chrome — управляет браузером команда за командой из вашего терминала или shell-скриптов. Сохраняет состояние сеанса между вызовами через ~/.kuri/session.json.```bash zig build agent # build kuri-agent

1. Find a Chrome tab

kuri-agent tabs

→ ws://127.0.0.1:9222/devtools/page/ABC123 https://example.com

2. Attach to it

kuri-agent use ws://127.0.0.1:9222/devtools/page/ABC123

3. Navigate + interact

kuri-agent go https://example.com kuri-agent snap --interactive # → [{"ref":"e0","role":"link","name":"More info"}] kuri-agent click e0 kuri-agent shot # saves ~/.kuri/screenshots/.png

### Команды

| Команда | Описание |
|---------|-------------|
| `tabs [--port N]` | Показать список вкладок Chrome |
| `use <ws_url>` | Подключиться к вкладке (сохраняет сессию) |
| `open [url] [--port N]` | Открыть новую вкладку (опционально перейти по url) |
| `status` | Показать текущую сессию |
| `go <url>` | Перейти по URL |
| `snap [--interactive] [--json] [--text] [--depth N]` | Снимок A11y, сохраняет ссылки `eN` |
| `click <ref>` | Кликнуть по элементу по ref (CDP mouse events, совместимо с React) |
| `type <ref> <text>` | Ввести текст в элемент (посимвольные события клавиш, совместимо с React) |
| `fill <ref> <text>` | Заполнить значение input |
| `select <ref> <value>` | Выбрать опцию из выпадающего списка |
| `hover <ref>` | Навести курсор на элемент |
| `focus <ref>` | Сфокусировать элемент |
| `scroll` | Прокрутить страницу |
| `viewport [width height]` | Получить или установить размеры viewport |
| `eval <js>` | Выполнить JavaScript |
| `text [selector]` | Получить текст страницы |
| `shot [--out file.png]` | Снимок экрана |
| `back` | Назад |
| `forward` | Вперёд |
| `reload` | Перезагрузить текущую страницу |
| `cookies` | Список cookie с флагами безопасности |
| `headers` | Проверить security response headers |
| `audit` | Полная проверка безопасности |
| `storage [local\|session\|all]` | Выгрузить localStorage / sessionStorage |
| `jwt` | Извлечь и декодировать JWT из cookie и хранилища |
| `fetch <method> <url> [--data <json>]` | Аутентифицированный fetch с использованием cookie страницы |
| `probe <url-template> <start> <end>` | IDOR probe: перебор числовых ID в URL |
| `grab <ref>` | Кликнуть по ref, перехватить `window.open`, перейти по redirect во вкладке |
| `wait-for-tab [--port N]` | Ожидать новую вкладку, автоматически переключить сессию |
| `stealth` | Применить патчи антидетекта |
| `set-header <name> <value>` | Добавить пользовательский заголовок ко всем запросам |
| `show-headers` | Показать сохранённые дополнительные заголовки |
| `clear-headers` | Удалить все дополнительные заголовки |
---

## 📱 kuri-mobile (iOS + Android)

Нативный Zig CLI для управления iOS Simulators, реальными iPhone (список + запуск/завершение) и Android-устройствами/эмуляторами — вдохновлен [`mobile-device-mcp`](https://github.com/srmorete/mobile-device-mcp), переписан на Zig без Bun/Node/Gradle/Xcode в процессе сборки.```bash
cd kuri-mobile && zig build && cp zig-out/bin/kuri-mobile ../zig-out/bin/

# The main `kuri` binary forwards android/ios subcommands to kuri-mobile:
kuri ios list-devices                              # sims + real devices (usbmuxd, native)
kuri ios openurl https://example.com               # navigate Safari
kuri ios screenshot out.png                        # auto-picks booted sim
kuri ios launch com.apple.Preferences

kuri android list-devices                          # native Zig adb wire-protocol client
kuri android tap 540 1200
kuri android swipe 100 1500 100 500
kuri android screenshot phone.png
kuri android uitree                                # flat element list via uiautomator dump

Что нативно в Zig: adb host protocol (libc sockets, 4-hex framing поверх host:transport:/shell:/exec:), Android XML UI tree parser, usbmuxd ListDevices plist-клиент. Что вызывает внешние команды: xcrun simctl (iOS Simulator), xcrun devicectl (запуск/завершение на реальных iOS-устройствах). Намеренно без драйверов: на устройство не устанавливается приложение, поэтому run_code-песочницы и tap/uitree на базе XCUITest на реальных iOS-устройствах намеренно недоступны. См. kuri-mobile/README.md для полной матрицы соответствия по сравнению с upstream.


🔒 Security Testing

kuri-agent поддерживает браузерные сценарии безопасности — войдите один раз, а затем выполняйте разведку и аудит заголовков/куки, не выходя из терминала.

Сценарии

Enumerate → Inspect — после аутентификации выгрузите auth cookies и проверьте security flags:```bash kuri-agent go https://target.example.com/login kuri-agent snap --interactive kuri-agent fill e2 myuser kuri-agent fill e3 mypassword kuri-agent click e4 # submit login

kuri-agent cookies

cookies (3):

session_id domain=.example.com path=/ [Secure] [HttpOnly] [SameSite=Strict]

csrf_token domain=.example.com path=/ [Secure] [!HttpOnly]

tracking domain=.example.com path=/ [!Secure] [!HttpOnly]

**Аудит заголовков** — проверка, какие заголовки безопасности отправляет цель:```bash
kuri-agent go https://target.example.com
kuri-agent headers
# → {"url":"https://...","status":200,"headers":{
#     "content-security-policy":"default-src 'self'",
#     "strict-transport-security":"max-age=31536000",
#     "x-frame-options":"(missing)",
#     "x-content-type-options":"nosniff", ...}}

Полный аудит — HTTPS, отсутствующие заголовки, cookies, видимые через JS, за один раз:```bash kuri-agent audit

→ {"protocol":"https:","url":"https://...","score":6,

"issues":["MISSING:x-frame-options","COOKIES_EXPOSED_TO_JS:2"],

"headers":{"content-security-policy":"default-src 'self'", ...}}

**Межаккаунтная траектория** — используйте `eval` для повторного воспроизведения вызовов API с другими токенами:```bash
# After login, grab the auth token from localStorage
kuri-agent eval "localStorage.getItem('token')"

# Probe a resource ID with the current session
kuri-agent eval "fetch('/api/assessments/42').then(r=>r.status)"

# Check for IDOR: does a different user's resource return 200 or 403?
kuri-agent eval "fetch('/api/assessments/99').then(r=>r.status)"

Формат отчёта о траектории

kuri-agent выдаёт JSON, подходящий для интеграции в пайплайн. Каждая команда безопасности выдаёт одну строку JSON — пропустите через jq для сортировки:```bash kuri-agent audit | jq '.issues[]' kuri-agent cookies | head -20 kuri-agent headers | jq '.headers | to_entries[] | select(.value == "(missing)") | .key'

---


## 🏗 Архитектура```
┌──────────────────────────────────────────────────────────┐
│                     HTTP API Layer                        │
│         (std.http.Server, thread-per-connection)          │
├──────────────┬──────────────────┬────────────────────────┤
│   Browser    │  Crawler Engine  │   kuri-fetch / browse   │
│   Bridge     │                  │   (standalone CLIs)     │
├──────────────┼──────────────────┼────────────────────────┤
│ CDP Client   │ URL Validator    │ std.http.Client         │
│ Tab Registry │ HTML→Markdown    │ QuickJS JS Engine       │
│ A11y Snapshot│ Link Extractor   │ DOM Stubs (Layer 3)     │
│ Ref Cache    │ Text Extractor   │ SSRF Validator          │
│ HAR Recorder │                  │ Colored Renderer        │
│ Stealth JS   │                  │ History + REPL          │
├──────────────┴──────────────────┴────────────────────────┤
│  Chrome Lifecycle Manager                                 │
│  (launch, health-check, auto-restart, port detection)     │
└──────────────────────────────────────────────────────────┘

Модель памяти

  • Арена на запрос — вся память запроса освобождается одним вызовом deinit()
  • Без GCGeneralPurposeAllocator в режиме отладки ловит каждую утечку
  • Правильные цепочки очисткиLauncher → Bridge → CdpClients → HarRecorders → Snapshots → Tabs
  • Защита через errdefer — частичные сбои откатываются чисто

Жизненный цикл Chrome

РежимПоведение
Управляемый (без CDP_URL)Запускает Chrome в headless-режиме, находит свободный CDP-порт, контролирует, автоматически перезапускает при сбое (максимум 3 попытки), завершает при остановке
Внешний (CDP_URL задан)Подключается к существующему Chrome, проверяет работоспособность через /json/version, НЕ завершает при остановке

📁 Структура```

kuri/ ├── build.zig # Build system (Zig 0.16.0) ├── build.zig.zon # Package manifest + QuickJS dep ├── src/ │ ├── main.zig # CDP server entry point │ ├── fetch_main.zig # kuri-fetch CLI entry point │ ├── browse_main.zig # kuri-browse CLI entry point │ ├── js_engine.zig # QuickJS wrapper + DOM stubs │ ├── bench.zig # Benchmark harness │ ├── chrome/ │ │ └── launcher.zig # Chrome lifecycle manager │ ├── server/ │ │ ├── router.zig # HTTP route dispatch (40+ endpoints) │ │ ├── middleware.zig # Auth (constant-time comparison) │ │ └── response.zig # JSON response helpers │ ├── bridge/ │ │ ├── bridge.zig # Central state (tabs, CDP, HAR, snapshots) │ │ └── config.zig # Env var configuration │ ├── cdp/ │ │ ├── client.zig # CDP WebSocket client │ │ ├── websocket.zig # WebSocket frame codec │ │ ├── protocol.zig # CDP method constants │ │ ├── actions.zig # High-level CDP actions │ │ ├── stealth.zig # Bot detection bypass │ │ └── har.zig # HAR 1.2 recorder │ ├── snapshot/ │ │ ├── a11y.zig # A11y tree with interactive filter │ │ ├── diff.zig # Snapshot delta diffing │ │ └── ref_cache.zig # eN ref → node ID cache │ ├── crawler/ │ │ ├── validator.zig # SSRF defense, URL validation │ │ ├── markdown.zig # HTML → Markdown (SIMD tag counting) │ │ ├── fetcher.zig # Page fetching │ │ ├── extractor.zig # Readability extraction │ │ └── pipeline.zig # Parallel crawl pipeline │ ├── storage/ │ │ ├── local.zig # Local file writer │ │ └── r2.zig # R2/S3 uploader │ ├── util/ │ │ └── json.zig # JSON helpers │ └── test/ │ ├── harness.zig # Test HTTP client │ ├── integration.zig # Integration tests │ └── merjs_e2e.zig # E2E tests ├── js/ │ ├── stealth.js # Bot detection bypass │ └── readability.js # Content extraction ├── kuri-browser/ # Native Zig rendering experiments └── kuri-mobile/ # iOS + Android device control (Zig-native adb + usbmuxd) ├── src/ │ ├── common/ # io helpers, unified UI tree parser │ ├── android/ # adb wire protocol client, driver, CLI │ └── ios/ # simctl, usbmuxd, devicectl, CLI └── README.md # Full parity matrix vs mobile-device-mcp

---

## ⚙️ Конфигурация

| Переменная среды | По умолчанию | Описание |
|---------|---------|-------------|
| `HOST` | `127.0.0.1` | Адрес привязки сервера |
| `PORT` | `8080` | Порт сервера |
| `CDP_URL` | *(none)* | Подключение к существующему Chrome (`ws://...` or `http://127.0.0.1:9222`) |
| `KURI_SECRET` | *(none)* | Секрет аутентификации для API-запросов |
| `STATE_DIR` | `.kuri` | Каталог состояния сессии |
| `REQUEST_TIMEOUT_MS` | `30000` | Таймаут HTTP-запросов |
| `NAVIGATE_TIMEOUT_MS` | `30000` | Таймаут навигации |
| `STALE_TAB_INTERVAL_S` | `30` | Интервал очистки устаревших вкладок |
| `NO_COLOR` | *(none)* | Отключить цветной вывод CLI |

---

## 💰 Стоимость токенов

Для задачи мониторинга 50 страниц (из бенчмарков Pinchtab):

| Метод | Токены | Стоимость ($) | Подходит для |
|--------|--------|----------|----------|
| `/text` | ~40,000 | $0.20 | Чтение (в 13 раз дешевле скриншотов) |
| `/snapshot?filter=interactive&format=compact` | ~40,000 | $0.20 | Взаимодействие с элементами при низком расходе токенов |
| `/snapshot` (full) | ~525,000 | $2.63 | Понимание всей страницы |
| `/screenshot` | ~100,000 | $1.00 | Визуальная проверка |

---

## 🤝 Вклад

Откройте issue перед отправкой крупного PR, чтобы мы могли согласовать подход.```bash
git clone https://github.com/justrach/kuri.git
cd kuri
zig build test         # 252+ tests must pass
zig build test-fetch   # kuri-fetch tests (69 tests)
zig build test-browse  # kuri-browse tests (22 tests)

Рекомендации по внесению вклада см. в CONTRIBUTORS.md.


Благодарности

ПроектЧто мы заимствовали
agent-browserСистема ссылок @eN, диффинг снимков, шаблоны записи HAR
PinchtabАрхитектура управления браузером для ИИ-агентов
PathikШаблоны высокопроизводительного краулинга
QuickJS-ng через mitchellh/zig-quickjs-ngДвижок JS для kuri-fetch
LightpandaПионер безголовых браузеров на Zig, шаблоны совместимости с CDP
Zig 0.16.0Весь стек

Лицензия

Apache-2.0

Категории