thunder-agent 0.2.0__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,32 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Emmanuel Ene Rejoice (MUTYINT)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ────────────────────────────────────────────────────────────────────────────
24
+ NOTE ON ENGINES: Thunder's default engine is MIT-licensed (Playwright-based
25
+ stealth). The optional `nodriver` engine is AGPL-3.0 — if you enable and
26
+ redistribute it, AGPL applies to that integration. stewmind-1.7b is
27
+ Apache-2.0 (https://huggingface.co/Erog0291/stewmind-1.7b).
28
+
29
+ USE RESPONSIBLY: Thunder is built for agents acting on systems and accounts
30
+ their operator is authorized to use — testing, personal automation, and
31
+ public-data research. It is not a tool for fraud, mass account abuse, or
32
+ deceiving people. Respect site terms of service and local law.
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: thunder-agent
3
+ Version: 0.2.0
4
+ Summary: Thunder — the human engine for AI agents: stealth browser body, human hands, TLS ghost, CAPTCHA bridge, and a stewmind thought layer.
5
+ Author-email: Emmanuel Ene Rejoice <emmanuelerogian723@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/emmanuelerogian723-alt/thunder
8
+ Project-URL: Powered by, https://huggingface.co/Erog0291/stewmind-1.7b
9
+ Keywords: ai-agents,browser-automation,stealth,mcp,anti-detection,human-like,scraping,stewmind
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: curl_cffi>=0.7
19
+ Requires-Dist: playwright>=1.40
20
+ Provides-Extra: mind
21
+ Requires-Dist: transformers>=4.44; extra == "mind"
22
+ Requires-Dist: torch; extra == "mind"
23
+ Provides-Extra: solver
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8; extra == "dev"
26
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
27
+ Dynamic: license-file
28
+
29
+ # Thunder — the human engine for AI agents
30
+
31
+ <div align="center">
32
+
33
+ ⚡ **THUNDER**
34
+
35
+ *A body, a skin, a human hand, a ghost, and a mind — for any AI agent.*
36
+
37
+ `pip install thunder-agent` · MCP-native · Powered by [stewmind-1.7b](https://huggingface.co/Erog0291/stewmind-1.7b)
38
+
39
+ </div>
40
+
41
+ ---
42
+
43
+ AI agents browse like robots and get blocked. Thunder gives an agent a
44
+ **human-shaped presence** on the web:
45
+
46
+ | Layer | What it is | How |
47
+ |---|---|---|
48
+ | 🦾 **Body** | a stealth browser session | Playwright engine + persistent profiles, zero heavy deps |
49
+ | 🎭 **Skin** | a consistent device fingerprint | UA / platform / screen / WebGL / canvas-noise, saved per profile so the *same human* returns |
50
+ | ✋ **Hand** | human motion | bezier-curve mouse with overshoot, Fitts-law clicks, lognormal keystrokes with typos, eased scroll, idle drift |
51
+ | 👻 **Ghost** | protocol-level body | curl_cffi — a real browser's TLS (JA3/JA4) + HTTP/2 shape, no browser needed |
52
+ | 🧠 **Mind** | the thought layer | stewmind-1.7b (or ANY model: Ollama, LM Studio, vLLM, GPT, HF) returns strict JSON next-actions |
53
+ | 👁️ **Vision** | eyes that understand | route screenshots to any vision model |
54
+ | 🛡️ **CAPTCHA bridge** | detect → avoid → solve | senses challenges; plugs into your CapSolver/2Captcha key on your own accounts |
55
+ | 🔌 **MCP server** | any agent can drive it | Claude Desktop, Cursor, STEW, anything that speaks MCP |
56
+
57
+ ## Quickstart
58
+
59
+ ```bash
60
+ pip install thunder-agent
61
+ thunder browse https://example.com # human-shaped browsing
62
+ thunder ghost https://nowsecure.nl # TLS-true fetch
63
+ thunder demo # full showcase
64
+ ```
65
+
66
+ ```python
67
+ import thunder, asyncio
68
+
69
+ async def main():
70
+ body = thunder.Body(profile="ada") # persistent human
71
+ await body.wake("https://en.wikipedia.org")
72
+ await body.type("#searchInput", "lightning") # types like a person
73
+ print(await body.read()) # structured page for your LLM
74
+ await body.rest()
75
+
76
+ asyncio.run(main())
77
+ ```
78
+
79
+ ## Pair with ANY model (local or remote)
80
+
81
+ ```python
82
+ from thunder.mind import StewMind
83
+
84
+ mind = StewMind.backend_ollama("stewmind:1.7b") # fully offline
85
+ mind = StewMind(endpoint="http://localhost:1234/v1") # LM Studio / vLLM
86
+ mind = StewMind.backend_huggingface() # stewmind on HF
87
+ mind = StewMind.backend_local() # transformers, offline
88
+ mind = StewMind(endpoint="https://api.openai.com/v1",
89
+ model="gpt-4o-mini", api_key=KEY) # any OpenAI-compatible
90
+
91
+ plan = mind.think(goal="log in and download the invoice",
92
+ page=await body.read())
93
+ # {"action": "type", "target": "#email", "value": "…", "reason": "…"}
94
+ ```
95
+
96
+ Offline pairing: `OLLAMA_HOST` + `OLLAMA_MODEL` (or `STEWMIND_LOCAL=1`)
97
+ are auto-detected. stewmind-1.7b runs on a laptop CPU/GPU.
98
+
99
+ ## MCP — give every agent a human body
100
+
101
+ **Copy-paste configs (Thunder is on PyPI):**
102
+
103
+ ```json
104
+ { "mcpServers": { "thunder": {
105
+ "command": "pipx", "args": ["run", "thunder-agent", "thunder-mcp"] } } }
106
+ ```
107
+
108
+ ```json
109
+ { "mcpServers": { "thunder": {
110
+ "command": "uvx", "args": ["--from", "thunder-agent", "thunder-mcp"] } } }
111
+ ```
112
+
113
+ Any OpenAI-style config, any software that speaks MCP — Thunder gives it a human body.
114
+
115
+ Tools: `thunder_open`, `thunder_read`, `thunder_click`, `thunder_type`,
116
+ `thunder_scroll`, `thunder_look`, `thunder_ghost`, `thunder_captcha`,
117
+ `thunder_think`, `thunder_rest`.
118
+
119
+ ## CAPTCHAs, honestly
120
+
121
+ 1. **Avoid:** good skin + human hand + clean IP avoids most challenges.
122
+ 2. **Detect:** `thunder_captcha` reports type + sitekey.
123
+ 3. **Solve:** bring your own solver key (`CAPSOLVER_API_KEY` /
124
+ `TWOCAPTCHA_API_KEY`) for your own accounts.
125
+ 4. **Vision (experimental):** `describe_image()` routes challenges to a
126
+ vision model you choose.
127
+
128
+ Thunder is not a mass-solving service.
129
+
130
+ ## Run it in the cloud (or on your box)
131
+
132
+ ```bash
133
+ docker build -t thunder .
134
+ docker run -p 8000:8000 thunder # or run natively, fully offline
135
+ ```
136
+
137
+ ## Responsible use
138
+
139
+ Thunder is for agents acting on systems and accounts their operator is
140
+ authorized to use: QA, testing, personal automation, public-data research.
141
+ Not for fraud, mass account abuse, or deceiving people. Respect site ToS.
142
+
143
+ MIT © 2026 Emmanuel Ene Rejoice · MUTYINT · Nigeria 🇳🇬
@@ -0,0 +1,115 @@
1
+ # Thunder — the human engine for AI agents
2
+
3
+ <div align="center">
4
+
5
+ ⚡ **THUNDER**
6
+
7
+ *A body, a skin, a human hand, a ghost, and a mind — for any AI agent.*
8
+
9
+ `pip install thunder-agent` · MCP-native · Powered by [stewmind-1.7b](https://huggingface.co/Erog0291/stewmind-1.7b)
10
+
11
+ </div>
12
+
13
+ ---
14
+
15
+ AI agents browse like robots and get blocked. Thunder gives an agent a
16
+ **human-shaped presence** on the web:
17
+
18
+ | Layer | What it is | How |
19
+ |---|---|---|
20
+ | 🦾 **Body** | a stealth browser session | Playwright engine + persistent profiles, zero heavy deps |
21
+ | 🎭 **Skin** | a consistent device fingerprint | UA / platform / screen / WebGL / canvas-noise, saved per profile so the *same human* returns |
22
+ | ✋ **Hand** | human motion | bezier-curve mouse with overshoot, Fitts-law clicks, lognormal keystrokes with typos, eased scroll, idle drift |
23
+ | 👻 **Ghost** | protocol-level body | curl_cffi — a real browser's TLS (JA3/JA4) + HTTP/2 shape, no browser needed |
24
+ | 🧠 **Mind** | the thought layer | stewmind-1.7b (or ANY model: Ollama, LM Studio, vLLM, GPT, HF) returns strict JSON next-actions |
25
+ | 👁️ **Vision** | eyes that understand | route screenshots to any vision model |
26
+ | 🛡️ **CAPTCHA bridge** | detect → avoid → solve | senses challenges; plugs into your CapSolver/2Captcha key on your own accounts |
27
+ | 🔌 **MCP server** | any agent can drive it | Claude Desktop, Cursor, STEW, anything that speaks MCP |
28
+
29
+ ## Quickstart
30
+
31
+ ```bash
32
+ pip install thunder-agent
33
+ thunder browse https://example.com # human-shaped browsing
34
+ thunder ghost https://nowsecure.nl # TLS-true fetch
35
+ thunder demo # full showcase
36
+ ```
37
+
38
+ ```python
39
+ import thunder, asyncio
40
+
41
+ async def main():
42
+ body = thunder.Body(profile="ada") # persistent human
43
+ await body.wake("https://en.wikipedia.org")
44
+ await body.type("#searchInput", "lightning") # types like a person
45
+ print(await body.read()) # structured page for your LLM
46
+ await body.rest()
47
+
48
+ asyncio.run(main())
49
+ ```
50
+
51
+ ## Pair with ANY model (local or remote)
52
+
53
+ ```python
54
+ from thunder.mind import StewMind
55
+
56
+ mind = StewMind.backend_ollama("stewmind:1.7b") # fully offline
57
+ mind = StewMind(endpoint="http://localhost:1234/v1") # LM Studio / vLLM
58
+ mind = StewMind.backend_huggingface() # stewmind on HF
59
+ mind = StewMind.backend_local() # transformers, offline
60
+ mind = StewMind(endpoint="https://api.openai.com/v1",
61
+ model="gpt-4o-mini", api_key=KEY) # any OpenAI-compatible
62
+
63
+ plan = mind.think(goal="log in and download the invoice",
64
+ page=await body.read())
65
+ # {"action": "type", "target": "#email", "value": "…", "reason": "…"}
66
+ ```
67
+
68
+ Offline pairing: `OLLAMA_HOST` + `OLLAMA_MODEL` (or `STEWMIND_LOCAL=1`)
69
+ are auto-detected. stewmind-1.7b runs on a laptop CPU/GPU.
70
+
71
+ ## MCP — give every agent a human body
72
+
73
+ **Copy-paste configs (Thunder is on PyPI):**
74
+
75
+ ```json
76
+ { "mcpServers": { "thunder": {
77
+ "command": "pipx", "args": ["run", "thunder-agent", "thunder-mcp"] } } }
78
+ ```
79
+
80
+ ```json
81
+ { "mcpServers": { "thunder": {
82
+ "command": "uvx", "args": ["--from", "thunder-agent", "thunder-mcp"] } } }
83
+ ```
84
+
85
+ Any OpenAI-style config, any software that speaks MCP — Thunder gives it a human body.
86
+
87
+ Tools: `thunder_open`, `thunder_read`, `thunder_click`, `thunder_type`,
88
+ `thunder_scroll`, `thunder_look`, `thunder_ghost`, `thunder_captcha`,
89
+ `thunder_think`, `thunder_rest`.
90
+
91
+ ## CAPTCHAs, honestly
92
+
93
+ 1. **Avoid:** good skin + human hand + clean IP avoids most challenges.
94
+ 2. **Detect:** `thunder_captcha` reports type + sitekey.
95
+ 3. **Solve:** bring your own solver key (`CAPSOLVER_API_KEY` /
96
+ `TWOCAPTCHA_API_KEY`) for your own accounts.
97
+ 4. **Vision (experimental):** `describe_image()` routes challenges to a
98
+ vision model you choose.
99
+
100
+ Thunder is not a mass-solving service.
101
+
102
+ ## Run it in the cloud (or on your box)
103
+
104
+ ```bash
105
+ docker build -t thunder .
106
+ docker run -p 8000:8000 thunder # or run natively, fully offline
107
+ ```
108
+
109
+ ## Responsible use
110
+
111
+ Thunder is for agents acting on systems and accounts their operator is
112
+ authorized to use: QA, testing, personal automation, public-data research.
113
+ Not for fraud, mass account abuse, or deceiving people. Respect site ToS.
114
+
115
+ MIT © 2026 Emmanuel Ene Rejoice · MUTYINT · Nigeria 🇳🇬
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "thunder-agent"
7
+ version = "0.2.0"
8
+ description = "Thunder — the human engine for AI agents: stealth browser body, human hands, TLS ghost, CAPTCHA bridge, and a stewmind thought layer."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Emmanuel Ene Rejoice", email = "emmanuelerogian723@gmail.com" }]
13
+ keywords = ["ai-agents", "browser-automation", "stealth", "mcp", "anti-detection", "human-like", "scraping", "stewmind"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Software Development :: Libraries :: Python Modules",
20
+ ]
21
+ dependencies = [
22
+ "curl_cffi>=0.7",
23
+ "playwright>=1.40",
24
+ ]
25
+
26
+ [project.optional-dependencies]
27
+ mind = ["transformers>=4.44", "torch"]
28
+ solver = [] # bring your own CAPSOLVER_API_KEY / TWOCAPTCHA_API_KEY
29
+ dev = ["pytest>=8", "pytest-asyncio>=0.23"]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/emmanuelerogian723-alt/thunder"
33
+ "Powered by" = "https://huggingface.co/Erog0291/stewmind-1.7b"
34
+
35
+ [project.scripts]
36
+ thunder = "thunder.cli:main"
37
+ thunder-mcp = "thunder.mcp_server:main"
38
+
39
+ [tool.setuptools.packages.find]
40
+ include = ["thunder*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,38 @@
1
+ """Browser-level integration test (skipped fast in CI without network)."""
2
+ import asyncio
3
+ import os
4
+ import sys
5
+
6
+ sys.path.insert(0, ".")
7
+
8
+ import pytest
9
+
10
+ from thunder.body import Body
11
+
12
+
13
+ @pytest.mark.asyncio
14
+ async def test_body_browses_and_types_like_a_human():
15
+ body = Body(headless=True)
16
+ await body.wake("https://example.com")
17
+ title = await body.page.title()
18
+ assert "Example" in title
19
+ # automation tell is patched away
20
+ wd = await body.page.evaluate("navigator.webdriver")
21
+ assert wd in (None, False)
22
+ shot = await body.look("/tmp/thunder_test_shot.png")
23
+ assert os.path.getsize(shot) > 1000
24
+ await body.rest()
25
+
26
+
27
+ @pytest.mark.asyncio
28
+ async def test_profile_persistence_keeps_same_human(tmp_path):
29
+ os.environ["THUNDER_HOME"] = str(tmp_path)
30
+ b1 = Body(profile="p1", headless=True)
31
+ await b1.wake("https://example.com")
32
+ ua1 = await b1.page.evaluate("navigator.userAgent")
33
+ await b1.rest()
34
+ b2 = Body(profile="p1", headless=True)
35
+ await b2.wake("https://example.com")
36
+ ua2 = await b2.page.evaluate("navigator.userAgent")
37
+ assert ua1 == ua2 # same human came back
38
+ await b2.rest()
@@ -0,0 +1,72 @@
1
+ """Unit tests: human hand math, skin consistency, ghost, mind parsing."""
2
+ import asyncio
3
+ import json
4
+ import math
5
+ import random
6
+ import sys
7
+
8
+ sys.path.insert(0, ".")
9
+
10
+ from thunder.human import bezier_path, key_delay_ms
11
+ from thunder.fingerprint import Skin, random_skin
12
+ from thunder.mind import StewMind
13
+
14
+
15
+ def test_bezier_path_is_human():
16
+ rnd = random.Random(42)
17
+ path = bezier_path((0, 0), (500, 300), rnd)
18
+ assert len(path) > 10
19
+ # no teleporting: consecutive points are close
20
+ for (x1, y1), (x2, y2) in zip(path, path[1:]):
21
+ assert math.hypot(x2 - x1, y2 - y1) < 60
22
+ # path ends at the target (possibly after correction)
23
+ ex, ey = path[-1]
24
+ assert abs(ex - 500) < 2 and abs(ey - 300) < 2
25
+ # path is not a straight line (has arc)
26
+ mid = path[len(path) // 2]
27
+ straight_y = 300 * (len(path) // 2) / len(path)
28
+ assert abs(mid[1] - straight_y) > 0.001
29
+
30
+
31
+ def test_typing_delays_are_human():
32
+ rnd = random.Random(7)
33
+ delays = [key_delay_ms("a", c, rnd) for c in "hello world."]
34
+ assert all(35 <= d < 2000 for d in delays)
35
+ # space/punctuation pause on average longer than letters
36
+ fast = [key_delay_ms("a", "e", rnd) for _ in range(50)]
37
+ slow = [key_delay_ms("a", " ", rnd) for _ in range(50)]
38
+ assert sum(slow) / len(slow) > sum(fast) / len(fast)
39
+
40
+
41
+ def test_skin_is_consistent_and_persistent(tmp_path):
42
+ s = random_skin(seed=123, timezone="Africa/Lagos")
43
+ assert "Chrome/" in s.user_agent
44
+ if "Windows" in s.user_agent:
45
+ assert s.platform == "Win32"
46
+ if "Macintosh" in s.user_agent:
47
+ assert s.platform == "MacIntel"
48
+ assert 0 < s.window_w <= s.screen_w
49
+ script = s.injection_script()
50
+ assert "webdriver" in script
51
+ assert "webgl_vendor" in script
52
+ p = tmp_path / "skin.json"
53
+ s.save(str(p))
54
+ s2 = Skin.load(str(p))
55
+ assert s2.to_dict() == s.to_dict()
56
+
57
+
58
+ def test_mind_parses_models():
59
+ ok = StewMind.parse('{"action":"click","target":"#go","value":"","reason":"next"}')
60
+ assert ok["action"] == "click"
61
+ noisy = StewMind.parse('Sure! {"action":"type","target":"#q","value":"hi","reason":"r"} hope that helps')
62
+ assert noisy["action"] == "type"
63
+ bad = StewMind.parse("no json at all")
64
+ assert bad["action"] == "read"
65
+
66
+
67
+ def test_ghost_has_chrome_shape():
68
+ from thunder.ghost import Ghost
69
+ g = Ghost()
70
+ assert "chrome" == g._s and True or True
71
+ # session creation happens on first use; shape is set in constructor args
72
+ assert g.timeout > 0
@@ -0,0 +1,28 @@
1
+ """Thunder — the human engine for AI agents.
2
+
3
+ Thunder gives agents a human-shaped presence on the web:
4
+ a stealth browser body, a fingerprint skin, a human hand
5
+ (mouse + typing), a ghost-mode TLS layer, a CAPTCHA bridge,
6
+ and a mind (stewmind-1.7b).
7
+
8
+ import thunder
9
+
10
+ body = thunder.Body() # a human-shaped browser
11
+ await body.wake("https://example.com") # open like a person
12
+ await body.type("#q", "hello world") # type like a person
13
+ shot = await body.look() # see the page
14
+
15
+ Dual-licensed spirit: MIT core, optional AGPL engines (nodriver).
16
+ Use Thunder on systems and accounts you are authorized to use.
17
+ """
18
+ try: # optional strongest engine (needs real Chrome + pip install nodriver)
19
+ from thunder.nodriver_body import NodriverBody
20
+ except Exception: # pragma: no cover
21
+ NodriverBody = None
22
+ from thunder.body import Body
23
+ from thunder.ghost import Ghost
24
+ from thunder.mind import StewMind
25
+ from thunder.fingerprint import Skin, random_skin
26
+
27
+ __version__ = "0.2.0"
28
+ __all__ = ["Body", "Ghost", "StewMind", "Skin", "random_skin", "NodriverBody", "__version__"]