Pure functions for action log management and SRT subtitle generation.
The action log records user-facing browser commands with timestamps, enabling SRT subtitle export for video overlays. All functions are pure — no atoms, no side effects, no Playwright dependency.
Action entry shape: {:idx long ;; 1-based sequence number :timestamp long ;; epoch millis (System/currentTimeMillis) :action string ;; command name ("click", "navigate", etc.) :target string|nil ;; ref or selector ("@e12345") :args map|nil ;; additional arguments :session string|nil} ;; session name
SRT format: 1 00:00:01,200 --> 00:00:03,500 click @e12345
2 00:00:03,500 --> 00:00:05,800 fill @e67890 "search text"
Pure functions for action log management and SRT subtitle generation.
The action log records user-facing browser commands with timestamps,
enabling SRT subtitle export for video overlays. All functions are
pure — no atoms, no side effects, no Playwright dependency.
Action entry shape:
{:idx long ;; 1-based sequence number
:timestamp long ;; epoch millis (System/currentTimeMillis)
:action string ;; command name ("click", "navigate", etc.)
:target string|nil ;; ref or selector ("@e12345")
:args map|nil ;; additional arguments
:session string|nil} ;; session name
SRT format:
1
00:00:01,200 --> 00:00:03,500
click @e12345
2
00:00:03,500 --> 00:00:05,800
fill @e67890 "search text"In-test Allure API for enriching test reports with steps, metadata, screenshots, and attachments.
All functions are no-ops when *context* is nil, meaning they are
safe to call even when not running under the Allure reporter.
Usage in tests:
(ns my-app.login-test (:require [com.blockether.spel.allure :as allure] [com.blockether.spel.page :as page] [com.blockether.spel.locator :as locator]))
(defdescribe login-flow (allure/epic "Authentication") (allure/feature "Login") (allure/severity :critical)
(it "logs in with valid credentials"
(allure/step "Navigate to login page"
(page/navigate page "https://example.org/login"))
(allure/step "Enter credentials"
(allure/parameter "username" "admin")
(locator/fill (locator/locator page "#username") "admin")
(locator/fill (locator/locator page "#password") "secret"))
(allure/step "Submit and verify"
(locator/click (locator/locator page "button[type=submit]"))
(allure/screenshot page "After login"))))
In-test Allure API for enriching test reports with steps, metadata,
screenshots, and attachments.
All functions are no-ops when `*context*` is nil, meaning they are
safe to call even when not running under the Allure reporter.
Usage in tests:
(ns my-app.login-test
(:require [com.blockether.spel.allure :as allure]
[com.blockether.spel.page :as page]
[com.blockether.spel.locator :as locator]))
(defdescribe login-flow
(allure/epic "Authentication")
(allure/feature "Login")
(allure/severity :critical)
(it "logs in with valid credentials"
(allure/step "Navigate to login page"
(page/navigate page "https://example.org/login"))
(allure/step "Enter credentials"
(allure/parameter "username" "admin")
(locator/fill (locator/locator page "#username") "admin")
(locator/fill (locator/locator page "#password") "secret"))
(allure/step "Submit and verify"
(locator/click (locator/locator page "button[type=submit]"))
(allure/screenshot page "After login"))))Allure 3 reporter for Lazytest with embedded Playwright trace viewer.
Writes JSON result files to allure-results/, then automatically generates the full HTML report to allure-report/ using Allure 3 CLI (prefers a global install, falls back to npx with pinned 3.3.1). The report embeds a local Playwright trace viewer so trace attachments load instantly without trace.playwright.dev.
Usage: clojure -M:test --output com.blockether.spel.allure-reporter/allure clojure -M:test --output nested --output com.blockether.spel.allure-reporter/allure
Output directory defaults to allure-results/. Override with: -Dlazytest.allure.output=path/to/dir LAZYTEST_ALLURE_OUTPUT=path/to/dir
Allure 3 reporter for Lazytest with embedded Playwright trace viewer. Writes JSON result files to allure-results/, then automatically generates the full HTML report to allure-report/ using Allure 3 CLI (prefers a global install, falls back to npx with pinned 3.3.1). The report embeds a local Playwright trace viewer so trace attachments load instantly without trace.playwright.dev. Usage: clojure -M:test --output com.blockether.spel.allure-reporter/allure clojure -M:test --output nested --output com.blockether.spel.allure-reporter/allure Output directory defaults to allure-results/. Override with: -Dlazytest.allure.output=path/to/dir LAZYTEST_ALLURE_OUTPUT=path/to/dir
Page annotation with ref labels, bounding boxes, and dimensions.
Injects CSS overlays directly into the page DOM. Overlays persist until
explicitly removed with remove-overlays!. No AWT dependency — works in
GraalVM native-image without any java.awt configuration.
Usage: (def snap (snapshot/capture-snapshot page)) (inject-overlays! page (:refs snap)) ;; overlays now visible on page ;; ... inspect, screenshot, etc. ... (remove-overlays! page) ;; clean up
Page annotation with ref labels, bounding boxes, and dimensions. Injects CSS overlays directly into the page DOM. Overlays persist until explicitly removed with `remove-overlays!`. No AWT dependency — works in GraalVM native-image without any java.awt configuration. Usage: (def snap (snapshot/capture-snapshot page)) (inject-overlays! page (:refs snap)) ;; overlays now visible on page ;; ... inspect, screenshot, etc. ... (remove-overlays! page) ;; clean up
Playwright test assertions - LocatorAssertions, PageAssertions, APIResponseAssertions.
Entry point is assert-that which returns the appropriate assertions
object for the given Playwright type. Chain with assertion functions
and use not- variants for negation.
All assertion functions wrap calls in safe to return anomaly maps
on assertion failure rather than throwing.
Playwright test assertions - LocatorAssertions, PageAssertions, APIResponseAssertions. Entry point is `assert-that` which returns the appropriate assertions object for the given Playwright type. Chain with assertion functions and use `not-` variants for negation. All assertion functions wrap calls in `safe` to return anomaly maps on assertion failure rather than throwing.
CI utilities for assembling Allure report sites.
Replaces Python scripts in GitHub Actions workflow with native Clojure.
Usage: spel ci-assemble --help spel ci-assemble --site-dir=gh-pages-site --run=123 ...
CI utilities for assembling Allure report sites. Replaces Python scripts in GitHub Actions workflow with native Clojure. Usage: spel ci-assemble --help spel ci-assemble --site-dir=gh-pages-site --run=123 ...
CLI client for the spel daemon.
Parses command-line arguments into JSON commands, sends them to the daemon over a Unix domain socket, and pretty-prints the results.
If the daemon isn't running, it auto-starts one in the background.
Usage: spel open https://example.org spel snapshot spel click @ref spel fill @ref "search text" spel screenshot shot.png spel close
CLI client for the spel daemon. Parses command-line arguments into JSON commands, sends them to the daemon over a Unix domain socket, and pretty-prints the results. If the daemon isn't running, it auto-starts one in the background. Usage: spel open https://example.org spel snapshot spel click @ref spel fill @ref "search text" spel screenshot shot.png spel close
Transforms Playwright JSONL recordings into idiomatic Clojure test code.
Reads JSONL produced by playwright codegen --target=jsonl and emits
Clojure code using the com.blockether.spel API.
Two usage modes:
A) Library:
(set! warn-on-reflection true)
(require '[com.blockether.spel.codegen :as codegen])
(codegen/jsonl->clojure "recording.jsonl")
(codegen/jsonl-str->clojure jsonl-string {:format :script})
B) CLI: clojure -M -m com.blockether.spel.codegen recording.jsonl clojure -M -m com.blockether.spel.codegen --format=script recording.jsonl cat recording.jsonl | clojure -M -m com.blockether.spel.codegen
Workflow: clojure -M -m com.blockether.spel.cli codegen --target=jsonl -o recording.jsonl https://example.org clojure -M -m com.blockether.spel.codegen recording.jsonl > test/my_test.clj
Any unrecognized action, unsupported signal, or unimplemented feature causes an IMMEDIATE hard error with details about what failed.
Transforms Playwright JSONL recordings into idiomatic Clojure test code.
Reads JSONL produced by `playwright codegen --target=jsonl` and emits
Clojure code using the com.blockether.spel API.
Two usage modes:
A) Library:
(set! *warn-on-reflection* true)
(require '[com.blockether.spel.codegen :as codegen])
(codegen/jsonl->clojure "recording.jsonl")
(codegen/jsonl-str->clojure jsonl-string {:format :script})
B) CLI:
clojure -M -m com.blockether.spel.codegen recording.jsonl
clojure -M -m com.blockether.spel.codegen --format=script recording.jsonl
cat recording.jsonl | clojure -M -m com.blockether.spel.codegen
Workflow:
clojure -M -m com.blockether.spel.cli codegen --target=jsonl -o recording.jsonl https://example.org
clojure -M -m com.blockether.spel.codegen recording.jsonl > test/my_test.clj
Any unrecognized action, unsupported signal, or unimplemented feature
causes an IMMEDIATE hard error with details about what failed.Config file loader for spel.json — agent-browser parity.
Layered precedence (lowest → highest):
~/.spel/config.json (user-level defaults)./spel.json (project-level overrides)SPEL_* environment vars (shell overrides)Config values use camelCase keys (headed, userAgent, allowedDomains)
and are normalized to the same kebab-case keywords the CLI flag parser
already uses, so a single merged map feeds both layers.
Unknown keys are silently ignored for forward compatibility. The
extensions array merges by concatenation (user + project) rather than
replacement so you can add project extensions on top of your user config.
Config file loader for `spel.json` — agent-browser parity. Layered precedence (lowest → highest): 1. `~/.spel/config.json` (user-level defaults) 2. `./spel.json` (project-level overrides) 3. `SPEL_*` environment vars (shell overrides) 4. CLI flags (explicit per-invocation) Config values use camelCase keys (`headed`, `userAgent`, `allowedDomains`) and are normalized to the same kebab-case keywords the CLI flag parser already uses, so a single merged map feeds both layers. Unknown keys are silently ignored for forward compatibility. The `extensions` array merges by concatenation (user + project) rather than replacement so you can add project extensions on top of your user config.
Clojure vars wrapping Playwright enum values as flat, documented names.
Eliminates the need for Java enum interop in eval-sci mode.
Flat naming: constants/<category>-<value>.
Usage: (require '[com.blockether.spel.constants :as constants]) (page/wait-for-load-state pg constants/load-state-networkidle) (page/navigate pg url {:wait-until constants/wait-until-commit})
Clojure vars wrapping Playwright enum values as flat, documented names.
Eliminates the need for Java enum interop in eval-sci mode.
Flat naming: `constants/<category>-<value>`.
Usage:
(require '[com.blockether.spel.constants :as constants])
(page/wait-for-load-state pg constants/load-state-networkidle)
(page/navigate pg url {:wait-until constants/wait-until-commit})Playwright lifecycle management and browser launching.
Entry point for all Playwright operations. Creates Playwright instances and launches browsers (Chromium, Firefox, WebKit).
Usage: (with-playwright [pw (create)] (with-browser [browser (launch-chromium pw {:headless true})] (with-page [page (new-page browser)] (navigate page "https://example.org") (text-content page "h1"))))
All operations return anomaly maps on failure instead of throwing exceptions.
Playwright lifecycle management and browser launching.
Entry point for all Playwright operations. Creates Playwright instances
and launches browsers (Chromium, Firefox, WebKit).
Usage:
(with-playwright [pw (create)]
(with-browser [browser (launch-chromium pw {:headless true})]
(with-page [page (new-page browser)]
(navigate page "https://example.org")
(text-content page "h1"))))
All operations return anomaly maps on failure instead of throwing exceptions.Background daemon that keeps a Playwright browser alive between CLI calls.
Listens on a Unix domain socket for JSON commands, executes them against the browser, and returns JSON responses. Each command is one JSON line; each response is one JSON line.
Usage: (start-daemon! {:session "default" :headless true}) ;; blocks (daemon-running? "default") ;; check (stop-daemon!) ;; cleanup
Background daemon that keeps a Playwright browser alive between CLI calls.
Listens on a Unix domain socket for JSON commands, executes them against
the browser, and returns JSON responses. Each command is one JSON line;
each response is one JSON line.
Usage:
(start-daemon! {:session "default" :headless true}) ;; blocks
(daemon-running? "default") ;; check
(stop-daemon!) ;; cleanupEmbedded HTTP server for the spel Observability Dashboard.
Serves a single-page web UI that shows live browser state: viewport screenshot, action log, console messages, and uncaught errors.
Uses only JDK built-in com.sun.net.httpserver — no external deps, GraalVM native-image safe.
Usage: (start-dashboard! 4848 state-fn) ;; starts HTTP server (stop-dashboard!) ;; stops it (dashboard-running?) ;; status check
Embedded HTTP server for the spel Observability Dashboard. Serves a single-page web UI that shows live browser state: viewport screenshot, action log, console messages, and uncaught errors. Uses only JDK built-in com.sun.net.httpserver — no external deps, GraalVM native-image safe. Usage: (start-dashboard! 4848 state-fn) ;; starts HTTP server (stop-dashboard!) ;; stops it (dashboard-running?) ;; status check
Datafy/nav extensions for Playwright Java objects.
Require this namespace to enable clojure.core.protocols/Datafiable
for key Playwright classes. After requiring, (clojure.datafy/datafy obj)
returns a Clojure map representation of the object.
Datafied classes:
Datafy/nav extensions for Playwright Java objects. Require this namespace to enable `clojure.core.protocols/Datafiable` for key Playwright classes. After requiring, `(clojure.datafy/datafy obj)` returns a Clojure map representation of the object. Datafied classes: - Page, Browser, BrowserContext, BrowserType - Request, Response, APIResponse - ConsoleMessage, Download, WebError, WebSocketFrame, Worker - ElementHandle, Locator (basic info) - PlaywrightException, TimeoutError
No vars found in this namespace.
Device and viewport presets for browser context emulation.
Provides keyword-based device presets (e.g. :iphone-14, :pixel-7)
and viewport presets (e.g. :desktop-hd, :tablet, :mobile) that
can be used with with-testing-page and new-context options.
Each device preset includes: viewport dimensions, device-scale-factor, is-mobile, has-touch, and user-agent string.
Viewport presets are simpler — just :width and :height.
Device and viewport presets for browser context emulation. Provides keyword-based device presets (e.g. `:iphone-14`, `:pixel-7`) and viewport presets (e.g. `:desktop-hd`, `:tablet`, `:mobile`) that can be used with `with-testing-page` and `new-context` options. Each device preset includes: viewport dimensions, device-scale-factor, is-mobile, has-touch, and user-agent string. Viewport presets are simpler — just :width and :height.
External Playwright driver management for native-image builds.
Instead of bundling Playwright's ~600MB Node.js driver in the native binary, this module downloads and caches it on first use.
Cache: ~/.cache/spel/<version>/<platform>/ CDN: https://cdn.playwright.dev/builds/driver/playwright-<version>-<platform>.zip
Configuration (checked in order):
External Playwright driver management for native-image builds. Instead of bundling Playwright's ~600MB Node.js driver in the native binary, this module downloads and caches it on first use. Cache: ~/.cache/spel/<version>/<platform>/ CDN: https://cdn.playwright.dev/builds/driver/playwright-<version>-<platform>.zip Configuration (checked in order): 1. playwright.cli.dir system property — points to pre-installed driver dir 2. SPEL_DRIVER_DIR env var — overrides cache location 3. Default: ~/.cache/spel/<version>/<platform>/
Frame and FrameLocator operations.
Frame and FrameLocator operations.
Generates API reference markdown from source code introspection.
Three categories of API docs:
spel eval-sci modespel binaryUsage: clojure -T:build gen-docs make gen-docs
Generates API reference markdown from source code introspection. Three categories of API docs: 1. Library API — public vars from all spel namespaces 2. SCI eval API — functions available in `spel eval-sci` mode 3. CLI commands — commands available via the `spel` binary Usage: clojure -T:build gen-docs make gen-docs
High-level agent helpers — opinionated one-shot commands that replace multi-step agent workflows with deterministic, correct-by-default operations.
These functions compose lower-level primitives (screenshot, scroll, snapshot, annotate, evaluate) into the operations AI agents need most often but get wrong when composing manually.
All functions take an explicit Page argument (library layer). SCI wrappers in sci_env.clj provide implicit-page versions.
High-level agent helpers — opinionated one-shot commands that replace multi-step agent workflows with deterministic, correct-by-default operations. These functions compose lower-level primitives (screenshot, scroll, snapshot, annotate, evaluate) into the operations AI agents need most often but get wrong when composing manually. All functions take an explicit Page argument (library layer). SCI wrappers in sci_env.clj provide implicit-page versions.
CLI command to scaffold agent definitions for E2E testing.
Supports multiple agent loop targets via --loop:
Supports test framework flavours via --flavour:
Also generates:
Usage: spel init-agents --ns my-app spel init-agents --ns my-app --loop=claude spel init-agents --ns my-app --flavour=clojure-test spel init-agents --ns my-app --no-tests spel init-agents --ns my-app --test-dir test-e2e spel init-agents --dry-run
CLI command to scaffold agent definitions for E2E testing. Supports multiple agent loop targets via --loop: - opencode (default) — .opencode/agents/, .opencode/prompts/, .opencode/skills/ - claude — .claude/agents/, .claude/prompts/, .claude/docs/ Supports test framework flavours via --flavour: - lazytest (default) — defdescribe/it/expect from spel.allure, :context fixtures - clojure-test — deftest/testing/is from clojure.test, use-fixtures Also generates: - test-e2e/<ns>/e2e/ — seed test (path derived from --ns) Usage: spel init-agents --ns my-app spel init-agents --ns my-app --loop=claude spel init-agents --ns my-app --flavour=clojure-test spel init-agents --ns my-app --no-tests spel init-agents --ns my-app --test-dir test-e2e spel init-agents --dry-run
Keyboard, Mouse, and Touchscreen operations.
Keyboard, Mouse, and Touchscreen operations.
JUnit XML reporter for Lazytest.
Produces JUnit XML output fully compliant with the Apache Ant JUnit schema (https://github.com/windyroad/JUnit-Schema) and compatible with CI systems: GitHub Actions, Jenkins, GitLab CI.
Usage: clojure -M:test --output com.blockether.spel.junit-reporter/junit clojure -M:test --output nested --output com.blockether.spel.junit-reporter/junit
Output file defaults to test-results/junit.xml. Override with: -Dlazytest.junit.output=path/to/output.xml LAZYTEST_JUNIT_OUTPUT=path/to/output.xml
JUnit XML reporter for Lazytest. Produces JUnit XML output fully compliant with the Apache Ant JUnit schema (https://github.com/windyroad/JUnit-Schema) and compatible with CI systems: GitHub Actions, Jenkins, GitLab CI. Usage: clojure -M:test --output com.blockether.spel.junit-reporter/junit clojure -M:test --output nested --output com.blockether.spel.junit-reporter/junit Output file defaults to test-results/junit.xml. Override with: -Dlazytest.junit.output=path/to/output.xml LAZYTEST_JUNIT_OUTPUT=path/to/output.xml
Minimal read-only LevelDB parser for Chrome's localStorage.
Reads .log (WAL) and .ldb (SSTable) files from a LevelDB directory, returning raw key-value records with sequence numbers and state.
Implements:
Reference: https://github.com/google/leveldb/blob/master/doc/ Based on: ccl_chromium_reader by CCL Forensics (MIT license)
Minimal read-only LevelDB parser for Chrome's localStorage. Reads .log (WAL) and .ldb (SSTable) files from a LevelDB directory, returning raw key-value records with sequence numbers and state. Implements: - Log file parsing (32KB blocks with CRC32/length/type headers) - SSTable parsing (footer, index, data blocks with prefix compression) - Snappy decompression (pure Java, no native dependencies) - Varint encoding (protobuf-style little-endian varints) Reference: https://github.com/google/leveldb/blob/master/doc/ Based on: ccl_chromium_reader by CCL Forensics (MIT license)
Locator and ElementHandle operations.
Locators are the primary way to find and interact with elements. They auto-wait and auto-retry, making them the preferred API.
Locator and ElementHandle operations. Locators are the primary way to find and interact with elements. They auto-wait and auto-retry, making them the preferred API.
Parse and generate GitHub-flavored markdown tables.
Converts between markdown table strings and Clojure data:
from-markdown-table : markdown string → vector of mapsto-markdown-table : vector of maps → markdown stringUseful in eval-sci scripts for processing tabular data from web pages, API responses, or LLM output.
Parse and generate GitHub-flavored markdown tables. Converts between markdown table strings and Clojure data: - `from-markdown-table` : markdown string → vector of maps - `to-markdown-table` : vector of maps → markdown string Useful in eval-sci scripts for processing tabular data from web pages, API responses, or LLM output.
Convert HTML documents into readable Markdown using the browser runtime.
Convert HTML documents into readable Markdown using the browser runtime.
Native-image entry point for spel.
Provides a CLI tool (spel) for browser automation with persistent browser sessions.
Modes:
Usage: spel open https://example.org # Navigate (auto-starts browser) spel snapshot # ARIA snapshot with refs spel click @ref # Click by ref spel fill @ref "search text" # Fill input by ref spel screenshot shot.png # Take screenshot spel close # Close browser spel eval-sci '(+ 1 2)' # Evaluate and exit spel install # Install Playwright browsers spel --help # Show help
Native-image entry point for spel. Provides a CLI tool (spel) for browser automation with persistent browser sessions. Modes: - CLI commands (default): Send commands to the browser process - Eval: Evaluate a Clojure expression and exit Usage: spel open https://example.org # Navigate (auto-starts browser) spel snapshot # ARIA snapshot with refs spel click @ref # Click by ref spel fill @ref "search text" # Fill input by ref spel screenshot shot.png # Take screenshot spel close # Close browser spel eval-sci '(+ 1 2)' # Evaluate and exit spel install # Install Playwright browsers spel --help # Show help
Request, Response, Route, WebSocket operations.
All response and request accessor functions participate in the anomaly railway pattern: if passed an anomaly map (e.g. from a failed navigate), they pass it through unchanged instead of throwing ClassCastException.
Request, Response, Route, WebSocket operations. All response and request accessor functions participate in the anomaly railway pattern: if passed an anomaly map (e.g. from a failed navigate), they pass it through unchanged instead of throwing ClassCastException.
Option map to Playwright Java options object conversion.
Converts idiomatic Clojure maps to Playwright's typed option objects. All functions use reflection-free type hints.
Option map to Playwright Java options object conversion. Converts idiomatic Clojure maps to Playwright's typed option objects. All functions use reflection-free type hints.
Page operations - navigation, content, evaluation, screenshots, events, and utility classes (Dialog, Download, ConsoleMessage, Clock, FileChooser, Worker, WebError). The Page class is the central API surface of Playwright. Wraps all Page methods with idiomatic Clojure functions.
Page operations - navigation, content, evaluation, screenshots, events, and utility classes (Dialog, Download, ConsoleMessage, Clock, FileChooser, Worker, WebError). The Page class is the central API surface of Playwright. Wraps all Page methods with idiomatic Clojure functions.
Platform detection and CDP discovery utilities. Pure functions with no dependency on daemon state, browser sessions, or SCI context.
Extracted from daemon.clj to break the circular dependency between sci_env.clj (which needs WSL + CDP functions for the spel namespace) and daemon.clj (which requires sci_env.clj for evaluation).
Both daemon.clj and sci_env.clj can safely require this namespace.
Platform detection and CDP discovery utilities. Pure functions with no dependency on daemon state, browser sessions, or SCI context. Extracted from daemon.clj to break the circular dependency between sci_env.clj (which needs WSL + CDP functions for the spel namespace) and daemon.clj (which requires sci_env.clj for evaluation). Both daemon.clj and sci_env.clj can safely require this namespace.
Chrome/Chromium user-profile lookup and cloning for --profile <name>.
Matches the semantics of vercel-labs/agent-browser:
Three-tier resolution of the user-supplied string to an on-disk profile directory (exact match → case-insensitive display name → case-insensitive directory name). Ambiguous display-name matches raise an error.
Selective copy to a fresh temp user-data directory. Large cache/non-auth
subdirectories are skipped (e.g. Cache, Code Cache, GPUCache,
Service Worker) because they are not needed for login-state reuse and
would bloat the copy by hundreds of MB. See exclude-dirs.
The clone function returns BOTH the temp user-data dir path AND the
resolved profile directory name. The caller is responsible for passing
--profile-directory=<dir> to Chrome so it picks the right profile from
the cloned user-data dir (without this, Chrome always defaults to
Default and ignores other profiles in the clone).
Platform-specific Chrome user-data roots: macOS ~/Library/Application Support/Google/Chrome Linux ~/.config/google-chrome Windows %LOCALAPPDATA%/Google/Chrome/User Data
Chrome/Chromium user-profile lookup and cloning for `--profile <name>`. Matches the semantics of vercel-labs/agent-browser: 1. Three-tier resolution of the user-supplied string to an on-disk profile directory (exact match → case-insensitive display name → case-insensitive directory name). Ambiguous display-name matches raise an error. 2. Selective copy to a fresh temp user-data directory. Large cache/non-auth subdirectories are skipped (e.g. `Cache`, `Code Cache`, `GPUCache`, `Service Worker`) because they are not needed for login-state reuse and would bloat the copy by hundreds of MB. See `exclude-dirs`. 3. The clone function returns BOTH the temp user-data dir path AND the resolved profile directory name. The caller is responsible for passing `--profile-directory=<dir>` to Chrome so it picks the right profile from the cloned user-data dir (without this, Chrome always defaults to `Default` and ignores other profiles in the clone). Platform-specific Chrome user-data roots: macOS ~/Library/Application Support/Google/Chrome Linux ~/.config/google-chrome Windows %LOCALAPPDATA%/Google/Chrome/User Data
Clojure vars wrapping Playwright's AriaRole enum values.
Eliminates the need for (:import [com.microsoft.playwright.options AriaRole]).
Usage: (require '[com.blockether.spel.roles :as role]) (page/get-by-role pg role/button) (page/get-by-role pg role/heading {:name "Title"})
Clojure vars wrapping Playwright's AriaRole enum values.
Eliminates the need for `(:import [com.microsoft.playwright.options AriaRole])`.
Usage:
(require '[com.blockether.spel.roles :as role])
(page/get-by-role pg role/button)
(page/get-by-role pg role/heading {:name "Title"})SCI (Small Clojure Interpreter) environment for native-image REPL.
Registers all spel functions as SCI namespaces so they can be evaluated in a native-image compiled REPL without JVM startup.
The SCI context wraps a stateful Playwright session with managed atoms for the Playwright, Browser, BrowserContext, and Page instances.
Namespaces available in eval-sci mode: spel/ - Simplified API (implicit page/context from atoms) snapshot/ - Accessibility snapshot capture annotate/ - Screenshot annotation input/ - Keyboard, Mouse, Touchscreen (raw pass-throughs) frame/ - Frame and FrameLocator operations (raw pass-throughs) net/ - Network request/response/route (raw pass-throughs) loc/ - Locator operations (raw pass-throughs, explicit Locator arg) assert/ - Assertion functions (raw pass-throughs, explicit assertion obj) core/ - Lifecycle stubs + utility pass-throughs
Usage: (def ctx (create-sci-ctx)) (eval-string ctx "(spel/start!)") (eval-string ctx "(spel/navigate "https://example.org")") (eval-string ctx "(spel/capture-snapshot)") (eval-string ctx "(spel/stop!)")
SCI (Small Clojure Interpreter) environment for native-image REPL. Registers all spel functions as SCI namespaces so they can be evaluated in a native-image compiled REPL without JVM startup. The SCI context wraps a stateful Playwright session with managed atoms for the Playwright, Browser, BrowserContext, and Page instances. Namespaces available in eval-sci mode: spel/ - Simplified API (implicit page/context from atoms) snapshot/ - Accessibility snapshot capture annotate/ - Screenshot annotation input/ - Keyboard, Mouse, Touchscreen (raw pass-throughs) frame/ - Frame and FrameLocator operations (raw pass-throughs) net/ - Network request/response/route (raw pass-throughs) loc/ - Locator operations (raw pass-throughs, explicit Locator arg) assert/ - Assertion functions (raw pass-throughs, explicit assertion obj) core/ - Lifecycle stubs + utility pass-throughs Usage: (def ctx (create-sci-ctx)) (eval-string ctx "(spel/start!)") (eval-string ctx "(spel/navigate \"https://example.org\")") (eval-string ctx "(spel/capture-snapshot)") (eval-string ctx "(spel/stop!)")
Google Search automation using Playwright.
Provides functions for searching Google, extracting results, handling pagination, and supporting web, image, and news search types — all without any API key. Uses Playwright browser automation to navigate Google Search and extract structured data from the results pages.
Library functions take a Page as the first argument. The -main entry point provides a standalone CLI tool.
Usage (library): (search! page "clojure programming") (search! page "cats" {:type :images}) (next-page! page) (search-and-collect! page "clojure" {:max-pages 3})
Usage (CLI): spel search "clojure programming" spel search "cats" --images spel search "news topic" --news --json spel search "query" --page 2 --num 20
Google Search automation using Playwright.
Provides functions for searching Google, extracting results, handling
pagination, and supporting web, image, and news search types — all
without any API key. Uses Playwright browser automation to navigate
Google Search and extract structured data from the results pages.
Library functions take a Page as the first argument.
The -main entry point provides a standalone CLI tool.
Usage (library):
(search! page "clojure programming")
(search! page "cats" {:type :images})
(next-page! page)
(search-and-collect! page "clojure" {:max-pages 3})
Usage (CLI):
spel search "clojure programming"
spel search "cats" --images
spel search "news topic" --news --json
spel search "query" --page 2 --num 20Security helpers for agent-safe browser deployments.
Three independent, opt-in features:
Domain allowlist — restrict navigation and sub-resource fetches to
a list of trusted domains. Used by the daemon at context creation time.
*.example.com wildcards match example.com AND any subdomain.
Content boundaries — wrap tool output in XML-style delimiters so downstream LLMs can distinguish trusted tool output from untrusted page content. Mitigates prompt-injection from malicious page text.
Max output truncation — cap tool output at N characters to protect the agent's context window from runaway pages (e.g., a 2MB snapshot).
All functions are pure and side-effect free. Used by cli (print-result
wrapping + truncation) and daemon (route handler for allowed-domains).
Security helpers for agent-safe browser deployments. Three independent, opt-in features: 1. **Domain allowlist** — restrict navigation and sub-resource fetches to a list of trusted domains. Used by the daemon at context creation time. `*.example.com` wildcards match `example.com` AND any subdomain. 2. **Content boundaries** — wrap tool output in XML-style delimiters so downstream LLMs can distinguish trusted tool output from untrusted page content. Mitigates prompt-injection from malicious page text. 3. **Max output truncation** — cap tool output at N characters to protect the agent's context window from runaway pages (e.g., a 2MB snapshot). All functions are pure and side-effect free. Used by `cli` (print-result wrapping + truncation) and `daemon` (route handler for allowed-domains).
Accessibility snapshot with content-hash stable refs.
Walks the DOM tree via JavaScript injection and builds a YAML-like
accessibility tree with content-hash refs (deterministic across page states).
Elements are tagged with data-pw-ref attributes for later interaction.
Usage: (def snap (capture-snapshot page)) (:tree snap) ;; YAML-like string with [@eXXXXX] annotations (:refs snap) ;; {ref-id {:role :name :tag :bbox} ...} (resolve-ref page ref-id) ;; returns Locator for the element
Accessibility snapshot with content-hash stable refs.
Walks the DOM tree via JavaScript injection and builds a YAML-like
accessibility tree with content-hash refs (deterministic across page states).
Elements are tagged with `data-pw-ref` attributes for later interaction.
Usage:
(def snap (capture-snapshot page))
(:tree snap) ;; YAML-like string with [@eXXXXX] annotations
(:refs snap) ;; {ref-id {:role :name :tag :bbox} ...}
(resolve-ref page ref-id) ;; returns Locator for the elementBlockether Allure report renderer. Reads allure-results/ JSON files and generates a standalone HTML report with a clean neutral palette, search, sorting, compact investigation-first layout, and shared attachment UX reused from the standard Allure report helpers.
Blockether Allure report renderer. Reads allure-results/ JSON files and generates a standalone HTML report with a clean neutral palette, search, sorting, compact investigation-first layout, and shared attachment UX reused from the standard Allure report helpers.
Custom SSL/TLS certificate support for corporate proxy environments.
When spel install downloads the Playwright driver from cdn.playwright.dev,
GraalVM native-image uses a TrustStore baked at build time. Corporate
SSL-inspecting proxies use internal CAs not in that store, causing
'PKIX path building failed'.
This namespace provides a composite TrustManager that merges the built-in default CAs with user-provided certificates (PEM or JKS/PKCS12), so both public CDN certs and corporate certs are trusted.
Environment variables (checked in order): SPEL_CA_BUNDLE — PEM file with extra CA certs (merged with defaults) NODE_EXTRA_CA_CERTS — same as above, also respected by Node.js subprocess SPEL_TRUSTSTORE — JKS/PKCS12 truststore path (merged with defaults) SPEL_TRUSTSTORE_TYPE — truststore type (default: JKS) SPEL_TRUSTSTORE_PASSWORD — truststore password (default: empty)
Custom SSL/TLS certificate support for corporate proxy environments. When `spel install` downloads the Playwright driver from cdn.playwright.dev, GraalVM native-image uses a TrustStore baked at build time. Corporate SSL-inspecting proxies use internal CAs not in that store, causing 'PKIX path building failed'. This namespace provides a composite TrustManager that merges the built-in default CAs with user-provided certificates (PEM or JKS/PKCS12), so both public CDN certs and corporate certs are trusted. Environment variables (checked in order): SPEL_CA_BUNDLE — PEM file with extra CA certs (merged with defaults) NODE_EXTRA_CA_CERTS — same as above, also respected by Node.js subprocess SPEL_TRUSTSTORE — JKS/PKCS12 truststore path (merged with defaults) SPEL_TRUSTSTORE_TYPE — truststore type (default: JKS) SPEL_TRUSTSTORE_PASSWORD — truststore password (default: empty)
Stealth mode for browser automation anti-detection.
Provides Chrome launch arguments, default-arg suppressions, and JavaScript init scripts that hide Playwright/automation signals from bot-detection systems. Based on puppeteer-extra-plugin-stealth evasions.
Usage: (stealth-args) ;; Chrome launch args (stealth-ignore-default-args) ;; args to suppress (stealth-init-script) ;; JS evasion script for addInitScript
Stealth mode for browser automation anti-detection. Provides Chrome launch arguments, default-arg suppressions, and JavaScript init scripts that hide Playwright/automation signals from bot-detection systems. Based on puppeteer-extra-plugin-stealth evasions. Usage: (stealth-args) ;; Chrome launch args (stealth-ignore-default-args) ;; args to suppress (stealth-init-script) ;; JS evasion script for addInitScript
Vertical image stitching via Playwright browser rendering. Combines multiple screenshot PNGs into one by rendering them as HTML and taking a full-page screenshot — no AWT/ImageIO dependency.
Vertical image stitching via Playwright browser rendering. Combines multiple screenshot PNGs into one by rendering them as HTML and taking a full-page screenshot — no AWT/ImageIO dependency.
Encrypted credential store for spel auth save/login/list/delete.
Goal: the LLM never sees the password. Credentials are encrypted at rest with AES-256-GCM. The decryption key comes from:
SPEL_ENCRYPTION_KEY (64-char hex = 32 bytes), or~/.spel/.encryption-key (chmod 600 on POSIX).Records live in ~/.spel/vault/<name>.json.enc as:
[12-byte IV][ciphertext][16-byte GCM tag]
The JSON payload (before encryption) has :name :url :username :password.
Threat model: this protects credentials from casual disk inspection and from the LLM driving the CLI — NOT from a local attacker with filesystem read. Use OS-level keyring for stronger guarantees.
Encrypted credential store for `spel auth save/login/list/delete`. Goal: the LLM never sees the password. Credentials are encrypted at rest with AES-256-GCM. The decryption key comes from: 1. Env `SPEL_ENCRYPTION_KEY` (64-char hex = 32 bytes), or 2. Auto-generated file at `~/.spel/.encryption-key` (chmod 600 on POSIX). Records live in `~/.spel/vault/<name>.json.enc` as: [12-byte IV][ciphertext][16-byte GCM tag] The JSON payload (before encryption) has `:name :url :username :password`. Threat model: this protects credentials from casual disk inspection and from the LLM driving the CLI — NOT from a local attacker with filesystem read. Use OS-level keyring for stronger guarantees.
Pixel-level screenshot comparison using the pixelmatch algorithm. Runs inside Playwright's Canvas API — no AWT/ImageIO dependency, GraalVM native-image safe.
Based on pixelmatch by Mapbox (ISC license): https://github.com/mapbox/pixelmatch
Pixel-level screenshot comparison using the pixelmatch algorithm. Runs inside Playwright's Canvas API — no AWT/ImageIO dependency, GraalVM native-image safe. Based on pixelmatch by Mapbox (ISC license): https://github.com/mapbox/pixelmatch
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |