Skip to content

Releases: kubed-io/selenium-flow

v0.3.0

Choose a tag to compare

@kubed-io kubed-io released this 26 Sep 16:00
bce36b8

Publish
Image Builder
Tests
Quality

Added

  • run_flow reports 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 told selector={"css": "button.go"}, in the same words save_flow uses for a step.

  • outline lists a page's content before its navigation, header, footer and sidebars, says which region each entry is in, and reports a total so 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 given list_resources and read_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.

  • print keeps the page as a PDF or as HTML, with landscape and background for 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_schema and list_secrets are 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 with read_resource. session_files is listed only to hosts that render MCP Apps.

  • BREAKING: --stateless / STATELESS_HTTP is removed. Run one replica.

  • BREAKING: save_pdf is print, at POST /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 as name (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/screenshots until kept or cleared; session://files lists Files and names the screenshots and downloads folders, and entries carry a uri instead of kept.

  • BREAKING: keep_file takes a file's uri — a screenshot moves into Files, a download is copied — and upload_file(kept=) is upload_file(file=uri), for any file.

  • BREAKING: PUT /files/{name}/kept is PUT /files/screenshots/{name}/kept or PUT /files/downloads/{name}/kept, beside GET /files/screenshots and GET /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/upload no longer takes a session field; 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 /health answer while it runs.

  • Cancelling run_flow stops 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 selector referenced a definition the schema did not include.

  • A PDF saved from a plain-http page, or a second screenshot on about:blank or a data: 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 /secrets is 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.0

Python 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.whl

Or straight from the tag:

pip install git+https://github.com/kubed-io/selenium-flow.git@v0.3.0

v0.2.0

Choose a tag to compare

@kubed-io kubed-io released this 16 Sep 02:54
d5bf435

Publish
Image Builder
Tests
Quality

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.

  • outline maps 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. assert runs JavaScript that has to come back true, and fails with the message you wrote.

  • Two prompts a person can pick. repair_flow fixes a flow against the page it now fails on; build_flow walks a task by hand and saves what worked.

  • A failed run says where to read and which prompt repairs it.

  • press_key takes the browser's key names, single characters and combinations — ArrowLeft, /, Control+a, Shift+Tab.

  • drag drags an element onto another, or by an offset — drag(selector={"css": ".card"}, to={"css": ".done"}) or drag(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.

  • interact takes glide — the pointer travels in steps instead of jumping, for interfaces that watch movement rather than arrival.

  • assert takes stable_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_file takes kept, the name of a file keep_file kept — 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_PREFIX mounts the whole server, and defaults to /. Every tree is fixed beneath it — /browser, /flows, /files, /admin, /mcp, /openapi.yaml — where it used to rename /browser while 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. /admin redirects there and remains the API the page calls.

  • BREAKING: every session is named by its caller. Add ?session=<name> to the URL or send an X-Session-Key header; sending both, or neither, is refused. There is no session_id on 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}, PUT to save it, DELETE to remove it, POST /flows/{name}/runs to run it; POST /browser opens your browser, DELETE /browser ends it, GET /browser says what you are holding; a mouse action is a path — POST /browser/interact/click. Files are GET /files and PUT /files/{name}/kept.

  • BREAKING: xpath and css are one selector. interact(selector={"css": "button.go"}), and drag takes selector and to. Exactly one of the two, never both — and never a fallback from one to the other.

  • SAVED_SESSIONS is 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 returns file_error with the image.

  • BREAKING: a stored file is now described the way session_files describes one — created instead of creationTime, plus content_type, image, kept, url and absolute_url. Affects the file in screenshot and save_pdf results.

  • Tools list their fixed choices in their schemas — every interact, dialog and frame action, and open_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 :hover menu stays open across it. The click itself is unchanged, so a covered target is still refused.

  • outline says what reveals a hidden element, not just what blocks it — revealed_by carries the trigger's selector and open_with says 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 /ready or /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-expanded now and says which.

  • outline no longer offers a selector built from a container's concatenated descendant text, and no longer skips an <a> for having no href.

  • An unkept file says what would keep it — keep_with on 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_file naming a kept file that is not there answers 400, like naming a path that is not there — not 500.

  • POST /browser/upload takes session, naming the library a kept file belongs to — the same way /files/keep already 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 global one.

  • assert refuses a stable_for it cannot read instead of silently dropping the stability check, and no longer accepts an answer a slow script returned after wait_timeout had 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...
Read more

v0.1.0

Choose a tag to compare

@kubed-io kubed-io released this 12 Sep 20:33
bd39825

Publish
Image Builder
Tests
Quality

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. Set FLOW_DATA_DIR to 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 write a secret naming one instead of text, 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_DIRS at a directory per secret and a file per key — the shape Kubernetes already mounts — and list_secrets shows 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 global flow library every session can list and run, which only an operator can change.

  • Kept files. keep_file copies a download out of the browser so it survives the browser being reaped, switched or ended, and session_files lists 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 css as well as xpath — 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 name keep_file takes.

  • 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: 400 for a request you can fix, 404 for a browser that has ended, 503 for a Grid that is unreachable or full. They were all 500.

  • Clear downloads lists 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.0

Python 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.whl

Or straight from the tag:

pip install git+https://github.com/kubed-io/selenium-flow.git@v0.1.0

v0.0.2

Choose a tag to compare

@kubed-io kubed-io released this 10 Sep 20:41
48c5a0a

Publish
Image Builder
Tests
Quality

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 an X-Session-Key header, and session_id becomes 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://current says 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_pdf prints 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) or save_pdf, and listed as session://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. /health stays open so a kubelet can probe a pod that has no credentials, and reports Grid readiness rather than just process liveness.

  • GET /openapi.yaml describing 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 --stateless for running more than one replica.

  • Ships as the kubed/selenium-flow image, 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

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.2

Python 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.whl

Or straight from the tag:

pip install git+https://github.com/kubed-io/selenium-flow.git@v0.0.2