sleight
Drive any CDP browser like a human. Bezier trajectories with real hand tremor, typing rhythm modelled on keystroke-dynamics research, and exclusive leasing for browser instance pools.
Python ≥ 3.11 · one runtime dependency (websocket-client) · MIT
📖 中文文档手册 — 安装、快速开始、实战场景、CloakBrowser Manager 部署
pip install sleight
30 seconds
from sleight import connect, Text
with connect("http://127.0.0.1:9222") as s: # opens its own tab, closes it on exit
s.open("https://example.com", wait=Text("Example Domain"))
print(s.title(), len(s.content()))
With a browser pool that has real profiles behind it:
from sleight.providers import CloakBrowserManager
mgr = CloakBrowserManager("http://127.0.0.1:19000", token="…")
with mgr.lease() as inst: # exclusive lease, released on exit
with inst.session(human=True) as s: # every action gets a human trajectory
s.open("https://example.com")
s.click("#login")
s.type("#email", "user@example.com")
s.click("#submit", human=False) # …except this one, speed matters here
See what the page actually loaded — the library gives you structured data, you decide what to print:
with s.capture_resources(types={"Script", "Stylesheet"}) as capture:
s.open(url, wait=Load())
s.pump_events(10) # the async batch that arrives after `load`
for r in capture.snapshot():
print(r.resource_type, r.status, r.url)
Target one specific profile — by id, by name, or by tag:
with mgr.lease(instance_id="5edcc28a-…") as inst: ...
with mgr.lease(where=lambda i: i.name == "Win-US-02") as inst: ...
with mgr.lease(where=lambda i: "us" in i.tags) as inst: ...
Three providers' worth of instances, one logical pool:
from sleight import Pool
from sleight.providers import CloakBrowserManager, Plain
pool = Pool([
CloakBrowserManager("http://10.0.0.1:9000", token=T1, name="hk"),
CloakBrowserManager("http://10.0.0.2:9000", token=T2, name="sg"),
Plain("http://127.0.0.1:9222", name="local"),
])
with pool.lease(where=lambda i: "us" in i.tags) as inst:
...
Why this exists
Fingerprint-level anti-detection is a solved problem — CloakBrowser patches Chromium at the source level, Camoufox patches Firefox. They fix what the browser looks like. Nothing fixes how it moves.
- Playwright and Puppeteer teleport the mouse.
mouse.move(steps=N)interpolates a straight line at constant speed — zero jitter, zero acceleration. That is itself a signature. - The browser will not fill in the trajectory for you. Even with a humanize feature
enabled browser-side, an external CDP client produces zero
mousemoveevents between press and release. Measured, not assumed. - The good trajectory work lives in JavaScript (
ghost-cursor). Python ports are thinly maintained. - Crawlee for Python's
BrowserPooldoes not support remote browsers.
sleight fills exactly that gap: Python + remote CDP + human behaviour + instance leasing.
Relationship to Playwright
Not a replacement — a complement. sleight is a driver layer, not a framework. It deliberately does not do iframes/OOPIF, downloads, video, tracing, or a full locator DSL. When you need those, use Playwright.
The interesting part is that you can use both: sleight's human module is
sans-io — it emits (method, params, sleep_after)
tuples and never touches a socket — so it drives a Playwright CDPSession just as
happily as sleight's own transport.
What makes the motion credible
| sleight | typical automation | |
|---|---|---|
| Path shape | cubic Bezier, control points offset to one side | straight line |
| Micro-motion | WindMouse wind term (correlated tremor) | none, or white noise |
| Point count | Fitts's law — far small targets take longer | fixed steps=N |
| Landing | truncated Gaussian inside the box | dead centre |
| Coordinates | integers | floats used as "jitter" |
| Overshoot | past the target then back, distance-scaled | exact arrival |
| Typing | per-character events, interval by digraph class | one insertText |
| Scrolling | repeated small mouseWheel deltas |
one scrollTo |
Parameters are not invented. They come from the WindMouse physical model, ghost-cursor's Fitts-law point budgeting, and published keystroke-dynamics measurements (alternating-hand digraphs average 114 ms, same-hand-different-finger 131 ms, same-finger slowest and most variable).
Scope
Does: navigation and typed wait conditions · rendered-DOM reads · CSS queries · human mouse / keyboard / wheel · structured network-resource capture · instance discovery across providers · cooperative exclusive leasing with TTL renewal · idempotent recovery.
Does not: data extraction · scheduling and queues · fingerprint spoofing (that is the browser's job) · iframe / OOPIF / Shadow DOM piercing · strict fencing · WebDriver BiDi · Firefox.
Status
0.x — alpha, the API will move. Every release documents its breaking changes.
Releases are published from a git tag by
.github/workflows/publish.yml via PyPI Trusted
Publishing — no token is stored in this repository.
License
MIT
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 sleight-0.1.3.tar.gz.
File metadata
- Download URL: sleight-0.1.3.tar.gz
- Upload date:
- Size: 159.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
695682ec757ece2b51abcc9ba29ec36a1ae6633cfb5d0da13c8532c394736c5a
|
|
| MD5 |
09a54b1c4f63e9a0da67835ca1fbf35d
|
|
| BLAKE2b-256 |
1df43489ef7339bd8ba277d3e5e9417e1658d55405349f6ab23688e2b224e51d
|
Provenance
The following attestation bundles were made for sleight-0.1.3.tar.gz:
Publisher:
publish.yml on yuanqimanong/sleight
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sleight-0.1.3.tar.gz -
Subject digest:
695682ec757ece2b51abcc9ba29ec36a1ae6633cfb5d0da13c8532c394736c5a - Sigstore transparency entry: 2280099018
- Sigstore integration time:
-
Permalink:
yuanqimanong/sleight@7f09e9791c051fe53c2e23ce906f88d820c4977f -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/yuanqimanong
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7f09e9791c051fe53c2e23ce906f88d820c4977f -
Trigger Event:
push
-
Statement type:
File details
Details for the file sleight-0.1.3-py3-none-any.whl.
File metadata
- Download URL: sleight-0.1.3-py3-none-any.whl
- Upload date:
- Size: 101.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0ff0f88dc065142b790fbec79d00bd032d151908f7a87a50eb68f1448781f1f
|
|
| MD5 |
0dffd8ca3167d2d6602ae274e13f25e0
|
|
| BLAKE2b-256 |
cd0ff49d698c7f504535ec45fcbb395bd1997d9fbdb0cd1018194b016714ed38
|
Provenance
The following attestation bundles were made for sleight-0.1.3-py3-none-any.whl:
Publisher:
publish.yml on yuanqimanong/sleight
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sleight-0.1.3-py3-none-any.whl -
Subject digest:
f0ff0f88dc065142b790fbec79d00bd032d151908f7a87a50eb68f1448781f1f - Sigstore transparency entry: 2280099041
- Sigstore integration time:
-
Permalink:
yuanqimanong/sleight@7f09e9791c051fe53c2e23ce906f88d820c4977f -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/yuanqimanong
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7f09e9791c051fe53c2e23ce906f88d820c4977f -
Trigger Event:
push
-
Statement type: