onionwright 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ harness/last-run.json
@@ -0,0 +1,75 @@
1
+ onionwright Commercial Licence
2
+ Copyright (c) 2026 OpenOnion. All rights reserved.
3
+
4
+ NOT OPEN SOURCE. This software is proprietary and is licensed, not sold.
5
+
6
+ NOTE: This text has not been reviewed by a lawyer. It states the intent agreed for
7
+ the product and is a drafting starting point, not final terms. Review before the
8
+ first paid release.
9
+
10
+ 1. GRANT
11
+
12
+ Subject to an active subscription at the tier designated by OpenOnion, you are
13
+ granted a non-exclusive, non-transferable, revocable licence to install and use
14
+ this software for your own purposes, personal or commercial, on systems you
15
+ control.
16
+
17
+ 2. RESTRICTIONS
18
+
19
+ You may not:
20
+
21
+ a. Redistribute this software, in whole or in part, modified or unmodified.
22
+ b. Resell, sublicense, rent, lease, or repackage it.
23
+ c. Reverse engineer, decompile, or disassemble it, or attempt to derive its
24
+ source, except to the extent applicable law expressly permits despite this
25
+ restriction.
26
+ d. Remove or alter any copyright, licence, or attribution notice.
27
+ e. Share licence credentials, or use one licence across more concurrent
28
+ sessions than your tier permits.
29
+
30
+ 3. OEM / SAAS
31
+
32
+ A separate written agreement is required where this software is bundled,
33
+ embedded, exposed through an API, or otherwise used to provide functionality to
34
+ third-party customers. No subscription tier grants redistribution, resale,
35
+ sublicensing, bundling, embedding, hosted-service, white-label, OEM, or SaaS
36
+ rights unless expressly stated in a separate agreement.
37
+
38
+ 4. VERSIONS AND CONTINUITY
39
+
40
+ The latest major version requires an active subscription. Versions already
41
+ obtained lawfully under an earlier active subscription remain usable
42
+ indefinitely; lapsing only stops access to new releases, it does not disable
43
+ what you already have.
44
+
45
+ 5. ACCEPTABLE USE
46
+
47
+ This software is licensed for lawful automation of systems you own or are
48
+ authorised to access — testing, monitoring, authorised data collection, and
49
+ management of accounts you control under terms you are entitled to accept. It
50
+ is not licensed for unauthorised access, fraud, or circumventing controls you
51
+ have no right to circumvent. OpenOnion may terminate a licence for use in
52
+ breach of this section.
53
+
54
+ 6. NO WARRANTY
55
+
56
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
57
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
58
+ FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. Detection and
59
+ anti-detection are adversarial and change continuously; no representation is
60
+ made that any particular site will accept traffic driven by this software.
61
+
62
+ 7. LIMITATION OF LIABILITY
63
+
64
+ IN NO EVENT SHALL OPENONION BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
65
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM
66
+ OR IN CONNECTION WITH THE SOFTWARE OR ITS USE.
67
+
68
+ 8. THIRD-PARTY COMPONENTS
69
+
70
+ This software depends on Playwright (Apache-2.0, Copyright (c) Microsoft
71
+ Corporation), which it uses through its public API without modification.
72
+ Playwright is not distributed as part of this software and remains governed by
73
+ its own licence.
74
+
75
+ Contact: info@openonion.ai
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: onionwright
3
+ Version: 0.0.1
4
+ Summary: Humanized input for browser automation
5
+ Project-URL: Homepage, https://openonion.ai
6
+ Project-URL: Documentation, https://docs.connectonion.com
7
+ Author-email: OpenOnion <info@openonion.ai>
8
+ License: onionwright Commercial Licence
9
+ Copyright (c) 2026 OpenOnion. All rights reserved.
10
+
11
+ NOT OPEN SOURCE. This software is proprietary and is licensed, not sold.
12
+
13
+ NOTE: This text has not been reviewed by a lawyer. It states the intent agreed for
14
+ the product and is a drafting starting point, not final terms. Review before the
15
+ first paid release.
16
+
17
+ 1. GRANT
18
+
19
+ Subject to an active subscription at the tier designated by OpenOnion, you are
20
+ granted a non-exclusive, non-transferable, revocable licence to install and use
21
+ this software for your own purposes, personal or commercial, on systems you
22
+ control.
23
+
24
+ 2. RESTRICTIONS
25
+
26
+ You may not:
27
+
28
+ a. Redistribute this software, in whole or in part, modified or unmodified.
29
+ b. Resell, sublicense, rent, lease, or repackage it.
30
+ c. Reverse engineer, decompile, or disassemble it, or attempt to derive its
31
+ source, except to the extent applicable law expressly permits despite this
32
+ restriction.
33
+ d. Remove or alter any copyright, licence, or attribution notice.
34
+ e. Share licence credentials, or use one licence across more concurrent
35
+ sessions than your tier permits.
36
+
37
+ 3. OEM / SAAS
38
+
39
+ A separate written agreement is required where this software is bundled,
40
+ embedded, exposed through an API, or otherwise used to provide functionality to
41
+ third-party customers. No subscription tier grants redistribution, resale,
42
+ sublicensing, bundling, embedding, hosted-service, white-label, OEM, or SaaS
43
+ rights unless expressly stated in a separate agreement.
44
+
45
+ 4. VERSIONS AND CONTINUITY
46
+
47
+ The latest major version requires an active subscription. Versions already
48
+ obtained lawfully under an earlier active subscription remain usable
49
+ indefinitely; lapsing only stops access to new releases, it does not disable
50
+ what you already have.
51
+
52
+ 5. ACCEPTABLE USE
53
+
54
+ This software is licensed for lawful automation of systems you own or are
55
+ authorised to access — testing, monitoring, authorised data collection, and
56
+ management of accounts you control under terms you are entitled to accept. It
57
+ is not licensed for unauthorised access, fraud, or circumventing controls you
58
+ have no right to circumvent. OpenOnion may terminate a licence for use in
59
+ breach of this section.
60
+
61
+ 6. NO WARRANTY
62
+
63
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
64
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
65
+ FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. Detection and
66
+ anti-detection are adversarial and change continuously; no representation is
67
+ made that any particular site will accept traffic driven by this software.
68
+
69
+ 7. LIMITATION OF LIABILITY
70
+
71
+ IN NO EVENT SHALL OPENONION BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
72
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM
73
+ OR IN CONNECTION WITH THE SOFTWARE OR ITS USE.
74
+
75
+ 8. THIRD-PARTY COMPONENTS
76
+
77
+ This software depends on Playwright (Apache-2.0, Copyright (c) Microsoft
78
+ Corporation), which it uses through its public API without modification.
79
+ Playwright is not distributed as part of this software and remains governed by
80
+ its own licence.
81
+
82
+ Contact: info@openonion.ai
83
+ License-File: LICENSE
84
+ Keywords: automation,browser,human,input,playwright
85
+ Classifier: Intended Audience :: Developers
86
+ Classifier: License :: Other/Proprietary License
87
+ Classifier: Programming Language :: Python :: 3
88
+ Classifier: Topic :: Software Development :: Testing
89
+ Requires-Python: >=3.9
90
+ Requires-Dist: playwright>=1.55
91
+ Description-Content-Type: text/markdown
92
+
93
+ # onionwright
94
+
95
+ Humanized input for browser automation — a commercial layer over Playwright that
96
+ makes automated interaction behave like a person using the browser.
97
+
98
+ **This is a placeholder release. There is no implementation in this version.**
99
+
100
+ onionwright is proprietary software distributed under a commercial licence, not
101
+ from this index. This entry reserves the name and points at the product.
102
+
103
+ - Product: https://openonion.ai
104
+ - Docs: https://docs.connectonion.com
105
+ - Licensing enquiries: info@openonion.ai
106
+
107
+ Licensed, not sold. Redistribution, resale, and reverse engineering are not
108
+ permitted. See the LICENSE included in the distribution.
@@ -0,0 +1,94 @@
1
+ # onionwright
2
+
3
+ Humanized input for browser automation. A drop-in layer over Playwright's public
4
+ API that makes automated interaction statistically indistinguishable from a person
5
+ using the browser.
6
+
7
+ **Status: scaffold.** No implementation yet — the repo exists so the name, the
8
+ licence, and the release path are settled before code lands. Work is tracked in
9
+ issues.
10
+
11
+ ## Positioning
12
+
13
+ > Patchright makes you look like a real browser.
14
+ > onionwright makes you *act* like a real person.
15
+
16
+ [Patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright) (Apache-2.0, free,
17
+ and what OpenOnion's free tier uses) fixes **static** automation tells: the
18
+ `Runtime.enable` CDP leak, detectable launch flags, the `__playwright__binding__`
19
+ global. It does not touch timing or behaviour at all.
20
+
21
+ Measured on stock Playwright 1.55 driving Chrome 150 (`harness/measure.py`):
22
+
23
+ | signal | stock Playwright | human |
24
+ |---|---|---|
25
+ | keystroke flight (keyup → next keydown) | **2.8 ms** | 200–400 ms |
26
+ | keystroke hold, standard deviation | **0.7 ms** | tens of ms |
27
+ | click hold (mousedown → mouseup) | **0.4 ms** | 60–150 ms |
28
+ | hover → press dwell | **0.45 ms** | hundreds of ms |
29
+ | mouse path | exact linear interpolation — every step `movementX` 11–12, zero acceleration | jerk-limited, with corrective submovements |
30
+
31
+ `page.keyboard.type(delay=80)` sets key **hold** time, not flight time. That is
32
+ why flights come out at 2.8 ms: nothing in Playwright's API controls the interval
33
+ a detector actually measures.
34
+
35
+ These are different layers, not competing products. The free tier keeps Patchright.
36
+
37
+ ## Where this sits
38
+
39
+ ```
40
+ free playwright + stock Chrome
41
+ paid onionwright + onion browser (patched Chromium)
42
+ ```
43
+
44
+ onionwright ships first: it works against stock Chrome, so it needs no build
45
+ infrastructure and is useful before a single Chromium patch exists. The patched
46
+ browser ([openonion/browser](https://github.com/openonion/browser)) covers what no
47
+ driver-level layer can reach — TLS/JA3, HTTP/2 framing, and fingerprint surfaces
48
+ that live below the script layer.
49
+
50
+ ## Design commitments
51
+
52
+ **On top of Playwright, never inside it.** onionwright calls `page.mouse.move()`
53
+ and friends through the public API. It does not patch Playwright's internals, so
54
+ there is no AST surgery, no per-release rebase treadmill, and no inherited test
55
+ failures. Playwright upgrades do not break us. (Patchright takes the other route —
56
+ ~4,900 lines of ts-morph rewriting against a fresh upstream tree — and pays ~70
57
+ failing Playwright tests for it, including Console being entirely dead.)
58
+
59
+ **Distributions, not paths.** Detectors compare moments of empirical distributions,
60
+ not screenshots. DataDome's payload ships sigma/mu and angle medians; Unbotify's
61
+ patent (US 2017/0185758 A1) describes histogram comparison against references. A
62
+ trajectory that looks human and has the wrong variance is not an improvement, so
63
+ `harness/` asserts on distributions.
64
+
65
+ **Bézier is a signature, not a defence.** Cloudflare's Precursor launch names the
66
+ tell directly: bots produce "linear interpolations or mathematically ideal Bézier
67
+ curves". BeCAPTCHA-Mouse (*Pattern Recognition*, 2022) trained a GAN reproducing
68
+ human traits and a plain random forest still detected it. Every open-source
69
+ humanizer is Bézier-plus-easing, which is roughly 2020 state of the art. Our core
70
+ is a biomechanical model instead — minimum-jerk (Flash & Hogan 1985) with
71
+ optimized submovements (Meyer et al. 1988), so overshoot-and-correct emerges from
72
+ the model rather than being bolted on as a random branch.
73
+
74
+ ## harness/
75
+
76
+ The measurement instrument, not the product. Serves an instrumented page over
77
+ `http://127.0.0.1` (a secure context, so `pointerrawupdate` and
78
+ `getCoalescedEvents` behave as they do in production — on `about:blank` they
79
+ silently disappear, which is the likely origin of several widely repeated false
80
+ claims about CDP).
81
+
82
+ ```
83
+ python3 harness/measure.py # system Chrome via channel="chrome"
84
+ python3 harness/measure.py --bundled # Playwright's bundled Chromium
85
+ ```
86
+
87
+ Run it before writing any humanization code: the numbers above are the baseline
88
+ every change is measured against.
89
+
90
+ ## Licence
91
+
92
+ Proprietary and commercial — see [`LICENSE`](LICENSE). This is not open source.
93
+ Distribution is gated on an active licence
94
+ ([openonion/browser#6](https://github.com/openonion/browser/issues/6)).
@@ -0,0 +1,167 @@
1
+ """Measure the input-timing distributions a driver actually produces.
2
+
3
+ Run against stock Playwright to get the baseline every humanization change is
4
+ measured against. The point is distributions, not paths: detectors compare
5
+ moments of empirical distributions (DataDome ships sigma/mu/angle medians;
6
+ Unbotify's patent describes histogram comparison), so a trajectory that looks
7
+ human in a screenshot but has the wrong variance is not an improvement.
8
+
9
+ python3 harness/measure.py # stock playwright, system Chrome
10
+ python3 harness/measure.py --bundled # playwright's bundled Chromium
11
+
12
+ Served over http://127.0.0.1 rather than set_content(): localhost is a secure
13
+ context, so pointerrawupdate and getCoalescedEvents behave as they would in
14
+ production. On about:blank they silently disappear.
15
+ """
16
+
17
+ import argparse
18
+ import functools
19
+ import http.server
20
+ import json
21
+ import math
22
+ import statistics
23
+ import threading
24
+ from pathlib import Path
25
+
26
+ from playwright.sync_api import sync_playwright
27
+
28
+ HERE = Path(__file__).parent
29
+ PORT = 8931
30
+
31
+
32
+ def serve():
33
+ handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(HERE))
34
+ handler.log_message = lambda *a: None
35
+ server = http.server.ThreadingHTTPServer(("127.0.0.1", PORT), handler)
36
+ threading.Thread(target=server.serve_forever, daemon=True).start()
37
+ return server
38
+
39
+
40
+ def drive(page):
41
+ """One scenario: move to the field, type, then move to the button and click."""
42
+ page.mouse.move(60, 60)
43
+ page.click("#field")
44
+ page.keyboard.type("hello world", delay=80)
45
+ page.mouse.move(480, 344, steps=25)
46
+ page.click("#target")
47
+
48
+
49
+ def spans(events, down, up, key=lambda e: None):
50
+ """Durations from each `down` to the matching `up`, in ms."""
51
+ open_at = {}
52
+ out = []
53
+ for e in events:
54
+ if e["type"] == down:
55
+ open_at[key(e)] = e["t"]
56
+ elif e["type"] == up and key(e) in open_at:
57
+ out.append(e["t"] - open_at.pop(key(e)))
58
+ return out
59
+
60
+
61
+ def flights(events):
62
+ """keyup -> next keydown, in ms. The interval Playwright's `delay` does NOT set."""
63
+ out = []
64
+ last_up = None
65
+ for e in events:
66
+ if e["type"] == "keyup":
67
+ last_up = e["t"]
68
+ elif e["type"] == "keydown" and last_up is not None:
69
+ out.append(e["t"] - last_up)
70
+ return out
71
+
72
+
73
+ def describe(name, values, human):
74
+ """Median leads: one setup gap (the click before typing) skews the mean badly
75
+ at these sample sizes, and it is the steady-state interval we care about."""
76
+ if not values:
77
+ print(f" {name:<26} (none observed)")
78
+ return
79
+ print(f" {name:<26} n={len(values):<3} median={statistics.median(values):8.2f}ms "
80
+ f"mean={statistics.mean(values):8.2f} sd={statistics.pstdev(values):7.2f} human≈{human}")
81
+
82
+
83
+ def longest_run(events):
84
+ """The longest unbroken stretch of mousemove events.
85
+
86
+ A scenario contains both teleports (page.click jumps straight to the target)
87
+ and travelled paths. Mixing them makes a teleport look like curvature, which
88
+ flatters the straightness index. Measure one journey at a time.
89
+ """
90
+ # pointermove/pointerrawupdate interleave with every mousemove and must stay
91
+ # transparent here, or every run is length 1.
92
+ transparent = {"pointermove", "pointerrawupdate", "mouseover"}
93
+ runs, current = [], []
94
+ for e in events:
95
+ if e["type"] in transparent:
96
+ continue
97
+ if e["type"] == "mousemove":
98
+ current.append(e)
99
+ elif current:
100
+ runs.append(current)
101
+ current = []
102
+ runs.append(current)
103
+ return max(runs, key=len)
104
+
105
+
106
+ def path_metrics(moves):
107
+ """Straightness and velocity variability over a mouse path."""
108
+ pts = [(m["clientX"], m["clientY"], m["t"]) for m in moves]
109
+ if len(pts) < 3:
110
+ print(f" path only {len(pts)} move events — no path to measure")
111
+ return
112
+ step = [math.dist(pts[i][:2], pts[i + 1][:2]) for i in range(len(pts) - 1)]
113
+ gap = [pts[i + 1][2] - pts[i][2] for i in range(len(pts) - 1)]
114
+ direct = math.dist(pts[0][:2], pts[-1][:2]) or 1
115
+ speeds = [d / g for d, g in zip(step, gap) if g > 0]
116
+ cov = statistics.pstdev(speeds) / statistics.mean(speeds) if speeds else 0
117
+ print(f" path n={len(pts)} moves")
118
+ print(f" straightness index {sum(step) / direct:.4f} human≈1.02–1.5, bot→1.0000")
119
+ print(f" velocity CoV {cov:.4f} human>0.4")
120
+ print(f" step length sd {statistics.pstdev(step):.4f} 0 means linear interpolation")
121
+ print(f" inter-event gap mean={statistics.mean(gap):.2f}ms sd={statistics.pstdev(gap):.2f}"
122
+ f" real mice quantize to 8ms@125Hz")
123
+
124
+
125
+ def main():
126
+ ap = argparse.ArgumentParser()
127
+ ap.add_argument("--bundled", action="store_true", help="use Playwright's bundled Chromium")
128
+ args = ap.parse_args()
129
+
130
+ serve()
131
+ with sync_playwright() as p:
132
+ launch = {"headless": False} if args.bundled else {"headless": False, "channel": "chrome"}
133
+ browser = p.chromium.launch(**launch)
134
+ page = browser.new_page()
135
+ page.goto(f"http://127.0.0.1:{PORT}/page.html")
136
+ drive(page)
137
+ events = page.evaluate("window.__events")
138
+ ctx = page.evaluate("window.__ctx")
139
+ version = browser.version
140
+ browser.close()
141
+
142
+ print(f"\nengine {'bundled chromium' if args.bundled else 'system chrome'} {version}")
143
+ print(f"context secure={ctx['isSecureContext']} coalescedAPI={ctx['hasCoalesced']} "
144
+ f"webdriver={ctx['webdriver']}")
145
+
146
+ mouse = [e for e in events if e["type"] == "mousemove"]
147
+ leak = sum(1 for e in mouse if e["clientX"] == e["screenX"] and e["clientY"] == e["screenY"])
148
+ print(f"screen/client coords {leak}/{len(mouse)} identical "
149
+ f"(brotector Input.cordinatesLeak; fixed upstream ~Chrome 142)")
150
+ print(f"isTrusted all={all(e['trusted'] for e in events)}")
151
+
152
+ print("\nkeyboard")
153
+ describe("hold (keydown->keyup)", spans(events, "keydown", "keyup", lambda e: e["code"]), "60–150ms, sd tens")
154
+ describe("flight (keyup->keydown)", flights(events), "200–400ms")
155
+
156
+ print("\nmouse")
157
+ describe("click hold (down->up)", spans(events, "mousedown", "mouseup"), "60–150ms")
158
+ dwell = spans(events, "mouseover", "mousedown", lambda e: e.get("target"))
159
+ describe("hover->press dwell", dwell, "hundreds of ms")
160
+ path_metrics(longest_run(events))
161
+
162
+ Path(HERE / "last-run.json").write_text(json.dumps(events, indent=1))
163
+ print(f"\nraw events -> {HERE / 'last-run.json'}\n")
164
+
165
+
166
+ if __name__ == "__main__":
167
+ main()
@@ -0,0 +1,70 @@
1
+ <!doctype html>
2
+ <meta charset="utf-8">
3
+ <title>input measurement harness</title>
4
+ <style>
5
+ body { font: 14px system-ui; margin: 40px; }
6
+ #target { width: 160px; height: 48px; margin-top: 300px; margin-left: 400px; }
7
+ #field { width: 300px; padding: 8px; }
8
+ </style>
9
+
10
+ <h1>input measurement harness</h1>
11
+ <p>Records raw input events for distribution analysis. Nothing here is stealth
12
+ logic — this is the instrument, not the thing being measured.</p>
13
+
14
+ <input id="field" placeholder="typing target">
15
+ <button id="target">click target</button>
16
+
17
+ <script>
18
+ // One flat event log. Analysis happens in Python, where the distribution work
19
+ // belongs — this file only needs to observe without perturbing timing.
20
+ window.__events = [];
21
+
22
+ function record(e) {
23
+ const r = {
24
+ type: e.type,
25
+ t: e.timeStamp,
26
+ trusted: e.isTrusted,
27
+ };
28
+ if (e instanceof MouseEvent) {
29
+ Object.assign(r, {
30
+ clientX: e.clientX, clientY: e.clientY,
31
+ screenX: e.screenX, screenY: e.screenY,
32
+ movementX: e.movementX, movementY: e.movementY,
33
+ buttons: e.buttons, detail: e.detail,
34
+ target: e.target.id || null,
35
+ });
36
+ }
37
+ if (e instanceof PointerEvent) {
38
+ Object.assign(r, {
39
+ pointerId: e.pointerId, pointerType: e.pointerType,
40
+ pressure: e.pressure, tiltX: e.tiltX, tiltY: e.tiltY,
41
+ // Coalescing is how real pointer streams arrive; a driver that emits one
42
+ // discrete event per move never produces a group here.
43
+ coalesced: e.getCoalescedEvents ? e.getCoalescedEvents().length : -1,
44
+ });
45
+ }
46
+ if (e instanceof KeyboardEvent) {
47
+ Object.assign(r, { key: e.key, code: e.code, repeat: e.repeat });
48
+ }
49
+ window.__events.push(r);
50
+ }
51
+
52
+ for (const type of [
53
+ 'mousemove', 'mousedown', 'mouseup', 'click', 'mouseover',
54
+ 'pointermove', 'pointerrawupdate', 'pointerdown', 'pointerup',
55
+ 'keydown', 'keyup', 'wheel',
56
+ ]) {
57
+ window.addEventListener(type, record, { capture: true, passive: true });
58
+ }
59
+
60
+ // Secure-context gate check. pointerrawupdate and getCoalescedEvents are only
61
+ // available in a secure context; testing on about:blank or a data: URL makes
62
+ // them look absent and produces wrong conclusions.
63
+ window.__ctx = {
64
+ isSecureContext: window.isSecureContext,
65
+ hasCoalesced: typeof PointerEvent.prototype.getCoalescedEvents === 'function',
66
+ webdriver: navigator.webdriver,
67
+ outerInnerDelta: window.outerHeight - window.innerHeight,
68
+ screen: { x: window.screenX, y: window.screenY },
69
+ };
70
+ </script>
@@ -0,0 +1,16 @@
1
+ # onionwright
2
+
3
+ Humanized input for browser automation — a commercial layer over Playwright that
4
+ makes automated interaction behave like a person using the browser.
5
+
6
+ **This is a placeholder release. There is no implementation in this version.**
7
+
8
+ onionwright is proprietary software distributed under a commercial licence, not
9
+ from this index. This entry reserves the name and points at the product.
10
+
11
+ - Product: https://openonion.ai
12
+ - Docs: https://docs.connectonion.com
13
+ - Licensing enquiries: info@openonion.ai
14
+
15
+ Licensed, not sold. Redistribution, resale, and reverse engineering are not
16
+ permitted. See the LICENSE included in the distribution.
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "onionwright"
7
+ version = "0.0.1"
8
+ description = "Humanized input for browser automation"
9
+ # NOT the repo README. That one is internal: it carries the positioning, the
10
+ # measured competitive baseline, and links to private repos that would 404 for
11
+ # everyone reading PyPI. What ships to a public index is the minimal one.
12
+ readme = "packaging/README-pypi.md"
13
+ requires-python = ">=3.9"
14
+ license = { file = "LICENSE" }
15
+ authors = [{ name = "OpenOnion", email = "info@openonion.ai" }]
16
+ keywords = ["playwright", "automation", "browser", "human", "input"]
17
+
18
+ # Proprietary. The trove classifier matters: PyPI shows it on the project page,
19
+ # and it is the difference between a developer assuming they can vendor this and
20
+ # knowing they cannot.
21
+ classifiers = [
22
+ "License :: Other/Proprietary License",
23
+ "Programming Language :: Python :: 3",
24
+ "Topic :: Software Development :: Testing",
25
+ "Intended Audience :: Developers",
26
+ ]
27
+
28
+ dependencies = ["playwright>=1.55"]
29
+
30
+ [project.urls]
31
+ Homepage = "https://openonion.ai"
32
+ Documentation = "https://docs.connectonion.com"
33
+
34
+ [tool.hatch.build.targets.wheel]
35
+ packages = ["src/onionwright"]
@@ -0,0 +1,7 @@
1
+ """onionwright — humanized input for browser automation.
2
+
3
+ Placeholder release. The package name is reserved; the implementation is tracked
4
+ in https://github.com/openonion/onionwright/issues
5
+ """
6
+
7
+ __version__ = "0.0.1"