
kuri v0.6.0
Browser-Automatisierung, Web-Crawling und iOS- + Android-Gerätesteuerung für KI-Agenten. Zig-native, token-effiziente CDP-Snapshots, HAR-Aufzeichnung, nativer adb-Wire-Protocol-Client und ein eigenständiger Fetcher.
Kuri 🌰
Installation```sh
curl -fsSL https://kuri.trilok.ai/download | sh
macOS arm64/x86_64 und Linux x86_64/arm64. Ein einzelnes Binary, keine Laufzeitabhängigkeiten.
Direkte Downloads: [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)
---
**Browser-Automatisierung & Web-Crawling für KI-Agenten. Geschrieben in Zig. Kein Node.js.**
CDP-Automatisierung · A11y-Snapshots · HAR-Aufzeichnung · Eigenständiger Fetcher · Interaktiver Terminal-Browser · Agentische CLI · Sicherheitstests · iOS + Android-Gerätesteuerung
[Schnellstart](#-quick-start) · [Benchmarks](#-benchmarks) · [kuri-agent](#-kuri-agent) · [Sicherheitstests](#-security-testing) · [API](#-http-api) · [Fähigkeiten](#-skills) · [Changelog](https://github.com/justrach/kuri/blob/HEAD/CHANGELOG.md)
> **Warum Teams auf Kuri umsteigen:** Aktuelle `ReleaseFast`-Builds für Apple Silicon bleiben unter 2 MB pro Binary, und ein frischer Google-Flights-Durchlauf am 2026-04-23 maß **3,392 Token** für eine vollständige `kuri-agent`-Schleife (`go→snap→click→snap→eval`). Toolübergreifende Differenzen sollten in derselben Umgebung neu gemessen werden, bevor ein Prozentsatz genannt wird.
---
## Warum Kuri für Agenten gewinnt
Die meisten Browser-Tools wurden für QA-Ingenieure gebaut. Kuri ist für Agentenschleifen gebaut: Seite lesen, Token-Kosten niedrig halten, auf stabile Referenzen reagieren und weitermachen.
- **135 HTTP-Endpunkte** — volle Parität mit agent-browser und browser-use, von der React-Inspektion bis zu Core Web Vitals.
- **7-12% weniger Token** als agent-browser auf echten Seiten dank `@eN`-Referenzformat und Zero-Prefix-Rendering.
- **44x leichtere Beobachtungen** mit `/page/state` (48 Token) gegenüber vollständigem Snapshot (2,124 Token) für dieselbe Google-Flights-Seite.
- **Batch-Ausführung** — `POST /batch` sendet N Befehle in einem HTTP-Aufruf und eliminiert N-1 Round-Trips und N-1 LLM-Turns.
- **React-kompatibel** — echte CDP-Mausereignisse und zeichenweise Tastaturereignisse lösen React 18/19 `onClick` und `onChange` aus.
### Snapshot-Token: Google Flights `SIN → TPE`
Frischer Durchlauf am 2026-05-24 in diesem Workspace, gemessen mit `wc -c` und `chars/4`-Näherung.
| Tool / Modus | Zeichen | ~Token | Hinweis |
|---|---:|---:|---|
| `kuri snap` (vollständig) | 8,499 | **2,124** | Alle Knoten + interaktive Referenzen |
| `kuri snap` (nur interaktiv) | ~3,000 | **~750** | Am besten für Agentenschleifen |
| `kuri /page/state` | 190 | **48** | Leichte Beobachtung (url, title, scroll%, counts) |
| agent-browser snap (geschätzt) | ~9,183 | **~2,295** | Overhead durch `[ref=e0]`-Format |
### Token-Effizienz: kuri vs. agent-browser
| Seite | kuri-Token | agent-browser-Token | Ersparnis |
|---|---:|---:|---|
| example.com | 40 | 35 | -13% (triviale Seite, agent-browser überspringt Root) |
| Hacker News | 386 | ~440 | **12% weniger** |
| Google Flights SIN→TPE | 2,124 | ~2,295 | **7% weniger** |
Die Ersparnis ergibt sich aus kuris kompaktem Format:
- `@e0`-Referenzen (3 Zeichen) vs. `[ref=e0]` (9 Zeichen)
- Kein `- `-Präfix pro Zeile (spart 2 Zeichen × Zeilenanzahl)
- Gleiche Einrückung, gleiche Knotenfilterung
### Vollständige Workflow-Kosten: `go → snap → click → snap → eval`
| Tool | Token pro Zyklus |
|---|---:|
| **kuri-agent** | **~3,400** |
| Mit `/page/state` statt zweitem Snap | **~1,700** |
| Mit `POST /batch` (alles in einem Aufruf) | **~1,700** (gleiche Token, 1 HTTP-Aufruf statt 5) |
### kuri vs. libretto
[libretto](https://github.com/saffron-health/libretto) (Playwright + Node) ist der engste Konkurrent bei den Token-Kosten pro Schritt. Direkt im Vergleich gemessen am 2026-07-04 — gleiches Chrome, gleicher Tab, echte `tiktoken`-`o200k_base`-Zählungen (vollständige Methodik und Reproduktion: **[benchmarks/libretto_comparison.md](https://github.com/justrach/kuri/blob/HEAD/benchmarks/libretto_comparison.md)**). Die ehrliche Aufteilung:
| Achse | Gewinner | Detail |
|---|---|---|
| Latenz pro Aufruf | **kuri** | 4–117 ms vs. 1,344–1,500 ms (**13–376× schneller** — persistenter Server vs. Node-pro-Befehl) |
| Snapshot-Token, typische Seite | **kuri** | einfach 61 vs. 151 (2.5×), Artikel 265 vs. 363 (1.37×) — straffere Grammatik |
| Snapshot-Token, große Liste | geteilt | kuri Standard 4,424 vs. 813 — kuri emittiert alle 259 Referenzen, libretto kürzt standardmäßig. Mit `limit=5` rendert kuri 555 Token (**1.46× unter libretto**), 34 Referenzen + `… +45 more`-Marker |
| Trajektorie (Feed, 9 Klicks) | **kuri**, knapp | 898 vs. 939 Token (`limit=5`-Basis + Diff-Schleife vs. Exec-Schleife) — Parität bis leichter Vorsprung; der 5.1×-Verlust am Morgen war die ungekürzte Basis |
| Wiederholte Läufe | **libretto** | kompiliert Trajektorien zu einem Playwright-Skript → 0-Token-Wiedergaben; kuri zahlt die Schleife bei jedem Lauf erneut |
**Was kuri aus der Untersuchung von libretto gewonnen hat** (alles in diesem Release enthalten): eine diff-first-Schleife (`take_snapshot_diff`, ~38 Token/Schritt); ein adaptiver Diff, der bei Navigation auf einen vollständigen Snapshot mit `! page replaced`-Header zurückfällt; Entfernungszeilen nur mit Identität; Screenshots, die auf die Festplatte geschrieben werden (Pfad wird zurückgegeben, Bytes gelangen nie in den Kontext); `get_page_state` über MCP; und — nachdem `parseA11yNodes` als echter DFS-Tree-Walk neu geschrieben wurde — **optionale Listenkürzung** (`/snapshot?limit=N`, eine `… +K more`-Zeile pro begrenztem Lauf), **bereichsbezogene Neuerfassung** (`scope=@ref`) und **Hierarchie-Einrückung**, ebenfalls als `uid`/`limit` im MCP-`take_snapshot` verfügbar. Die 9-Klick-Feed-Trajektorie, die mit naiven vollständigen Re-Snapshots 44,285 Token kostete, kostet mit gekürzter Basis + Diffs **898** — 49× günstiger und besser als librettos 939.
> Die älteren Tabellen oben verwenden eine `chars/4`-Token-Näherung; der libretto-Vergleich verwendet echte `tiktoken`-Zählungen. Führen Sie die toolübergreifenden Zahlen in Ihrer eigenen Umgebung erneut aus, bevor Sie einen Prozentsatz angeben.
### Binary-Größe und Speicher
Gemessen auf Apple M4 Pro, macOS 26.4.1. Die aktuellen Binaries wurden mit `-Doptimize=ReleaseFast` erstellt.
| Binary | Aktuelle Größe |
|---|---:|
| `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 blieb über die Zig-0.16-Migration hinweg stabil
Gemessen gegen den aktuellen `v0.4.3`-`ReleaseFast`-Build mit `/usr/bin/time -l`.
| Befehl | `v0.4.3` mittlerer max. RSS |
|---|---:|
| `kuri-fetch --version` | ~2.45 MiB |
| `kuri-browse --version` | ~2.45 MiB |
| `kuri-fetch --quiet --dump markdown http://example.com/` | ~9.17 MiB |
## Das Problem
Jedes Browser-Automatisierungstool schleppt Playwright (~300 MB), eine Node.js-Laufzeit und eine Kaskade von npm-Abhängigkeiten mit. Ihr KI-Agent möchte einfach nur eine Seite lesen, einen Button klicken und weitermachen.
**Kuri ist ein einzelnes Zig-Binary.** Vier Modi, null Laufzeit:```
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)
📦 Installation
Einzeilige Installation (macOS / Linux)```sh
curl -fsSL https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/install.sh | sh
Erkennt deine Plattform, lädt das passende Binary herunter und installiert es in `~/.local/bin`.
Die Downloads stammen aus dem selbstverwalteten `release-channel`-Branch von Kuri. macOS-Binaries sind lokal mit einem Developer-ID-Zertifikat signiert. GitHub-Release-Assets spiegeln dieselben Tarballs wider.
### bun / npm```sh
bun install -g kuri-agent
# or: npm install -g kuri-agent
Lädt zur Installationszeit das richtige native Binary für deine Plattform herunter.
Release-Kanal
Die stabilen Binaries von Kuri liegen auf dem Branch release-channel und werden direkt über GitHub-Raw-URLs ausgeliefert.
- Stabiler Installer:
https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/install.sh - Stabiles Manifest:
https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/latest.json - Branch-Ansicht:
https://github.com/justrach/kuri/tree/release-channel/stable - Direkt-Download-Muster:
https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/<version>/kuri-<version>-<target>.tar.gz
Manuell
Lade das Tarball für deine Plattform aus dem stabilen Release-Manifest oder von der GitHub-Releases-Seite herunter und entpacke es in deinen $PATH.
Stabile Installations-URL:```sh curl -fsSL https://raw.githubusercontent.com/justrach/kuri/release-channel/stable/install.sh | sh
Das Manifest enthält exakte Asset-URLs sowie SHA-256-Prüfsummen für `aarch64-linux`, `x86_64-linux`, `aarch64-macos` und `x86_64-macos`.
### Plattformunterstützung
| Plattform | Status |
|---|---|
| macOS (`aarch64`, `x86_64`) | Vorgefertigte Binärdateien, signiert + notarisiert |
| Linux (`aarch64`, `x86_64`) | Vorgefertigte Binärdateien |
| Windows (`x86_64`) | **Experimentell — nur Cross-Compile.** `zig build -Dtarget=x86_64-windows-gnu` ist CI-verifiziert, aber Chrome-Automatisierung, Dämonisierung, signalbasierter Shutdown, HAR-Aufzeichnung und der dateigestützte Auth-Speicher sind zur Laufzeit alle als Stubs mit `error.UnsupportedOnWindows` implementiert. Verwende **WSL2**, wenn du den echten Funktionsumfang benötigst. Nachverfolgt unter [#153](https://github.com/justrach/kuri/issues/153). |
Kuri stützt sich an mehreren Stellen auf POSIX-Primitive (`fork`, `clock_gettime`, Raw-Sockets), daher ist ein vollständiger nativer Windows-Port echte Arbeit. Die obige Baseline auf Kompilierungsebene lässt die `--version`/`--help`-Pfade und reine In-Memory-Operationen laufen; die kniffligen Teile (Chrome, Sockets, Dämonisierung) benötigen echte Win32-Implementierungen, bevor sie von der Stub-Liste genommen werden. Wenn du eine davon übernehmen möchtest, +1 bei [#153](https://github.com/justrach/kuri/issues/153) oder eröffne einen PR.
### Build aus dem Quellcode
Erfordert [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
⚡ Schnellstart
Voraussetzungen: Zig ≥ 0.16.0 · Chrome/Chromium (für den CDP-Modus)```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)
### Erster Lauf, kürzester Pfad```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
Session-first-Agentenschleife
Verwende für agentenbasierte HTTP-Nutzung bevorzugt einen Session-Header plus /tab/new, /page/info und /snapshot anstatt tab_id bei jedem Aufruf zu wiederholen.```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"
Es gibt auch einen dünnen experimentellen Wrapper unter `tools/kuri_harness.py`, falls Sie Python-Helfer auf derselben HTTP-Oberfläche nutzen möchten.
Wenn Sie bereits Chrome mit Remote-Debugging ausführen, setzen Sie `CDP_URL` entweder auf den WebSocket- oder HTTP-Endpunkt:```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 in 4 Befehlen durchsuchen```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
Alle Endpunkte geben JSON zurück. Optionale Authentifizierung über die `KURI_SECRET`-Umgebungsvariable. **135 Endpunkte** — volle Parität mit agent-browser und browser-use.
### Kern
| Pfad | Beschreibung |
|------|-------------|
| `GET /health` | Serverstatus, Tab-Anzahl, Version |
| `GET /tabs` | Alle registrierten Tabs auflisten |
| `GET /discover` | Chrome-Tabs automatisch über CDP erkennen |
| `GET /tab/current` | Aktuellen Tab für eine `X-Kuri-Session` abrufen oder setzen |
| `GET /page/info` | Live-URL/Titel/Ready-State/Viewport/Scroll für den aktiven Tab |
| `GET /page/state` | Kompakte Seitenbeobachtung: url, title, scroll%, viewport, Anzahl von Formularen/Links/Bildern/Inputs |
| `POST /batch` | Mehrere Befehle in einem HTTP-Aufruf ausführen — gibt ein Array mit Ergebnissen zurück |
| `GET /browdie` | 🌰 (Osterei) |
### Browser-Steuerung
| Pfad | Parameter | Beschreibung |
|------|--------|-------------|
| `GET /navigate` | `tab_id`, `url` | Tab zur URL navigieren |
| `GET /tab/new` | `url`, `activate`, `wait` | Neuen Tab erstellen und optional hydratisieren/als aktuellen Tab setzen |
| `GET /tab/close` | `tab_id` | Tab schließen |
| `GET /window/new` | `url`, `activate`, `wait` | Neues Fenster/Tab-Ziel erstellen |
| `GET /snapshot` | `tab_id`, `filter`, `format` | A11y-Baum-Snapshot mit `eN`-Referenzen. Verwende `filter=interactive&format=compact` für tokenarme Agent-Schleifen. |
| `GET /text` | `tab_id` | Seitentext extrahieren |
| `GET /screenshot` | `tab_id`, `format`, `quality`, `save` | Screenshot aufnehmen (base64); `save=true` schreibt das PNG nach `STATE_DIR/screenshots` und gibt stattdessen `{path,bytes}` zurück |
| `GET /screenshot/annotated` | `tab_id` | Screenshot mit nummerierten Elementbeschriftungen |
| `GET /screenshot/diff` | `tab_id`, `baseline` | Visueller Diff zwischen aktuellen und Basis-Screenshots |
| `GET /action` | `tab_id`, `ref`, `action`, `value` | Klicken/Tippen/Ausfüllen/Auswählen/Scrollen/Hover/Doppelklick/Aktivieren/Deaktivieren/Blur per Referenz |
| `GET /evaluate` | `tab_id`, `expression` | JavaScript ausführen |
| `GET /evalhandle` | `tab_id`, `expression` | JS ausführen, objectId-Handle zurückgeben (nicht den Wert) |
| `GET /close` | `tab_id` | Tab schließen + aufräumen |
| `GET /bringtofront` | `tab_id` | Tab in den Vordergrund bringen |
### Aktionen
| Pfad | Parameter | Beschreibung |
|------|--------|-------------|
| `GET /clear` | `ref` | Wert des Eingabefelds leeren |
| `GET /selectall` | `ref` | Gesamten Text in input/contenteditable auswählen |
| `GET /setvalue` | `ref`, `value` | Eingabewert direkt setzen (umgeht Tastaturereignisse) |
| `GET /dispatch` | `ref`, `type` | Benutzerdefiniertes DOM-Ereignis auf Element auslösen |
| `GET /boundingbox` | `ref` | Begrenzungsrechteck des Elements abrufen (x, y, width, height, centerX, centerY) |
| `GET /getattribute` | `ref`, `name` | Elementattribut nach Name abrufen |
| `GET /inputvalue` | `ref` | Aktuellen Wert des Eingabeelements abrufen |
| `GET /element/state` | `ref`, `check` | Schneller boolescher Wert: `exists`, `visible`, `enabled`, `checked` |
| `GET /find-element` | `text`/`role`/`label`/`placeholder`/`testid` | Semantischer Locator — Element ohne Snapshot finden |
| `GET /highlight` | `ref` oder `selector` | Element mit Overlay hervorheben |
### Maus & Touch
| Pfad | Parameter | Beschreibung |
|------|--------|-------------|
| `GET /mouse/move` | `x`, `y` | Maus zu Koordinaten bewegen |
| `GET /mouse/down` | `x`, `y`, `button` | Maustaste gedrückt |
| `GET /mouse/up` | `x`, `y`, `button` | Maustaste losgelassen |
| `GET /mouse/wheel` | `x`, `y`, `deltaX`, `deltaY` | Mausrad-Scrollen |
| `GET /tap` | `x`, `y` | Touch-Tippen (touchStart + touchEnd) |
| `GET /swipe` | `startX`, `startY`, `endX`, `endY` | Touch-Wisch-Geste |
| `GET /drag` | `src_ref`, `tgt_ref` | Element zum Ziel ziehen |
### Tastatur
| Pfad | Parameter | Beschreibung |
|------|--------|-------------|
| `GET /keyboard/type` | `tab_id`, `text` | Text über Tastaturereignisse eingeben |
| `GET /keyboard/inserttext` | `tab_id`, `text` | Text direkt einfügen |
| `GET /keydown` | `tab_id`, `key` | Taste-gedrückt-Ereignis |
| `GET /keyup` | `tab_id`, `key` | Taste-losgelassen-Ereignis |
### Inhaltsextraktion
| Pfad | Beschreibung |
|------|-------------|
| `GET /markdown` | Seite in Markdown konvertieren |
| `GET /links` | Alle Links extrahieren |
| `GET /dom/query` | CSS-Selektor-Abfrage |
| `GET /dom/html` | Element-HTML abrufen |
| `GET /dom/attributes` | Elementattribute abrufen |
| `GET /pdf` | Seite als PDF drucken |
| `GET /find` | Textsuche auf der Seite |
### Warten
| Pfad | Parameter | Beschreibung |
|------|--------|-------------|
| `GET /wait` | `selector`, `text`, `url`, `state`, `visible`, `timeout` | Auf Selektor/Text/URL-Muster/networkidle/Ladestatus warten |
| `GET /wait/function` | `expression`, `timeout` | Warten, bis ein beliebiger JS-Ausdruck truthy ist |
| `GET /wait/download` | `timeout` | Auf Abschluss des Dateidownloads warten |
### Dialogbehandlung
| Pfad | Beschreibung |
|------|-------------|
| `GET /dialog/auto` | Alle JS-Dialoge automatisch behandeln (akzeptieren oder ablehnen) |
| `GET /dialog/accept` | Aktuellen Dialog akzeptieren (mit optionalem Prompt-Text) |
| `GET /dialog/dismiss` | Aktuellen Dialog ablehnen |
### Netzwerk & HAR
| Pfad | Beschreibung |
|------|-------------|
| `GET /har/start` | Aufzeichnung des Netzwerkverkehrs starten |
| `GET /har/stop` | Stoppen + HAR-1.2-JSON zurückgeben |
| `GET /har/status` | Aufzeichnungsstatus + Anzahl der Einträge |
| `GET /har/replay` | API-Zuordnung mit curl/fetch/python-Codeausschnitten |
| `GET /cookies` | Cookies abrufen |
| `GET /cookies/set` | Cookies setzen |
| `GET /cookies/delete` | Cookies löschen |
| `GET /cookies/clear` | Alle Cookies löschen |
| `GET /headers` | Benutzerdefinierte Request-Header setzen |
| `GET /intercept/start` | Request-Interception starten |
| `GET /intercept/stop` | Request-Interception stoppen |
| `GET /intercept/requests` | Abgefangene Requests auflisten |
| `GET /request/detail` | Antworttext für eine Request-ID abrufen |
| `GET /response/body` | URL abrufen und Antworttext zurückgeben |
| `GET /network` | Statistiken zum Netzwerkverkehr |
| `GET /download` | Dateidownload auslösen |
### Navigation & Zustand
| Pfad | Beschreibung |
|------|-------------|
| `GET /back` | Browser zurück |
| `GET /forward` | Browser vor |
| `GET /reload` | Seite neu laden |
| `GET /stop` | Seitenladen stoppen |
| `GET /pushstate` | SPA-Navigation über history.pushState |
| `GET /storage/local` | localStorage abrufen/setzen |
| `GET /storage/session` | sessionStorage abrufen/setzen |
| `GET /storage/local/clear` | localStorage leeren |
| `GET /storage/session/clear` | sessionStorage leeren |
| `GET /session/save` | Browsersitzung speichern |
| `GET /session/load` | Browsersitzung wiederherstellen |
| `GET /session/list` | Gespeicherte Sitzungen auflisten |
| `GET /setcontent` | Seiten-HTML direkt setzen (POST) |
### Auth-Profile
| Pfad | Beschreibung |
|------|-------------|
| `GET /auth/profile/save` | Cookies + Speicher als benanntes Auth-Profil speichern |
| `GET /auth/profile/load` | Benanntes Auth-Profil in einen Tab wiederherstellen |
| `GET /auth/profile/list` | Gespeicherte Auth-Profile auflisten |
| `GET /auth/profile/delete` | Gespeichertes Auth-Profil löschen |
| `GET /auth/extract` | Auth-Tokens extrahieren (JWT, Cookies, Header) |
| `GET /set/credentials` | HTTP-Basic-Auth-Anmeldedaten setzen |
Unter macOS werden Auth-Profil-Geheimnisse in der Benutzer-Keychain gespeichert.
### Emulation
| Pfad | Parameter | Beschreibung |
|------|--------|-------------|
| `GET /emulate` | Gerätetyp, Bildschirmgröße | Geräteemulation |
| `GET /set/viewport` | `width`, `height` | Viewport-Größe setzen |
| `GET /set/useragent` | `ua` | User-Agent setzen |
| `GET /set/media` | `media` | Medientyp emulieren |
| `GET /set/offline` | `offline` | Offline-Modus umschalten |
| `GET /geolocation` | `lat`, `lng` | Geolokalisierung überschreiben |
| `GET /timezone` | `timezone` | Zeitzone überschreiben (z. B. `America/New_York`) |
| `GET /locale` | `locale` | Locale überschreiben (z. B. `en-US`) |
| `GET /permissions` | `name`, `state` | Berechtigungen erteilen/verweigern (Geolokalisierung, Benachrichtigungen, Zwischenablage) |
### Skripte & Injection
| Pfad | Beschreibung |
|------|-------------|
| `GET /script/inject` | JavaScript in die Seite injizieren (bleibt über Navigationen hinweg erhalten) |
| `GET /initscript/remove` | Ein zuvor injiziertes Init-Skript entfernen |
| `GET /addstyle` | CSS-Stylesheet injizieren |
| `GET /expose` | Eine benannte Funktion für den JS-Kontext der Seite bereitstellen |
### React-Inspektion
| Pfad | Beschreibung |
|------|-------------|
| `GET /react/tree` | React-Komponentenbaum über DevTools-Hook |
| `GET /react/inspect` | Props und State der React-Komponente |
| `GET /react/renders` | React-Render-Tracking (start/stop) |
| `GET /react/suspense` | Status der React-Suspense-Grenzen |
### Aufzeichnung & Leistung
| Pfad | Beschreibung |
|------|-------------|
| `GET /recording/start` | Benutzeraktionen aufzeichnen (Klick, Eingabe, Navigation) |
| `GET /recording/stop` | Aufzeichnung stoppen + Aktionsprotokoll zurückgeben |
| `GET /vitals` | Core Web Vitals (LCP, CLS, FID, TTFB, FCP, domInteractive) |
| `GET /perf/lcp` | Timing des Largest Contentful Paint |
| `GET /trace/start` | Leistungs-Trace starten |
| `GET /trace/stop` | Trace stoppen |
| `GET /profiler/start` | JS-Profiler starten |
| `GET /profiler/stop` | Profiler stoppen |
### Debugging
| Pfad | Beschreibung |
|------|-------------|
| `GET /debug/enable` | In-Seiten-Debug-HUD und optionalen Freeze-Modus aktivieren |
| `GET /debug/disable` | In-Seiten-Debug-HUD deaktivieren |
| `GET /inspect` | Elementinspektion |
| `GET /errors` | JS-Fehler sammeln |
| `GET /console` | Konsolenprotokolle lesen |
| `GET /frames` | Seiten-Frames auflisten |
| `GET /frame` | Zum iframe-Kontext nach Name oder URL wechseln |
| `GET /mainframe` | Zurück zum Hauptframe wechseln |
| `GET /diff/snapshot` | Kompakter `+`/`~`/`-`-Diff gegen den vorherigen Aufruf für diesen Tab — die tokeneffiziente Aktionsschleife (als Alias über `/snapshot/changes`). Fällt bei massiven Änderungen auf einen vollständigen Snapshot mit einem `! page replaced`-Header zurück. |
| `GET /diff/url` | Zwei URLs nebeneinander vergleichen (navigate, snapshot, diff) |
### Streaming
| Pfad | Beschreibung |
|------|-------------|
| `GET /screencast/start` | Bildschirmaufnahme starten |
| `GET /screencast/stop` | Bildschirmaufnahme stoppen |
| `GET /video/start` | Videoaufnahme starten |
| `GET /video/stop` | Videoaufnahme stoppen |
| `GET /ws/start` | WebSocket-Tunnel starten |
| `GET /ws/stop` | WebSocket-Tunnel stoppen |
### Agentenfreundliche Schleife
Die reibungsloseste Server-Schleife ist:
1. `GET /tab/new?url=...`
2. `GET /page/state` (leichtgewichtig) oder `GET /snapshot?filter=interactive&format=compact` (vollständig)
3. `GET /action?action=click&ref=eN`
4. Wiederholen — oder `POST /batch` für mehrstufige Vorgänge in einem Aufruf verwenden
`url`- und `expression`-Query-Parameter werden prozent-dekodiert. Sende `X-Kuri-Session: my-agent`, um den Tab-Kontext serverseitig beizubehalten.
---
## 🧠 Skills
Das Repository enthält einen benutzererweiterbaren Skill-Bereich:
- `skills/kuri-skill.md` ist das Basis-Kuri-HTTP-Agent-Skill
- `skills/custom/` ist für deine eigenen projektspezifischen Skills reserviert
- `skills/custom/hackernews-page-2.md` ist ein konkretes Beispiel für ein benutzerdefiniertes Skill
- `.claude/skills/kuri-server/SKILL.md` bleibt für Claude-artige Repository-Skills synchron
Das Basis-Skill erklärt nun auch, welcher Browser-Pfad verwendet werden soll:
- `kuri` HTTP-API: Produktions-Chrome/CDP-Automatisierung mit Sitzungen, Snapshots, Aktionen, HAR, Cookies und Screenshots
- `kuri-fetch`: eigenständige Fetch-/Textextraktion ohne Chrome
- `kuri-browse`: interaktives Terminal-Browsing
- `kuri-agent`: skriptbare CLI-Automatisierung gegen den Kuri-Server
- `kuri-browser/`: experimentelle separate Zig-native Browser-Laufzeit für Paritätsarbeit
Für die experimentelle Browser-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 bietet eine Chrome-ähnliche HTTP-Erkennung sowie einen minimalen WebSocket-JSON-RPC-Router für Protokoll-Smoke-Tests. Runtime eval gibt V8-förmige CDP-Remoteobjekte zurück, die auf QuickJS basieren; dies fügt keine V8-Abhängigkeit hinzu und ist noch keine vollständige Playwright/Puppeteer-Kompatibilität.
Screenshots in kuri-browser werden derzeit an den Haupt-Kuri/CDP-Renderer delegiert. Starten Sie zuerst ./zig-out/bin/kuri, dann:```bash
cd kuri-browser
zig build run -- screenshot https://example.com --out example.jpg --compress --kuri-base http://127.0.0.1:8080
`--compress` erfasst eine PNG-Baseline und einen JPEG-Kandidaten, schreibt die kleinere Datei und meldet die eingesparten Bytes. Aktuelle lokale Messung auf `https://example.com`: `20,523` Bytes PNG zu `18,183` Bytes JPEG-Qualität 50, spart `2,340` Bytes oder `11%`.
### Erweitert
| Pfad | Beschreibung |
|------|--------------|
| `GET /diff/snapshot` | Kompaktes `+`/`~`/`-`-Delta gegenüber dem vorherigen Snapshot (Agenten-Aktionsschleife) |
| `GET /emulate` | Geräteemulation |
| `GET /geolocation` | Geolokalisierung festlegen |
| `POST /upload` | Datei-Upload |
| `GET /script/inject` | JavaScript injizieren |
| `GET /intercept/start` | Anfrage-Interception starten |
| `GET /intercept/stop` | Interception stoppen |
| `GET /screenshot/annotated` | Screenshot mit Element-Anmerkungen |
| `GET /screenshot/diff` | Visueller Diff zwischen Screenshots |
| `GET /screencast/start` | Screencast starten |
| `GET /screencast/stop` | Screencast stoppen |
| `GET /video/start` | Videoaufzeichnung starten |
| `GET /video/stop` | Videoaufzeichnung stoppen |
| `GET /console` | Konsolenmeldungen abrufen |
| `GET /stop` | Seitenladen stoppen |
| `GET /get` | Direkter HTTP-Abruf (serverseitig) |
| `GET /scrollintoview` | Ein referenziertes Element in den sichtbaren Bereich scrollen |
| `GET /drag` | Von einem Ref zu einem anderen ziehen |
| `GET /keyboard/type` | Text mit Tastaturereignissen eingeben |
| `GET /keyboard/inserttext` | Text direkt einfügen |
| `GET /keydown` | Ein Keydown-Ereignis auslösen |
| `GET /keyup` | Ein Keyup-Ereignis auslösen |
| `GET /wait` | Auf Ready-State oder Elementbedingungen warten |
| `GET /tab/close` | Tab schließen |
| `GET /highlight` | Ein Element per Ref oder Selektor hervorheben |
| `GET /errors` | Seiten-/Laufzeitfehler abrufen |
| `GET /set/offline` | Offline-Netzwerkemulation umschalten |
| `GET /set/media` | Emulierte Medienfunktionen festlegen |
| `GET /set/credentials` | HTTP-Basisauthentifizierungsdaten festlegen |
| `GET /find` | Textübereinstimmungen auf der aktuellen Seite finden |
| `GET /trace/start` | Chrome-Tracing starten |
| `GET /trace/stop` | Tracing stoppen und Tracedaten zurückgeben |
| `GET /profiler/start` | JS-Profiler starten |
| `GET /profiler/stop` | JS-Profiler stoppen |
| `GET /inspect` | Ein Element oder den Seitenzustand untersuchen |
| `GET /set/viewport` | Viewport-Größe festlegen |
| `GET /set/useragent` | User-Agent überschreiben |
| `GET /dom/attributes` | Elementattribute abrufen |
| `GET /frames` | Frame-Baum auflisten |
| `GET /network` | Netzwerkstatus/-anfragen untersuchen |
---
## 🛡️ Stealth & Bot-Umgehung
Kuri wendet Anti-Erkennungs-Patches beim Start automatisch an — keine manuelle Konfiguration erforderlich.
### Was angewendet wird
- **`Page.addScriptToEvaluateOnNewDocument`** — Stealth-Patches werden vor jedem Seiten-JS ausgeführt
- **navigator.webdriver = false** — verbirgt das Automatisierungsflag auf Chromium-Ebene (`--disable-blink-features=AutomationControlled`)
- **WebGL/Canvas/AudioContext-Spoofing** — überwindet fingerabdruckbasierte Erkennung
- **UA-Rotation** — 5 realistische Chrome-/Safari-/Firefox-User-Agents
- **chrome.csi/chrome.loadTimes** — Stubs für Akamai-spezifische Prüfungen
### Bot-Block-Erkennung
Navigate erkennt Blockierungen automatisch und gibt ein strukturiertes Fallback zurück:```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
Detects: Akamai, Cloudflare, PerimeterX, DataDome, generisches Captcha.
Proxy-Unterstützung```bash
KURI_PROXY=socks5://user:pass@residential-proxy:1080 ./zig-out/bin/kuri KURI_PROXY=http://proxy:8080 ./zig-out/bin/kuri
### Getestete Websites
| Website | Schutz | Ergebnis |
|------|-----------|--------|
| Singapore Airlines | Akamai WAF | ✅ Umgangen (war vor v0.4.0 blockiert) |
| Shopee SG | Eigener Anti-Fraud-Schutz | ✅ Seite lädt, leitet zur Anmeldung um |
| Google Flights | Keiner | ✅ Vollständige Interaktion |
| Booking.com | PerimeterX | ⚠️ Benötigt Proxy |
---
## 🔧 kuri-fetch
Eigenständiger HTTP-Fetcher — kein Chrome, kein Playwright, kein npm. Wird als ~2 MB große Binärdatei mit integriertem QuickJS für die JavaScript-Ausführung geliefert.```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
Funktionen
- 5 Ausgabemodi —
markdown,html,links,text,json - QuickJS-JS-Engine —
--jsführt Inline-<script>-Tags aus - DOM-Stubs —
document.querySelector,getElementById,window.location,document.title,console.log,setTimeout(SSR-Stil) - SSRF-Schutz — blockiert private IPs, Metadaten-Endpunkte, Nicht-HTTP-Schemas
- Farbige Ausgabe — berücksichtigt
NO_COLOR,TERM=dumb,--no-color, TTY-Erkennung - Dateiausgabe —
-o/--outputmit Byteanzahl + Timing-Zusammenfassung - Benutzerdefinierter UA —
--user-agent-Flag - Stiller Modus —
-qunterdrückt die stderr-Statusausgabe
🌐 kuri-browse
Interaktiver Terminal-Browser — durchstöbere das Web von deinem Terminal aus. Kein Chrome erforderlich.```bash zig build browse # build + run
kuri-browse https://example.com
Bitte fügen Sie den zu übersetzenden Markdown-Inhalt ein.```
🌰 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
Befehle
| Befehl | Aktion |
|---|---|
<number> | Link [N] folgen |
<url> | Navigieren (wenn . enthalten) |
:go <url> | Zu URL navigieren |
:back, :b | In der Historie zurückgehen |
:forward, :f | Vorwärtsgehen |
:reload, :r | Aktuelle Seite neu laden |
:links, :l | Link-Index anzeigen |
/<term> | In Seite suchen (markiert Treffer) |
:search <t> | In Seite suchen |
:n, :next | Suche erneut hervorheben |
:history | Navigationsverlauf anzeigen |
:help, :h | Alle Befehle anzeigen |
:quit, :q | Beenden |
Funktionen
- Farbige Markdown-Darstellung — Überschriften, Links, Codeblöcke, Fettdruck, Blockzitate
- Nummerierte Links — jeder Link erhält
[N]; gib die Nummer ein, um ihm zu folgen - Navigationsverlauf — zurück/vorwärts wie in einem echten Browser
- Suche in der Seite —
/termmarkiert alle Treffer - Relative URL-Auflösung — folgt Links natürlich über Seiten hinweg
- Intelligente Filterung — überspringt
javascript:- undmailto:-hrefs
🤖 kuri-agent
Skriptbare CLI für die Chrome-Automatisierung — steuert den Browser Befehl für Befehl von deinem Terminal oder Shell-Skripten. Teilt den Sitzungszustand über Aufrufe hinweg über ~/.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
### Befehle
| Befehl | Beschreibung |
|---------|-------------|
| `tabs [--port N]` | Chrome-Tabs auflisten |
| `use <ws_url>` | An einen Tab anhängen (speichert Sitzung) |
| `open [url] [--port N]` | Neuen Tab öffnen (optional zu URL navigieren) |
| `status` | Aktuelle Sitzung anzeigen |
| `go <url>` | Zu URL navigieren |
| `snap [--interactive] [--json] [--text] [--depth N]` | A11y-Snapshot, speichert `eN`-Referenzen |
| `click <ref>` | Element anhand der Referenz anklicken (CDP-Mausereignisse, React-kompatibel) |
| `type <ref> <text>` | In Element tippen (Tastaturereignisse pro Zeichen, React-kompatibel) |
| `fill <ref> <text>` | Eingabewert ausfüllen |
| `select <ref> <value>` | Dropdown-Option auswählen |
| `hover <ref>` | Maus über Element bewegen |
| `focus <ref>` | Element fokussieren |
| `scroll` | Seite scrollen |
| `viewport [width height]` | Viewport-Abmessungen abrufen oder festlegen |
| `eval <js>` | JavaScript auswerten |
| `text [selector]` | Seitentext abrufen |
| `shot [--out file.png]` | Screenshot |
| `back` | Zurück navigieren |
| `forward` | Vorwärts navigieren |
| `reload` | Aktuelle Seite neu laden |
| `cookies` | Cookies mit Sicherheitsflags auflisten |
| `headers` | Sicherheits-Response-Header prüfen |
| `audit` | Vollständiger Sicherheits-Audit |
| `storage [local\|session\|all]` | localStorage / sessionStorage ausgeben |
| `jwt` | JWTs aus Cookies und Storage extrahieren und decodieren |
| `fetch <method> <url> [--data <json>]` | Authentifizierten Fetch mit Seiten-Cookies durchführen |
| `probe <url-template> <start> <end>` | IDOR-Probe: numerische IDs in der URL durchlaufen |
| `grab <ref>` | Referenz anklicken, `window.open` abfangen, Weiterleitung im Tab verfolgen |
| `wait-for-tab [--port N]` | Auf neuen Tab warten (Polling), Sitzung automatisch wechseln |
| `stealth` | Anti-Erkennungs-Patches anwenden |
| `set-header <name> <value>` | Benutzerdefinierten Header zu allen Anfragen hinzufügen |
| `show-headers` | Gespeicherte zusätzliche Header anzeigen |
| `clear-headers` | Alle zusätzlichen Header entfernen |
---
## 📱 kuri-mobile (iOS + Android)
Native Zig-CLI zum Steuern von iOS-Simulatoren, echten iPhones (Auflisten + Starten/Beenden) und Android-Geräten/Emulatoren — inspiriert von [`mobile-device-mcp`](https://github.com/srmorete/mobile-device-mcp), in Zig neu implementiert, ohne Bun/Node/Gradle/Xcode im Build-Pfad.```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
Was nativ in Zig ist: adb-Hostprotokoll (libc-Sockets, 4-Hex-Framing über host:transport:/shell:/exec:), Android-XML-UI-Tree-Parser, usbmuxd-ListDevices-Plist-Client.
Was extern aufruft: xcrun simctl (iOS-Simulator), xcrun devicectl (Starten/Beenden auf echten iOS-Geräten).
Konstruktionsbedingt treiberlos: es wird keine App auf dem Gerät installiert, daher sind run_code-Sandboxes und die auf XCUITest basierenden tap/uitree-Funktionen auf echten iOS-Geräten bewusst nicht verfügbar. Siehe kuri-mobile/README.md für die vollständige Paritätsmatrix im Vergleich zum Upstream.
🔒 Sicherheitstests
kuri-agent unterstützt browser-native Sicherheits-Trajektorien — melde dich einmal an und führe dann Reconnaissance- sowie Header-/Cookie-Audits durch, ohne das Terminal zu verlassen.
Trajektorien
Enumerate → Inspect — nach der Authentifizierung Authentifizierungs-Cookies ausgeben und Sicherheits-Flags prüfen:```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]
**Header-Überprüfung** — prüfen, welche Sicherheits-Header das Ziel sendet:```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", ...}}
Vollständiges Audit — HTTPS, fehlende Header, JS-sichtbare Cookies auf einen Schlag:```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'", ...}}
**Kontenübergreifende Trajektorie** — verwenden Sie `eval`, um API-Aufrufe mit unterschiedlichen Tokens erneut auszuführen:```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)"
Format des Trajektorienberichts
kuri-agent gibt JSON aus, das für die Pipeline-Integration geeignet ist. Jeder Sicherheitsbefehl erzeugt eine einzelne JSON-Zeile – zur Triage durch jq leiten:```bash
kuri-agent audit | jq '.issues[]'
kuri-agent cookies | head -20
kuri-agent headers | jq '.headers | to_entries[] | select(.value == "(missing)") | .key'
---
## 🏗 Architektur```
┌──────────────────────────────────────────────────────────┐
│ 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) │
└──────────────────────────────────────────────────────────┘
Speichermodell
- Arena-pro-Anfrage — der gesamte Anfrage-Speicher wird in einem einzigen
deinit()-Aufruf freigegeben - Kein GC —
GeneralPurposeAllocatorim Debug-Modus fängt jedes Speicherleck - Saubere Bereinigungsketten —
Launcher → Bridge → CdpClients → HarRecorders → Snapshots → Tabs errdefer-Schutzmechanismen — Teilfehler werden sauber zurückgerollt
Chrome-Lebenszyklus
| Modus | Verhalten |
|---|---|
Verwaltet (keine CDP_URL) | Startet Chrome headless, findet freien CDP-Port, überwacht, startet bei Absturz automatisch neu (max. 3 Versuche), beendet beim Herunterfahren |
Extern (CDP_URL gesetzt) | Verbindet sich mit vorhandenem Chrome, führt einen Health-Check über /json/version durch, beendet beim Herunterfahren NICHT |
📁 Struktur```
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
---
## ⚙️ Konfiguration
| Umgebungsvariable | Standard | Beschreibung |
|---------|---------|-------------|
| `HOST` | `127.0.0.1` | Bindeadresse des Servers |
| `PORT` | `8080` | Server-Port |
| `CDP_URL` | *(none)* | Mit bestehendem Chrome verbinden (`ws://...` oder `http://127.0.0.1:9222`) |
| `KURI_SECRET` | *(none)* | Auth-Secret für API-Anfragen |
| `STATE_DIR` | `.kuri` | Verzeichnis für den Sitzungsstatus |
| `REQUEST_TIMEOUT_MS` | `30000` | HTTP-Anfrage-Timeout |
| `NAVIGATE_TIMEOUT_MS` | `30000` | Navigations-Timeout |
| `STALE_TAB_INTERVAL_S` | `30` | Intervall für die Bereinigung veralteter Tabs |
| `NO_COLOR` | *(none)* | Farbige CLI-Ausgabe deaktivieren |
---
## 💰 Token-Kosten
Für eine Monitoring-Aufgabe über 50 Seiten (aus Pinchtab-Benchmarks):
| Methode | Token | Kosten ($) | Geeignet für |
|--------|--------|----------|----------|
| `/text` | ~40,000 | $0.20 | Leselastig (13× günstiger als Screenshots) |
| `/snapshot?filter=interactive&format=compact` | ~40,000 | $0.20 | Elementinteraktion mit geringem Token-Verbrauch |
| `/snapshot` (voll) | ~525,000 | $2.63 | Verständnis der gesamten Seite |
| `/screenshot` | ~100,000 | $1.00 | Visuelle Verifizierung |
---
## 🤝 Mitwirken
Öffne ein Issue, bevor du einen großen PR einreichst, damit wir uns über die Vorgehensweise abstimmen können.```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)
Siehe CONTRIBUTORS.md für Richtlinien.
Danksagungen
| Projekt | Was wir übernommen haben |
|---|---|
| agent-browser | @eN-Referenzsystem, Snapshot-Diffing, HAR-Aufzeichnungsmuster |
| Pinchtab | Browsersteuerungsarchitektur für KI-Agenten |
| Pathik | Hochleistungs-Crawling-Muster |
| QuickJS-ng via mitchellh/zig-quickjs-ng | JS-Engine für kuri-fetch |
| Lightpanda | Zig-nativer Headless-Browser-Pionier, CDP-Kompatibilitätsmuster |
| Zig 0.16.0 | Der gesamte Stack |
Lizenz
Apache-2.0