Drive and observe a live browser session from Python, with bidirectional async events.
Project description
loominum
A Python library for driving and observing a live browser session from Python code, with bidirectional async event flow. Python pushes JavaScript into the page; the page emits events back.
Two transports
-
JS-injection mode (working today) — a one-time paste-bootstrap opens a WebSocket from the page back to the Python server. Site-agnostic, no Chrome flags, but bootstrap is manual and injection dies on navigation unless re-pasted.
-
CDP sidecar mode (working today, via the
lum-cdpconsole script) — a Python sidecar speaks Chrome DevTools Protocol to a CDP-enabled browser and impersonates a browser to the Loominum server. Gives nav-surviving injection (Page.addScriptToEvaluateOnNewDocument) and CAPTCHA-safe trusted-event dispatch (Input.dispatchKeyEvent/dispatchMouseEvent). Requires the browser launched with--remote-debugging-port.
Same lum.exec / on / add_init / navigate API across both transports.
Use cases
- Authenticated API automation (call APIs in browser context with session cookies)
- Scrape orchestration with DOM-event triggers
- Download monitoring (watch DOM for ready-links, hand off filenames to Python)
- Form automation
- Interactive Python-REPL → browser debugging
Status
src/loominum/— JS-injection implementation. Site-agnostic API surface; ships with localhost defaults indata/loominum/config.json(port 7773, TLS off).src/loominum/README.md— the API documentation (server/client/browser).src/loominum/EVENTS.md— event-system documentation.src/loominum/NGINX.md— deployment notes for fronting the server with nginx (TLS termination, path-based routing).- CDP sidecar — implemented in
src/loominum/cdp.py; run via thelum-cdpconsole script. Bridge logic covered by an automated fake-CDP test harness intests/test_cdp.py.
Layout
loominum/
├── README.md this file
├── HANDOFF.md design doc for the next agent picking this up
├── src/loominum/ library source (JS-injection base + CDP sidecar)
│ ├── __init__.py
│ ├── server.py WS server — handles both the /remote browser and /client python endpoints
│ ├── client.py Python client
│ ├── config.py config schema + discovery (discover_config, LumConf.auto)
│ ├── cdp.py CDP sidecar transport (lum-cdp)
│ ├── htdocs/ served static files: remote.js (the bootstrap),
│ │ evtcap.js (console tap helper), install-cert scripts
│ ├── scripts/ cert install helpers
│ ├── README.md API reference
│ ├── EVENTS.md event system
│ └── NGINX.md nginx deployment notes
├── data/loominum/
│ └── config.json template — localhost defaults, no TLS
└── tests/
├── test_cdp.py CDP bridge tests (fake-CDP harness)
├── test_config.py config discovery + env-override tests
└── test_lum.py manual live-server smoke script
Installation
pip install loominum
Installs the lum (server) and lum-cdp (CDP sidecar) console scripts.
Configuration
Loominum runs with localhost defaults and no config file required. To
customize, drop a config.json in any conventional location or override
single settings via environment variables — full precedence rules in
src/loominum/README.md.
{
"verbose": false,
"log_file": "log/lum.log",
"server_url": "http://127.0.0.1:7773",
"client_url": "http://127.0.0.1:7773",
"cert_sans": null
}
Quick paths:
- A file:
./loominum.json,~/.config/loominum/config.json, or$LOOMINUM_CONFIG=/full/path.json. (PRJ_DIRis still honored for back-compat but no longer required.) - A single setting:
LOOMINUM_SERVER_URL,LOOMINUM_CLIENT_URL,LOOMINUM_LOG_FILE,LOOMINUM_CERT_SANS,LOOMINUM_VERBOSE.
For TLS: set cert_sans to a comma-separated list of hostnames/IPs to
include in the cert SANs (e.g. "localhost,192.168.1.100") and use an
https:// scheme on server_url. Cert install helpers under
src/loominum/scripts/.
Quick start (current JS-injection mode)
lum # or, from a source checkout: PYTHONPATH=src python -m loominum.server
Then in the browser DevTools console of the target page:
fetch('http://127.0.0.1:7773/remote.js?t='+Date.now()).then(r=>r.text()).then(eval);
Then from Python:
import asyncio
from loominum import LumClient
async def main():
async with LumClient() as client:
title = await client.exec('document.title')
print(title)
asyncio.run(main())
See src/loominum/README.md for the full API.
Quick start (CDP sidecar mode)
Launch a Chromium-based browser with remote debugging enabled:
chromium --remote-debugging-port=9222
Then start the server and the sidecar (it discovers the target tab, auto-injects the page bridge, and impersonates a browser to the server):
lum & # start the server (installed console script)
lum-cdp --target-url example.com
From Python, the API is identical to JS-injection mode:
import asyncio
from loominum import LumClient
async def main():
async with LumClient() as client:
print(await client.exec('document.title'))
asyncio.run(main())
The CDP transport survives page navigations (init code re-runs on every new
document) and can dispatch trusted input via CDPTransport.dispatch_key,
type_text, and click.
Testing
pytest tests/test_cdp.py
Covers the bridge with a fake CDP browser (no real Chrome required). A real-
browser scenario auto-runs when localhost:9222 is reachable and otherwise
skips.
License
Apache-2.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file loominum-0.1.0.tar.gz.
File metadata
- Download URL: loominum-0.1.0.tar.gz
- Upload date:
- Size: 50.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6b5868fbe23abe4ab1655627a0920f7263274ee76835199e1749c11a5c83abc
|
|
| MD5 |
88c32c34d3baf434e5d19ed4b916b7c6
|
|
| BLAKE2b-256 |
5761433357dffa9c3f841008d7ca1328d692e70ab73cf2f78d0b700f8ced3845
|
Provenance
The following attestation bundles were made for loominum-0.1.0.tar.gz:
Publisher:
workflow.yml on apresence/loominum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
loominum-0.1.0.tar.gz -
Subject digest:
d6b5868fbe23abe4ab1655627a0920f7263274ee76835199e1749c11a5c83abc - Sigstore transparency entry: 1856019553
- Sigstore integration time:
-
Permalink:
apresence/loominum@e6fa3dbb3f581d94e72d4fb1ad8f41581b2957a6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/apresence
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@e6fa3dbb3f581d94e72d4fb1ad8f41581b2957a6 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file loominum-0.1.0-py3-none-any.whl.
File metadata
- Download URL: loominum-0.1.0-py3-none-any.whl
- Upload date:
- Size: 56.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5883c391be09d95c47ffc6734830182ef1ec7b5f6a21891e13ba35d1a091a619
|
|
| MD5 |
60af9c9425f7b73de44c00fec4902252
|
|
| BLAKE2b-256 |
e4055f76f20ecd2948397c88814b371a749e78ca1fbee03e27db76bb0d8330fe
|
Provenance
The following attestation bundles were made for loominum-0.1.0-py3-none-any.whl:
Publisher:
workflow.yml on apresence/loominum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
loominum-0.1.0-py3-none-any.whl -
Subject digest:
5883c391be09d95c47ffc6734830182ef1ec7b5f6a21891e13ba35d1a091a619 - Sigstore transparency entry: 1856019583
- Sigstore integration time:
-
Permalink:
apresence/loominum@e6fa3dbb3f581d94e72d4fb1ad8f41581b2957a6 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/apresence
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@e6fa3dbb3f581d94e72d4fb1ad8f41581b2957a6 -
Trigger Event:
workflow_dispatch
-
Statement type: