Docs
Getting started
From nothing to a passing flow: start a local hub with the fixture app, record a session, cut four elements, write a flow and run it from the CLI.
Last updated 15 October 2026
This guide takes you from nothing to a flow that passes. You start a local hub with the fixture app (a small web app made for testing), record yourself signing in to it, cut four elements from the recording, write a flow that uses them, and run it from the command line. It takes about fifteen minutes.
What you need
- A checkout of the attention.run repository. The CLI and the SDK aren’t published to npm yet, so everything here runs from it.
- Node.js 24 or newer, Rust 1.85 or newer (to build the native matcher), and Google Chrome. The matcher is built and tested on macOS on Apple Silicon; the hosted app’s Linux image builds it too.
- Docker only if you want a desktop over VNC as a second target (
npm run desktop).
The hosted app at app.attention.run is an invite-only preview, and its API can’t be reached by the CLI from outside yet. The steps below run the same product on your machine, so the addresses in this guide are local ones.
1. Start the demo
In the checkout:
npm install
npm run tm:build
npm run demo
npm run tm:build builds the native matcher once. npm run demo starts, and stops on Ctrl+C:
| What | Where |
|---|---|
| The hub and a runner (local Chrome) | http://127.0.0.1:7300 |
| The fixture app, the demo’s target (sign in with demo / demo) | http://127.0.0.1:7310 |
| The web app: open this | http://localhost:5173 |
| An SSH server for SSH targets | 127.0.0.1:7322 |
It adds a Chrome target named “Fixture app” that opens the fixture’s sign-in page. Data is kept in ./attn-data, so what you make survives a restart.
To use the CLI from the checkout, run it with npx attn, or make attn an alias for this terminal:
alias attn='npx attn'
attn status
2. Record a session
- In the web app, open Sessions, click New session, pick the target “Fixture app” (keep Record on) and click Start session. The live view mirrors its screen; your clicks and typing go to it.
- Sign in: click the Username field and type
demo, click the Password field and typedemo, then click Sign in. The page says “Welcome, demo”. - Click End session. The recording keeps every frame and every input on one clock.
3. Cut four elements
An element is a named piece of the screen that attention.run finds by its pixels. Open the recording from Sessions (Open the recording). Its clicks panel lists your clicks: Cut… on one opens the cutter on the frame just before it. For a frame without a click, scrub the timeline and use Cut at this frame…. Draw a rectangle, name the element and save it. The cutter checks the cut against the whole recording and says whether it is unique.
Cut these four, named exactly like this, because the flow below uses the names:
| Element | What to cut |
|---|---|
login/username |
The “Username” label and its empty field together. Both fields look alike while empty; with their labels they don’t. |
login/password |
The “Password” label and its empty field. |
login/sign-in |
The Sign in button. |
welcome/heading |
The “Welcome, demo” heading, on a frame after you signed in (Cut at this frame…). |
When you cut from a click, the click’s position inside the rectangle becomes the element’s click point, so a flow that types “into” login/username clicks the field, not the label.
4. Write a flow
A flow is JSON: a slug, optional parameters, and steps that name elements. Save this as sign-in.json:
{
"flow": "fixture/sign-in",
"params": { "user": { "type": "string", "default": "demo" } },
"steps": [
{ "navigate": "http://127.0.0.1:7310/login" },
{ "type": { "param": "user" }, "into": "login/username" },
{ "type": "demo", "into": "login/password" },
{ "click": "login/sign-in" },
{ "waitFor": { "visible": "welcome/heading" }, "timeout": 5000 },
{ "assert": { "gone": "login/sign-in" } }
]
}
Each step that names an element first waits for it to be on screen (10 seconds unless the step or the flow says otherwise), so there are no sleeps. A waitFor after an action only looks at frames from after that action, so a heading that was already there doesn’t count. Every field is in the flow JSON reference.
The recording’s Create flow button drafts a flow like this one from your clicks and typing, with the elements you cut. This guide writes it by hand so you see every step.
5. Run it
Push the flow to the hub, then run it on the target and wait for the result:
export ATTN_HUB=http://127.0.0.1:7300
attn flows push sign-in.json
attn run fixture/sign-in --target "Fixture app" --wait
attn flows push checks the file before it sends it, and the hub checks that the elements exist in the library. attn run --wait prints a line per step as it ends, then a summary, and exits 0 when the run passed (1 failed, 2 error, 3 cancelled), so the same command works in CI:
fixture/sign-in v1 (new flow)
run_…: fixture/sign-in v1 on Fixture app (tgt_…)
✓ steps[0] navigate http://127.0.0.1:7310/login (25 ms)
✓ steps[1] type params.user into login/username (69 ms)
✓ steps[2] type "demo" into login/password (94 ms)
✓ steps[3] click login/sign-in (34 ms)
✓ steps[4] waitFor visible welcome/heading (55 ms)
✓ steps[5] assert gone login/sign-in (10 ms)
passed: fixture/sign-in v1 in 787 ms (6 passed, 0 failed, 0 skipped)
The same flow runs from the web app: Flows shows it; open it for the visual editor and the JSON, and Run… to run it on a target and watch each step on the live screen. A run that fails shows the frame at the failure and the best candidate it found, so you can see whether the screen changed or the element needs another variant.
Next
- Concepts: what targets, sessions, elements, flows, runs and triggers are.
- The attn CLI and the TypeScript SDK: drive the hub from scripts and CI.
- SSH targets: your own desktop or Chrome as a target.
- Webhooks: start this flow from another system, and hear back when it ends.