---
name: eoka
description: "Drive a real Chrome browser from the shell with eoka. Use for browser automation, protected sessions, agent-friendly observation, Tack TypeScript tools, CAPTCHA solving, network recording/HAR export/interception, browser-context fetch, state cloning, WASM memory, fake camera, SPA navigation, and daemon/session management."
---
# eoka
Use `eoka` when the task needs a real Chrome browser instead of plain HTTP or a synthetic DOM. Commands auto-start a per-session daemon, so repeated calls are fast and keep browser state.
## Agent Defaults
Prefer `--agent` for machine callers. It implies JSON output, adds `meta`, returns structured errors, and makes `observe` return structured element objects.
```bash
eoka --agent doctor
eoka --agent open https://example.com
eoka --agent observe --max 20
eoka --agent batch '[{"cmd":"observe","args":{"max":5}}]'
```
Use `--json` only when preserving older command-specific JSON shapes matters. `tools manifest --json` returns raw `{ "tools": [...] }`; `--agent tools manifest --json` returns the standard response envelope.
## Core Browser Flow
```bash
eoka open https://example.com
eoka snapshot -i
eoka observe --filter inputs --max 10
eoka click @e2
eoka fill @e3 "test@example.com"
eoka screenshot --annotate -o page.png
eoka close
```
Targets accepted by action commands:
| Target | Example |
| --- | --- |
| snapshot ref | `@e1` |
| observe index | `[0]`, `0`, `index:0` |
| text | `text:Submit` or `Submit` |
| placeholder | `placeholder:Email` |
| CSS | `css:#submit` |
| ID | `id:login` |
| role | `role:button` |
## Automation Tools
```bash
eoka eval "document.title"
eoka eval -f ./script.js --max-size 4096
eoka exec "localStorage.clear()"
eoka frames
eoka frame-eval 'iframe#login' 'document.title'
eoka frame-eval 'id:FRAME_ID_FROM_FRAMES' 'document.title'
eoka wait --text "Welcome"
eoka wait --url "/dashboard"
eoka tab list
eoka tab new https://example.com
```
Use `--no-return` or `--max-size` for JavaScript that may produce large results.
Frame evaluation stays in the selected page, including cross-origin OOPIFs.
Use a CSS selector matching one frame in the top document, or `id:` from `frames`
for nested frames. Bare URLs and indices are invalid. Frame code runs in an
isolated world with DOM access, not the page's own JavaScript globals. Frame
operations require an existing session and never launch, restart or replay.
Reacquire IDs after navigation; upgrade an old daemon explicitly.
## Keyboard
`eoka key o` sends a native printable key; `eoka key 'Shift+o'` sends uppercase
`O`. Literal character case is preserved. Shift uses US-keyboard ASCII mappings;
Ctrl/Alt/Meta shortcuts do not insert text. The command targets the current focus,
including a focused frame. Success confirms dispatch, not that a page accepted an
edit: read-only inputs and `preventDefault()` can still prevent insertion.
## Tack
`eoka tack` runs Tack TypeScript with Eoka tools registered from `eoka-protocol`.
```bash
eoka tools manifest --json
eoka tack 'return await tools.invoke({ path: "eoka.info", input: {} });'
eoka tack --raw-json 'const hits = await tools.search({ query: "info", limit: 3 }); return hits.data.map(t => t.path);'
eoka tack --capability network --raw-json 'return await tools.search({ query: "network.record", limit: 5 });'
eoka tack --all-tools --raw-json 'return await tools.search({ query: "network", limit: 20 });'
```
Default Tack exposure is agent-safe. Use `--capability <name>` for opt-in groups such as `network`, or `--all-tools` for every non-lifecycle tool.
## Network
```bash
eoka fetch https://api.target.com/me
eoka fetch https://api.target.com/data -m POST --headers '{"Content-Type":"application/json"}' -b '{"key":"value"}'
eoka network record start --pattern "*/api/*" --max-body-bytes 10485760 --clear
eoka network wait --pattern "*/api/*" --status 200 --timeout 10000 --include-existing
eoka network log --limit 50 --compact
eoka network show 12 --body --max-body 65536
eoka network har /tmp/session.har
eoka network export /tmp/session.json --format json
eoka network intercept add "*/api/data*" --capture /tmp/req.json
eoka network intercept add "*/config" --respond /tmp/mock.json --status 200
eoka network intercept log --clear
```
`fetch` runs inside the browser context with active cookies and fingerprint. It retains at most 8192 raw text bytes by default; `--max-body` accepts 0 (headers only) through 16777216. JSON reports `bytesRead`, `truncated`, and `bodyOmitted`; known binary bodies are skipped. `--body-only` remains bounded and fails on binary or truncated bodies. Use native downloads for files. Network bodies are captured unredacted by default; treat exports as sensitive.
Interception logs retain the latest 1,000 entries, including during long-running commands; older entries are evicted. `--respond` serves the file's exact bytes, including binary content. Response/capture files must be regular opened descriptors, capped at 8 MiB. File operations have a 500 ms deadline and a single worker; special files (including FIFOs/devices) are rejected. Failed capture/read/fulfillment or unconfirmed continuation invalidates DataDome clearance; fallback continues only never-dispatched pauses without erasing failures. Possibly dispatched resolutions are never replayed after missing acknowledgments or errors. DataDome integrity starts at command entry and includes the transferred idle worker.
## Download Files
```bash
eoka download --click 'text:Download' --out ./archive.zip
eoka download --url https://target.com/attachment --out ./file.zip --wait
eoka download list
eoka download status JOB_ID
eoka download wait JOB_ID --timeout-ms 120000
eoka download cancel JOB_ID
```
Use the existing Eoka-owned browser session; borrowed/remote browsers are rejected.
The default returns a job ID. One file runs per session; wait timeout or client
exit leaves it running, and session closure cancels unfinished work. Start actions
are never replayed. An older daemon requires an explicit restart before use.
`--click` accepts observation refs, text and CSS. `--url` uses a temporary tab and
must produce a native attachment, not an HTML landing/login page. The output parent
must exist; relative CLI paths resolve in the calling shell's directory.
`--expect-bytes` verifies a known size. `--start-timeout-ms` bounds native-start
detection (default 30000, maximum 300000); `--timeout-ms` only bounds waiting.
Completion means a verified regular file was atomically published without replacing
anything. Inspect `published_path` and retained `staging_path` on failures before
retrying or cleaning up. Published files survive session closure. Jobs are in-memory;
there is no resume or restart recovery. SDK/MCP/Tack require absolute output paths
on their host and use canonical `download.start/list/get/wait/cancel` operations
(Tack prefix: `eoka.`).
## State, Sessions, And Real Chrome
```bash
eoka save-state ./auth.json
eoka load-state ./auth.json
eoka open /dashboard --load-state ./auth.json
eoka clone-from 9222 --to state.json
eoka --clone-state-from 9222 open https://protected-app.com
eoka --from-profile auto open https://protected-app.com
eoka --cdp 9222 snapshot
eoka --auto-connect info
eoka cdp-url --port 9222
eoka --session work open https://example.com
eoka sessions
eoka status
eoka kill
```
In `--cdp` and `--auto-connect` mode, eoka attaches to an existing Chrome and does not close a browser it does not own.
## CAPTCHA, Media, WASM, SPA
```bash
eoka --session demo captcha datadome
eoka --session demo --json captcha datadome --max-attempts 2 --timeout-ms 60000
eoka captcha solve --captcha-type recaptcha_v3 --website-url https://target.com --website-key SITE_KEY --page-action submit
eoka captcha solve --captcha-type recaptcha_v3 --website-url https://target.com --website-key SITE_KEY --inject
eoka captcha inject TOKEN --captcha-type recaptcha --click-after "text:Continue"
eoka fake-camera /path/to/face.webm --loop-video
eoka wasm info
eoka wasm read 0x360000 32
eoka wasm write 0x360000 deadbeef
eoka wasm find "ff d8 ff e0" --max 5
eoka spa-info
eoka spa-navigate /dashboard
```
`captcha datadome` is local and requires an existing session/tab, with no API key.
It foregrounds that tab and leaves it open. Default: one attempt, 60-second budget
plus bounded cleanup. `challenge_cleared` is not login proof; `not_present` is a
no-op. Other outcomes/errors exit nonzero. Do not automatically retry application
actions or uncertain transport results. Use the same session/connection flags;
an old daemon is not automatically restarted.
Provider-based `captcha solve` uses `ANTI_CAPTCHA_KEY` unless `--api-key` is passed.
## Launch Flags
Common global flags:
| Flag | Use |
| --- | --- |
| `--session <name>` | isolate browser state |
| `--agent` | stable machine output and metadata |
| `--json` | legacy JSON mode |
| `--headed` | visible browser |
| `--cdp <PORT\|URL>` | attach to Chrome |
| `--auto-connect` | discover Chrome on 9222-9229 |
| `--clone-state-from <PORT\|URL>` | hydrate launched browser from live Chrome |
| `--from-profile <auto\|PATH>` | launch from copied profile |
| `--persist` | durable Chrome profile for the session (survives restarts; `EOKA_PROFILE_DIR` overrides the location) |
| `--no-geo-align` | disable IP-geolocation timezone/language alignment (on by default) |
| `--proxy <URL>` / `--proxy-file <FILE>` | launched-browser proxy |
| `--no-js`, `--js-allow`, `--js-block` | per-domain JavaScript policy |
Useful env vars: `EOKA_CDP`, `EOKA_AUTO_CONNECT`, `EOKA_FROM_PROFILE`, `EOKA_PROXY`, `EOKA_PROXY_FILE`, `EOKA_NO_JS`, `EOKA_IDLE_TIMEOUT`, `ANTI_CAPTCHA_KEY`.