eoka-runner
Config-based browser automation. Define actions in YAML, execute deterministically.
Part of the eoka-tools workspace.
Quick Start
# automation.yaml
name: "Example"
target:
url: "https://example.com"
actions:
- wait_for_network_idle:
timeout_ms: 5000
- click:
text: "More information"
- screenshot:
path: "result.png"
success:
any:
- url_contains: "/info"
Features
- YAML configs — define automation flows without writing code
- Parameterized configs —
${variable}substitution for reusable flows - 30 action types — navigation, clicking, input, scrolling, cookies, JS, conditionals
- Text targeting — click by visible text, not just CSS selectors
- Human-like actions — optional mouse movement and typing delays
- Retry logic — automatic retries with configurable delay
- Success conditions — verify URL or text content after completion
- Failure screenshots — capture state on error for debugging
CLI Usage
# Run config
# Run with parameters
# Validate without running
# Verbose output
# Override headless mode
# Quiet (errors only)
Config Format
name: "Automation Name"
# Parameters (optional) - for reusable configs
params:
email:
required: true
description: "User email"
timeout:
default: "5000"
description: "Wait timeout"
browser:
headless: false
proxy: "http://user:pass@host:port" # optional
user_agent: "Custom UA" # optional
viewport: # optional
width: 1920
height: 1080
target:
url: "https://example.com"
actions:
# Use ${param_name} for substitution
- fill:
selector: "#email"
value: "${email}"
success:
any: # OR conditions
- url_contains: "/success"
- text_contains: "Thank you"
# or use 'all' for AND conditions
on_failure:
screenshot: "error_{timestamp}.png"
retry:
attempts: 3
delay_ms: 2000
Action Types
Navigation
goto: { url }— Navigate to URLback— Browser backforward— Browser forwardreload— Refresh page
Waiting
wait: { ms }— Fixed delaywait_for_network_idle: { idle_ms, timeout_ms }wait_for: { selector, timeout_ms }— Wait for elementwait_for_visible: { selector, timeout_ms }wait_for_hidden: { selector, timeout_ms }wait_for_text: { text, timeout_ms }wait_for_url: { contains, timeout_ms }
Clicking
click: { selector | text, human, scroll_into_view }try_click: { selector | text }— No error if missingtry_click_any: { texts }— Click first found
Input
fill: { selector | text, value, human }— Clear and typetype: { selector | text, value }— Append textclear: { selector | text }— Clear input fieldselect: { selector | text, value }— Select dropdown optionpress_key: { key }— Press key (Enter, Tab, Escape, ArrowDown, etc.)
Mouse
hover: { selector | text }— Hover over element
Cookies
set_cookie: { name, value, domain?, path? }— Set a cookiedelete_cookie: { name, domain? }— Delete a cookie
JavaScript
execute: { js }— Run arbitrary JavaScript
Scrolling
scroll: { direction, amount }scroll_to: { selector | text }
Debug
screenshot: { path }log: { message }assert_text: { text }assert_url: { contains }
Control Flow
if_text_exists: { text, then, else }if_selector_exists: { selector, then, else }repeat: { times, actions }
Composition
include: { path, params? }— Include another config's actions
Reusable Flows with Include
Create reusable building blocks:
# flows/dismiss_cookies.yaml
name: "Dismiss Cookies"
target:
url: "about:blank"
actions:
- try_click_any:
texts:
# flows/login.yaml
name: "Login"
params:
email:
password:
target:
url: "about:blank"
actions:
- fill:
- fill:
- click:
Compose them in your main config:
name: "Checkout Flow"
target:
url: "https://shop.example.com"
actions:
- include:
- include:
path: "flows/login.yaml"
params:
email: "user@example.com"
password: "secret"
- click:
Include paths are relative to the config file's directory.
Library Usage
use ;
// Simple usage
let config = load?;
let mut runner = new.await?;
let result = runner.run.await?;
println!;
runner.close.await?;
// With parameters
let params = new
.set
.set;
let config = load_with_params?;
Examples
See the configs/ directory in this crate for example YAML configs.