sema-cdp

0.1.0

Chrome DevTools Protocol client: launch, connect, send commands, wait for events

$ sema pkg install sema-cdp
Readme Versions 1 Dependencies 0

sema-cdp

Chrome DevTools Protocol client: launch, connect, send commands, wait for events.

This is the raw protocol layer. It knows nothing about selectors or clicking — for a high-level API (auto-waiting actions, screenshots, accessibility snapshots) use sema-browser, which is built on this package. Use sema-cdp directly when you need protocol domains the high-level layer doesn't wrap: network interception, tracing, emulation, coverage, or any of the CDP domains.

Works against anything that speaks CDP: Chrome, Chromium, Edge, Brave, chrome-headless-shell, or an already-running browser started with --remote-debugging-port.

Install

sema pkg add sema-cdp

Quick start

(import "sema-cdp")

(define b (cdp/launch))                          ; headless Chrome
(define sess (cdp/attach! b (cdp/new-target! b "about:blank")))

(cdp/send sess "Page.enable")
(cdp/send sess "Page.navigate" {:url "https://sema-lang.com"})
(cdp/wait-event sess "Page.loadEventFired")

(define r (cdp/send sess "Runtime.evaluate"
                    {:expression "document.title" :returnByValue #t}))
(println (:value (:result r)))                   ; => Sema

(cdp/close! b)

API

Function Description
cdp/find-chrome Locate a Chrome-compatible binary, or nil
cdp/browser-candidates The paths find-chrome probes, in priority order
cdp/launch Launch a browser with remote debugging, return a connection
cdp/connect Attach to an already-running browser
cdp/close! Shut the browser down and release its process
cdp/send Send a command, return its result map
cdp/targets List browser targets
cdp/new-target! Create a page target, return its id
cdp/attach! Attach to a target, return a session
cdp/detach! Detach a session
cdp/on! Register an event handler (returns an id)
cdp/off! Remove a handler by id
cdp/pump Receive events while otherwise idle
cdp/wait-event Wait for (and consume) an event
cdp/events Buffered events
cdp/clear-events! Drop buffered events
cdp/version Browser version info

(cdp/find-chrome) / (cdp/browser-candidates)

cdp/find-chrome returns the path of a Chrome-compatible browser binary, or nil if none is found. It checks the SEMA_CHROME env var first, then well-known install locations on macOS, Linux, and Windows — Chrome, Chromium, Brave, and Edge, in that order. On Windows the per-machine and per-user roots come from PROGRAMFILES, PROGRAMFILES(X86), and LOCALAPPDATA. cdp/browser-candidates returns the probed paths for this machine, in priority order (most will not exist) — useful for diagnosing why no browser was found.

(cdp/launch) / (cdp/launch opts)

Launch a browser with remote debugging and return a connection. Options:

  • :headless — default #t
  • :chrome — binary path (default: cdp/find-chrome)
  • :args — list of extra CLI arguments
  • :user-data-dir — profile dir (default: a fresh dir under the system temp dir)

Errors if no browser binary can be found, naming the ways to provide one.

(cdp/connect url)

Connect to an already-running browser. Accepts a ws:// debugger URL, or an http:// devtools address (e.g. "http://localhost:9222") whose /json/version endpoint is used to discover it.

(define b (cdp/connect "http://localhost:9222"))

(cdp/close! conn)

Ask the browser to shut down (Browser.close), close the websocket, and kill the spawned process when this connection launched it.

(cdp/send target method) / (cdp/send target method params) / (cdp/send target method params opts)

Send a CDP command and return its result map (keyword keys). target is a connection or a session from cdp/attach! — a session routes the command to its target. opts supports :timeout in ms (default 30000). A CDP error response raises, including the method, code, and message.

(cdp/send sess "Emulation.setDeviceMetricsOverride"
          {:width 390 :height 844 :deviceScaleFactor 3 :mobile #t})

(cdp/targets conn)

List the browser's targets (pages, workers, extensions) as maps with :targetId, :type, :url, :title.

(cdp/new-target! conn url)

Create a new page target navigated to url and return its target id.

(cdp/attach! conn target-id)

Attach to a target (flat protocol) and return a session map. Pass the session to cdp/send / cdp/wait-event to address that target.

(cdp/detach! session)

Detach a session created by cdp/attach!.

(cdp/on! target method handler) / (cdp/off! target handler-id)

cdp/on! registers a handler called with each matching event map as it arrives, and returns a handler id; cdp/off! removes it. Scoped to the session when target is one, otherwise connection-wide. Events are only received while a cdp/send, cdp/wait-event, or cdp/pump is pumping the connection — there is no background thread. Handlers may call cdp/send themselves (responses are routed by id, so reentrant sends are safe).

(cdp/pump target duration-ms)

Receive and dispatch events for up to duration-ms without sending anything — use it to let cdp/on! handlers run while otherwise idle.

(cdp/version conn)

Return browser version info: :product, :revision, :userAgent, :jsVersion, :protocolVersion.

(cdp/wait-event target method) / (cdp/wait-event target method opts)

Wait for an event, consume it from the buffer, and return the event map. Already-buffered events match first, so an event that fired during a previous command is not missed. Options: :timeout ms (default 30000), :pred — a predicate on the event map.

(cdp/wait-event sess "Network.responseReceived"
                {:pred (fn (ev) (string/contains?
                                  (get-in ev [:params :response :url]) "/api/"))})

(cdp/events target) / (cdp/clear-events! target) / (cdp/clear-events! target method)

Inspect or drop the buffered events (oldest first, capped at 500). Scoped to the session when target is one.

Testing

sema tests.sema

The live tests launch a headless browser and are skipped when none is installed (cdp/find-chrome returns nil).

License

MIT

VersionSizePublished
0.1.0 9 KB 2026-08-25 14:26:50
No dependencies