-
Notifications
You must be signed in to change notification settings - Fork 0
Flows
A flow is a list of tool calls saved under a name. run_flow replays the
whole list server-side, in order, in the browser you already have.
The saving is not bandwidth. It is decisions: a twelve-step form becomes one call instead of twelve, and it replays a sequence somebody already got right instead of re-deriving the selectors every time.
Flows are off unless the server has somewhere to put them. Set
FLOW_DATA_DIR — see Deployment.
name: sign-up
description: Create an account on the demo site
steps:
- tool: navigate
params:
url: https://demo.example.com/join
- tool: write
id: email
params:
css: "#email"
text: a@example.com
- tool: interact
params:
action: click
css: button[type=submit]
- tool: extract
params:
css: h1
return: trueparams is exactly the arguments you would pass that tool directly.
Nothing is renamed and nothing is added. A step may also carry:
| Key | Does |
|---|---|
id |
a name for the step, shown in the report |
note |
a comment for the next reader |
onError |
abort (default) or continue past a failure |
return |
include this step's full result in the report |
Not steps: open_session and end_browser.
A flow runs in the browser the caller already holds, which is what lets the same
flow run on Firefox unedited. Never put session_id in a step either — the run
supplies it.
flow_schema lists every tool that may be a step and what each
takes. It is derived from the live tools, so it cannot describe a step that
would not run. save_flow validates against the same schemas and
reports every problem at once, so a broken flow is refused when you save it
rather than halfway through a form.
Declare them as JSON Schema and point a step at one with value_from:
parameters:
type: object
required: [email]
properties:
email: {type: string}
steps:
- tool: write
params:
css: "#email"
value_from:
param: emailThen run_flow(name="sign-up", params={"email": "a@example.com"}).
There is no templating. Nothing like {{email}} is ever substituted into a
string: a value arrives through value_from or not at all, and value_from
names exactly one source.
An action opens exactly one argument to a binding — the one the step is about:
| Action | Fills |
|---|---|
write |
text |
navigate |
url |
upload_file |
path |
A binding satisfies that argument, so giving it literally as well is refused rather than one of the two quietly winning.
Mark a parameter writeOnly: true when the caller supplies it but it must not
come back out — it is typed as usual and hidden from the report.
For a password, do not use a parameter at all. Name a secret where the value would go, and the server reads it and types it:
- tool: write
params:
css: "#password"
value_from:
secret:
name: the-internet
key: passwordThe value never passes through the caller, and the step's line in the report
reads text=<hidden> rather than the characters. A parameter shows its
length instead — text=18 chars — which is the difference between a value
that is merely varying and one that is guarded. See Secrets.
This depends on your session name, and it is not guessable from any schema.
| You are | You save into | You can run |
|---|---|---|
named (?session= / X-Session-Key) |
your own library | yours, plus the shared library |
| stdio | its own library | its own, plus the shared library |
unnamed, or named global
|
nowhere — you cannot save | the shared library |
global is read-only. Every session lists and runs what is in it and no
session may change it: save_flow and delete_flow refuse. That is not
tidiness — the shared library is live, so a flow one caller rewrote or deleted
would change or vanish underneath another part-way through running it. Only an
operator moves a flow into global, from the admin UI.
So name your session before you save anything. Without a name you have no
library of your own, and save_flow says so rather than writing somewhere you
would never look again.
Where a name exists in both, yours wins, and list_flows marks
each entry shared: true or false so you can tell which one will run.
Open a browser first — a flow never opens one. To run the same flow on Firefox,
open_session(browser="firefox") and run it again, unchanged.
The report has one line per step plus where the browser ended up. It stops at
the first failing step unless that step says onError: continue, and says which
step stopped it, the error, and the page it was on — so check that page with
extract before deciding the selector is wrong.
A run answers with the steps that said they were the answer. Mark each one
return: true — any number may, and nothing else in the report carries a
result. A flow whose point is its closing extract needs the flag on that
extract; without it the step is reported as having run and its result is
discarded.
Running a flow you cannot edit — one from the shared library — pass verbose
and read every step instead.
url_redacted: true means the page it ended on carried a guarded value, so the
URL shown is scrubbed and is not a real address. Do not navigate back to it.
The action pages are generated from openapi.yaml, which is itself generated from the live MCP tool schemas — so they describe the server that shipped, not the one someone remembered. Prose belongs in wiki-notes/<tool>.md in the repo.
selenium-flow · MIT
Start here
Guides
Lifecycle
Going places
Doing things
Getting things out