Repository navigation
Releases: kubed-io/selenium-flow
Release list
v0.3.0
Added
-
run_flowreports progress — the step it is on, and a heartbeat while one waits — so a long run stays alive in clients that abort a silent call, and shows as a progress bar where the client draws one. -
A flow can declare its own
timeout, in seconds, for a run that exists to wait longer than the default. -
A refused tool call says what shape it wanted —
css="button.go"is toldselector={"css": "button.go"}, in the same wordssave_flowuses for a step. -
outlinelists a page's content before its navigation, header, footer and sidebars, says whichregioneach entry is in, and reports atotalso a cut-short map says so. -
VS Code Copilot can read everything without
?resources=off. A client whose model cannot read resources is recognised and givenlist_resourcesandread_resource(uri), and told so in its instructions. -
Resources have readable names, and flow and file names complete in a client's resource and prompt pickers.
-
printkeeps the page as a PDF or as HTML, withlandscapeandbackgroundfor a PDF. -
open_session(insecure=true)accepts a self-signed certificate, for that session's browser. -
The admin UI's session page has Files and Flows tabs, and Files is three rows — Downloads, Screenshots and Files — each cleared the way that fits it.
-
The lightbox steps with ‹ Prev / Next › and the arrow keys, and Keep in it moves on to the next screenshot.
-
A Secrets tab lists every secret's keys and where it may be used — never a value — and the flows that type it, including names no secret answers to.
-
A source install can skip the admin UI. Without
npm --prefix ui run build, the admin page reads "Selenium Flow" and no MCP App is offered; every tool still works.
Changed
-
The admin page is faster: Secrets and flow listings load at once where a couple of dozen flows took over a second, and Files and the session list no longer slow down as screenshots pile up.
-
A flow run's default budget is 120 seconds, down from 300. A flow meant to wait longer sets
timeout. -
A failed MCP call reads like a failed HTTP call — no Selenium stack dump for the caller, and a caller's mistake is one warning line in the log rather than a traceback.
-
BREAKING:
current_session,selenium_flow_skill,list_flows,get_flow,flow_schemaandlist_secretsare gone. Everything they returned is a resource —session://current,skill://selenium-flow/SKILL.md,flow://flows,flow://flows/{name},flow://schema,secret://secrets— read directly or withread_resource.session_filesis listed only to hosts that render MCP Apps. -
BREAKING:
--stateless/STATELESS_HTTPis removed. Run one replica. -
BREAKING:
save_pdfisprint, atPOST /browser/print. -
Screenshots and prints are kept with the session from the start, on any page, and outlive the browser. They need
FLOW_DATA_DIR; a second of the same name is kept asname (1). -
Tool descriptions are half as long, and hints, errors, prompts and the skill name what to read by URI.
-
BREAKING: screenshots are kept in
session://files/screenshotsuntil kept or cleared;session://fileslists Files and names the screenshots and downloads folders, and entries carry auriinstead ofkept. -
BREAKING:
keep_filetakes a file'suri— a screenshot moves into Files, a download is copied — andupload_file(kept=)isupload_file(file=uri), for any file. -
BREAKING:
PUT /files/{name}/keptisPUT /files/screenshots/{name}/keptorPUT /files/downloads/{name}/kept, besideGET /files/screenshotsandGET /files/downloads. -
BREAKING: a server configured for Redis that cannot reach it now refuses to start, rather than running on in-memory sessions until someone notices.
-
BREAKING:
POST /browser/uploadno longer takes asessionfield; a file is always read from the calling session.
Fixed
-
Stopping the server is prompt and quiet with the admin page open: SIGTERM finishes in a fraction of a second instead of waiting out a 2s timeout and logging a traceback.
-
A slow browser action or flow run over HTTP no longer stalls the server; MCP calls, the admin page and
/healthanswer while it runs. -
Cancelling
run_flowstops the run instead of leaving it to drive the browser to the end. -
Error messages keep their XPath.
//input[@name='q']was cut to//name='q']by the credential scrub. -
The flow document schema resolves its selectors; every step's
selectorreferenced a definition the schema did not include. -
A PDF saved from a plain-http page, or a second screenshot on
about:blankor adata:page, is no longer lost. -
A failed resource read no longer quotes the Grid's internal URL, and a caller's bad read is one warning line in the log.
-
GET /secretsis in the OpenAPI spec and the wiki. -
The MCP App now starts. Its SDK ships bundled and pinned, instead of loading from a CDN URL that 404'd.
What's Changed
- A long flow run reports progress, can be cancelled, and sets its own budget by @kferrone in #37
- A refused call names the shape it wanted, and outline puts content first by @kferrone in #38
- Everything to read is a URI, and two tools read one for clients that cannot by @kferrone in #39
- print replaces save_pdf and keeps files directly, read errors are scrubbed, /secrets is documented by @kferrone in #40
- The Hangar: Downloads, Screenshots and Files, tabs, a stepping lightbox, and Secrets by @kferrone in #41
- The admin UI on Svelte: same page, real components, a smaller build by @kferrone in #42
- deps(docker): Build the UI on node:26 by @dependabot[bot] in #43
- deps(npm): Bump @types/node from 24.13.6 to 26.6.1 in /ui by @dependabot[bot] in #44
- Secrets and flow listings load at once: libyaml, and parsed flows cached by content by @kferrone in #45
- deps(npm): Bump @types/node from 26.6.1 to 26.6.2 in /ui in the npm-minor-patch group by @dependabot[bot] in #46
Full Changelog: v0.2.0...v0.3.0
Container image
The image is what this repo ships, and what the cluster runs:
docker pull kubed/selenium-flow:v0.3.0Python package
Not on PyPI. The wheel and sdist are attached to this release:
pip install https://github.com/kubed-io/selenium-flow/releases/download/v0.3.0/kubed_selenium_flow-0.3.0-py3-none-any.whlOr straight from the tag:
pip install git+https://github.com/kubed-io/selenium-flow.git@v0.3.0v0.2.0
Added
-
One endpoint per probe:
/health(liveness, and deliberately independent of the Grid),/started(startup),/ready(readiness, which is the one that asks the Grid) and/info(version, mount, what it is wired to). All four answer at the root as well as under the prefix. -
outlinemaps the page — every element worth acting on, with a selector checked to match exactly one, and whether it can be used. -
A failed click says why, and what to do: a hidden ancestor, something on top of it, no size, off-screen, disabled.
-
A flow can say what must be true.
assertruns JavaScript that has to come backtrue, and fails with the message you wrote. -
Two prompts a person can pick.
repair_flowfixes a flow against the page it now fails on;build_flowwalks a task by hand and saves what worked. -
A failed run says where to read and which prompt repairs it.
-
press_keytakes the browser's key names, single characters and combinations —ArrowLeft,/,Control+a,Shift+Tab. -
dragdrags an element onto another, or by an offset —drag(selector={"css": ".card"}, to={"css": ".done"})ordrag(selector={"css": "input[type=range]"}, by_x=120). Real pointer input, so range sliders respond, and on Chrome it drives native HTML5 drag-and-drop as well. -
interacttakesglide— the pointer travels in steps instead of jumping, for interfaces that watch movement rather than arrival. -
asserttakesstable_for— the answer must still be true that many seconds later, which is what a guard needs and what asking until true cannot give it. -
upload_filetakeskept, the name of a filekeep_filekept — download an export from one site and upload it to another without the bytes passing through you. -
open_session(fresh=true)opens on a blank page instead of returning to the one your session was last on.
Changed
-
The server's instructions name the embedded skill, so a client is told where the how-to lives when it connects rather than after its first failure. Omitted when the skill is not being served.
-
BREAKING:
ROUTE_PREFIXmounts the whole server, and defaults to/. Every tree is fixed beneath it —/browser,/flows,/files,/admin,/mcp,/openapi.yaml— where it used to rename/browserwhile everything else stayed put. -
The admin UI is at the server root, with its views after the hash (
#/sessions/<name>), so a view survives a reload and can be linked to./adminredirects there and remains the API the page calls. -
BREAKING: every session is named by its caller. Add
?session=<name>to the URL or send anX-Session-Keyheader; sending both, or neither, is refused. There is nosession_idon any tool, in any request body, or in any result — call again with the same name to get the same browser back. -
BREAKING: the HTTP surface is REST.
GET /flows/{name},PUTto save it,DELETEto remove it,POST /flows/{name}/runsto run it;POST /browseropens your browser,DELETE /browserends it,GET /browsersays what you are holding; a mouse action is a path —POST /browser/interact/click. Files areGET /filesandPUT /files/{name}/kept. -
BREAKING:
xpathandcssare oneselector.interact(selector={"css": "button.go"}), anddragtakesselectorandto. Exactly one of the two, never both — and never a fallback from one to the other. -
SAVED_SESSIONSis gone, along with the two session modes it switched between. There is one contract now. -
A browser opened over HTTP is a session like any other — it appears in the admin list, slides its TTL, and is reopened where it left off after the Grid reaps it.
-
Screenshots are kept with the session's files by default, and come back with a link anyone can open — signed and time-limited when the server has a token, a plain path when authentication is off.
screenshot(save=false)opts out, and a page that refuses the download returnsfile_errorwith the image. -
BREAKING: a stored file is now described the way
session_filesdescribes one —createdinstead ofcreationTime, pluscontent_type,image,kept,urlandabsolute_url. Affects thefileinscreenshotandsave_pdfresults. -
Tools list their fixed choices in their schemas — every
interact,dialogandframeaction, andopen_session's browsers — so a client can show them, and a flow naming one that does not exist is refused when it is saved. -
Every mouse gesture moves the pointer onto the element first, so after a click the pointer is on what you clicked and a
:hovermenu stays open across it. The click itself is unchanged, so a covered target is still refused. -
outlinesays what reveals a hidden element, not just what blocks it —revealed_bycarries the trigger's selector andopen_withsays whether to click it or hover it. -
A run report says where each step went when the page changed, without
verbose. -
A saved flow that starts on whatever page you happen to be on is warned about — not refused.
Fixed
-
An unreachable Grid no longer writes its credentials into the log. The URL is stripped from every failure message and from the traceback, not only from the one failure known to quote it.
-
A flow step that captures a file says where it went. A screenshot's link is in the run report without needing
return: true— a capture nobody can find cannot show anyone what it saw. -
The admin flow panel shows a selector as one expression —
css button.go— instead of a raw JSON object. -
The Grid's URL no longer appears with its credentials in
/readyor/info, which answer to anyone. -
A click on a page that repaints itself is no longer racy — the element is found again and the action retried once, rather than failing with a stale reference. Found by the admin page's own session list, which refreshes every two seconds.
-
The admin page lists sessions again on Redis — it went blank once any browser had been pointed at something.
-
A screenshot of a page the browser will not download from no longer costs fifteen seconds. It says so straight away and still returns the image.
-
A saved file is named for what it actually is —
screenshot(filename="chart.pdf")is stored as a PNG. -
A hover onto something the pointer was already on did nothing and reported success. The pointer now steps aside first, and the result says
nudged. -
The advice for a hidden menu said "hover" even when the page said it opens on click. It reads
aria-expandednow and says which. -
outlineno longer offers a selector built from a container's concatenated descendant text, and no longer skips an<a>for having nohref. -
An unkept file says what would keep it —
keep_withon every entry whose link dies with the browser. -
A glide to something below the fold works. It used to send coordinates outside the window, which WebDriver refuses, and the whole move was lost.
-
A drag step with no element or no destination is refused when it is saved, rather than at run time.
-
upload_filenaming a kept file that is not there answers 400, like naming apaththat is not there — not 500. -
POST /browser/uploadtakessession, naming the library a kept file belongs to — the same way/files/keepalready does, so a file kept into a named session can be uploaded back out of it. -
A drag that failed because the browser had gone says so — 404 with the fix, instead of 400 blaming the element's shape.
-
A drag whose source is taller than the window works. The pointer's position is now where WebDriver actually puts it — the element's in-view center — rather than the center of a rectangle running off the screen.
-
A flow that uploads a kept file reads it from the library the run belongs to, not from the shared
globalone. -
assertrefuses astable_forit cannot read instead of silently dropping the stability check, and no longer accepts an answer a slow script returned afterwait_timeouthad passed.
What's Changed
- ci(deps): Bump actions/upload-artifact from 5 to 7 by @dependabot[bot] in #24
- Tools show their choices, and press_key takes what a browser calls a key by @kferrone in #25
- assert: a flow can say what must be true by @kferrone in #26
- Every screenshot is saved, and the link is what you hand a person by @kferrone in #28
- outline: what is on the page, and whether it can be used by @kferrone in #29
- Two prompts a person can pick, and a failed run that names one by @kferrone in #30
- The pointer the server remembers, and the last of the pilot reports by @kferrone in #31
- JavaScript in js/, Copilot's third pass, and the session design by @kferrone in #32
- The server flies its own admin page, and the list is back by @kferrone in https://github.com/kubed-io/selenium-fl...
v0.1.0
0.0.2 could drive a browser. This one lets an agent keep what it worked out.
Save a sequence of steps as a named flow, give it parameters, and run the whole
thing in one call — including steps that type a credential the model is never
shown. The admin UI grew a panel for reading and editing them.
Added
-
Saved flows. Save a sequence of steps under a name and run the whole thing in one call with
run_flow— a twelve-step form becomes one call instead of twelve. SetFLOW_DATA_DIRto turn them on. -
Flows take parameters. Declare them and write
${name}in any argument of any step —url: ${site}/orders/${id}— so one flow serves every account and every environment. -
Type a secret you never see. Give
writeasecretnaming one instead oftext, and the server reads it and types it, in a flow step or a single call. It never becomes an argument string, so it cannot be assembled into one by mistake. -
A secrets catalogue. Point
SECRETS_DIRSat a directory per secret and a file per key — the shape Kubernetes already mounts — andlist_secretsshows an agent what it may use. A secret can be leashed to the sites it is allowed on. Values are never returned by anything. -
A shared
globalflow library every session can list and run, which only an operator can change. -
Kept files.
keep_filecopies a download out of the browser so it survives the browser being reaped, switched or ended, andsession_fileslists both kinds together. -
The admin UI reads and edits flows. Every session's flows beside its files: what a flow takes, what it does, and what any step or parameter holds. Edit the YAML, move a flow to or from
global, or delete it. -
CSS selectors. Every tool that acts on an element now takes
cssas well asxpath— pass one or the other. -
A flow run answers with the steps you marked
return: true— any number of them, and nothing else. -
screenshot(save=true)now tells you what the file was called, which is the namekeep_filetakes. -
The built-in skill teaches flows and secrets, and the wiki documents them with a page per endpoint.
Changed
-
HTTP errors now say whose fault they are:
400for a request you can fix,404for a browser that has ended,503for a Grid that is unreachable or full. They were all500. -
Clear downloadslists exactly what it will remove before it removes it. -
Error messages no longer carry the driver's stack trace.
Fixed
- A browser stays usable after logging in. Chrome's "Save password?" prompt took the keyboard and mouse for itself, so every click and keystroke after a login silently did nothing — while every call still reported success.
What's Changed
- fix: classify HTTP failures by what the caller should do by @kferrone in #8
- Flows: Chapter 1, and css selectors as the first slab by @kferrone in #9
- Flows E1: the store, and the naming rules around it by @kferrone in #10
- Flows E2: the document, its CRUD, and the plan for secrets by @kferrone in #11
- Flows E3: run a saved flow by @kferrone in #12
- Secrets E7: the catalogue, plus three leaks the last round missed by @kferrone in #13
- Secrets E9: a flow can type a secret it is never shown by @kferrone in #14
- Ground school (E6 + E10): the skill teaches flows and secrets by @kferrone in #15
- Kept files E4: a file that outlives the browser that made it by @kferrone in #16
- Flows: the shared global library is read-only to every agent by @kferrone in #17
- Admin UI: the page the Penpot file designed by @kferrone in #18
- Wiki: flows, secrets and kept files exist now by @kferrone in #19
- Flows: a parameter is text, a secret is structural by @kferrone in #20
- Admin UI: flows go live, counts stop lying, and a cleanup pass by @kferrone in #21
- Admin UI: a flow shows what it takes, and the image builds 4x faster by @kferrone in #22
- Flows: defaults are applied, a script reads as code, and an error names its escape by @kferrone in #23
Full Changelog: v0.0.2...v0.1.0
Container image
The image is what this repo ships, and what the cluster runs:
docker pull kubed/selenium-flow:v0.1.0Python package
Not on PyPI. The wheel and sdist are attached to this release:
pip install https://github.com/kubed-io/selenium-flow/releases/download/v0.1.0/kubed_selenium_flow-0.1.0-py3-none-any.whlOr straight from the tag:
pip install git+https://github.com/kubed-io/selenium-flow.git@v0.1.0v0.0.2
The first release. Everything is new, which is why there is no Changed or
Fixed below — both are relative to a version somebody is running, and there
isn't one.
Added
-
Drive a real Chrome or Firefox browser from an agent. Fourteen actions: open and end a browser, navigate, click, hover, type, press keys, read an element, screenshot, print to PDF, upload a file, switch frames, answer dialogs, resize, and run JavaScript.
-
Every action is an MCP tool and a plain HTTP endpoint, one to one. Hand the whole job to an agent over MCP, or drive the same actions yourself from an n8n HTTP node when you want exact control.
-
The browser stays open between calls, keeping its page, cookies and scroll position — it lives on the Grid, so a restart of this server does not lose it.
-
The server can hold your browser for you. Name your session with
?session=<name>on the MCP URL or anX-Session-Keyheader, andsession_idbecomes optional on every call. -
A session outlives its browser. When the Grid reaps one, or an operator ends it, the session keeps the browser choice, the window size and the page it was on — so
open_session()with no arguments puts you back where you were. -
Chrome or Firefox, chosen per session with
open_session(browser="firefox"). Every other action behaves identically on either. -
session://currentsays what you are holding: the browser, the page, the window size, whether you are inside a frame, and whether the Grid still has it. Reading it never opens a browser. -
Screenshots come back as images an agent can see — the viewport, one element, or the whole scrollable page.
-
save_pdfprints with the browser's own print engine, so the text stays selectable and the whole document is included rather than just the viewport. -
Everything the browser downloads is kept with the session, alongside anything saved with
screenshot(save=true)orsave_pdf, and listed assession://files. -
Signed file URLs, so a screenshot can be shown in a chat transcript or an
<img>tag rather than described. Rotating the server token revokes every link. -
An admin UI at
/admin: live sessions, what each has downloaded, clickable thumbnails, the Grid's own console, and buttons to clear a session's files or end its browser. It updates itself as things change. -
Bearer token auth on both surfaces.
/healthstays open so a kubelet can probe a pod that has no credentials, and reports Grid readiness rather than just process liveness. -
GET /openapi.yamldescribing the HTTP surface, generated from the MCP tool schemas so the two contracts cannot drift. -
An Agent Skill ships inside the wheel, written as an index so an agent loads only the reference its task needs.
-
MCP Apps components: a host that implements the extension renders a file grid instead of JSON.
-
Sessions in memory or Redis, and
--statelessfor running more than one replica. -
Ships as the
kubed/selenium-flowimage, and as a wheel attached to each release. The manual is the wiki.
What's Changed
- ci: static analysis, audits and a package step for the PR flow by @kferrone in #1
- fix(tests): run on Python 3.10, which the matrix proved they never did by @kferrone in #3
- deps(pip): Update redis requirement from <7,>=5.0 to >=5.0,<9 by @dependabot[bot] in #2
- refactor: a quality pass over code, tests and the lint that guards them by @kferrone in #4
- chore: prepare the first release by @kferrone in #5
- ci: stop the release notes being silently dropped, and two doc corrections by @kferrone in #7
New Contributors
- @kferrone made their first contribution in #1
- @dependabot[bot] made their first contribution in #2
Full Changelog: v0.0.1...v0.0.2
Container image
The image is what this repo ships, and what the cluster runs:
docker pull kubed/selenium-flow:v0.0.2Python package
Not on PyPI. The wheel and sdist are attached to this release:
pip install https://github.com/kubed-io/selenium-flow/releases/download/v0.0.2/kubed_selenium_flow-0.0.2-py3-none-any.whlOr straight from the tag:
pip install git+https://github.com/kubed-io/selenium-flow.git@v0.0.2