shadey 0.1.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.
shadey-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,165 @@
1
+ Metadata-Version: 2.4
2
+ Name: shadey
3
+ Version: 0.1.0
4
+ Summary: Stealth browser sessions for AI agents
5
+ Home-page: https://shadey.dev
6
+ Author: Shadey
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: aiohttp>=3.8
14
+ Provides-Extra: playwright
15
+ Requires-Dist: playwright>=1.40; extra == "playwright"
16
+ Dynamic: author
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: home-page
21
+ Dynamic: provides-extra
22
+ Dynamic: requires-dist
23
+ Dynamic: requires-python
24
+ Dynamic: summary
25
+
26
+ # shadey
27
+
28
+ Stealth browser sessions for AI agents. Pass Cloudflare, DataDome, PerimeterX, and Kasada from any IP.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install shadey
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ```python
39
+ import asyncio
40
+ from shadey import Shadey
41
+
42
+ async def main():
43
+ async with Shadey("sk_your_api_key") as client:
44
+ async with await client.session() as session:
45
+ # Navigate and extract — no CDP/Playwright needed
46
+ await session.goto("https://example.com")
47
+ title = await session.evaluate("document.title")
48
+ print(title["result"])
49
+
50
+ # Extract text from elements
51
+ headings = await session.extract("h1", all=True)
52
+ print(headings["data"])
53
+
54
+ # Get full page HTML
55
+ page = await session.content()
56
+ print(f"{page['length']} bytes")
57
+
58
+ # Screenshot
59
+ img = await session.screenshot() # base64 jpeg
60
+
61
+ asyncio.run(main())
62
+ ```
63
+
64
+ ## With Behavioral Actions
65
+
66
+ ```python
67
+ async with Shadey("sk_your_api_key") as client:
68
+ async with await client.session() as session:
69
+ await session.goto("https://nowsecure.nl")
70
+
71
+ # Human-like interactions to build trust
72
+ await session.idle(duration_ms=3000)
73
+ await session.scroll(400)
74
+ await session.click(500, 300)
75
+ await session.type_text("hello world", context="search")
76
+
77
+ # Then extract what you need
78
+ result = await session.extract(".result-title", all=True)
79
+ ```
80
+
81
+ ## With Playwright (CDP)
82
+
83
+ ```python
84
+ async with Shadey("sk_your_api_key") as client:
85
+ async with await client.session() as session:
86
+ # Full browser control via CDP
87
+ from playwright.async_api import async_playwright
88
+ async with async_playwright() as p:
89
+ browser = await p.chromium.connect_over_cdp(session.playwright_url)
90
+ page = browser.contexts[0].pages[0]
91
+ await page.goto("https://nowsecure.nl")
92
+ print(await page.title())
93
+ ```
94
+
95
+ ## API
96
+
97
+ ### `Shadey(api_key, base_url="https://api.shadey.dev")`
98
+
99
+ Create a client. Use as async context manager or call `.close()` manually.
100
+
101
+ ### `client.session(stealth="full", proxy=None, profile=None)`
102
+
103
+ Create a stealth browser session. Returns a `ShadeySession`.
104
+
105
+ ### REST Actions
106
+
107
+ ### `session.goto(url, wait_ms=3000)`
108
+
109
+ Navigate to a URL. Waits for page load + extra `wait_ms`.
110
+
111
+ ### `session.screenshot()`
112
+
113
+ Returns a base64 JPEG data URI of the current page.
114
+
115
+ ### `session.extract(selector, attribute="textContent", all=False)`
116
+
117
+ Extract data from the page. Set `all=True` to get all matching elements.
118
+
119
+ ### `session.evaluate(expression)`
120
+
121
+ Run JavaScript and return the result.
122
+
123
+ ### `session.content()`
124
+
125
+ Get the full page HTML.
126
+
127
+ ### Behavioral Actions
128
+
129
+ ### `session.click(x, y, width=50)`
130
+
131
+ Move mouse and click at coordinates with Fitts' law timing and corrective submovements.
132
+
133
+ ### `session.type_text(text, context="form")`
134
+
135
+ Type text with QWERTY bigram-modeled keystroke timing. Context: `"form"`, `"search"`, `"url"`.
136
+
137
+ ### `session.scroll(distance)`
138
+
139
+ Scroll with momentum and inertia. Positive = down, negative = up.
140
+
141
+ ### `session.dwell(context="content")`
142
+
143
+ Wait a human-realistic amount of time. Context: `"content"`, `"search"`, `"form"`.
144
+
145
+ ### `session.idle(duration_ms=5000)`
146
+
147
+ Simulate idle browsing — mouse movements, scrolls, pauses driven by HMM behavioral model.
148
+
149
+ ### CDP Access
150
+
151
+ ### `session.playwright_url` / `session.puppeteer_url`
152
+
153
+ CDP WebSocket URL for direct browser control.
154
+
155
+ ### `session.close()`
156
+
157
+ Destroy the session.
158
+
159
+ ### `client.usage()`
160
+
161
+ Get plan usage stats.
162
+
163
+ ### `client.health()`
164
+
165
+ Check API health.
shadey-0.1.0/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # shadey
2
+
3
+ Stealth browser sessions for AI agents. Pass Cloudflare, DataDome, PerimeterX, and Kasada from any IP.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install shadey
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ import asyncio
15
+ from shadey import Shadey
16
+
17
+ async def main():
18
+ async with Shadey("sk_your_api_key") as client:
19
+ async with await client.session() as session:
20
+ # Navigate and extract — no CDP/Playwright needed
21
+ await session.goto("https://example.com")
22
+ title = await session.evaluate("document.title")
23
+ print(title["result"])
24
+
25
+ # Extract text from elements
26
+ headings = await session.extract("h1", all=True)
27
+ print(headings["data"])
28
+
29
+ # Get full page HTML
30
+ page = await session.content()
31
+ print(f"{page['length']} bytes")
32
+
33
+ # Screenshot
34
+ img = await session.screenshot() # base64 jpeg
35
+
36
+ asyncio.run(main())
37
+ ```
38
+
39
+ ## With Behavioral Actions
40
+
41
+ ```python
42
+ async with Shadey("sk_your_api_key") as client:
43
+ async with await client.session() as session:
44
+ await session.goto("https://nowsecure.nl")
45
+
46
+ # Human-like interactions to build trust
47
+ await session.idle(duration_ms=3000)
48
+ await session.scroll(400)
49
+ await session.click(500, 300)
50
+ await session.type_text("hello world", context="search")
51
+
52
+ # Then extract what you need
53
+ result = await session.extract(".result-title", all=True)
54
+ ```
55
+
56
+ ## With Playwright (CDP)
57
+
58
+ ```python
59
+ async with Shadey("sk_your_api_key") as client:
60
+ async with await client.session() as session:
61
+ # Full browser control via CDP
62
+ from playwright.async_api import async_playwright
63
+ async with async_playwright() as p:
64
+ browser = await p.chromium.connect_over_cdp(session.playwright_url)
65
+ page = browser.contexts[0].pages[0]
66
+ await page.goto("https://nowsecure.nl")
67
+ print(await page.title())
68
+ ```
69
+
70
+ ## API
71
+
72
+ ### `Shadey(api_key, base_url="https://api.shadey.dev")`
73
+
74
+ Create a client. Use as async context manager or call `.close()` manually.
75
+
76
+ ### `client.session(stealth="full", proxy=None, profile=None)`
77
+
78
+ Create a stealth browser session. Returns a `ShadeySession`.
79
+
80
+ ### REST Actions
81
+
82
+ ### `session.goto(url, wait_ms=3000)`
83
+
84
+ Navigate to a URL. Waits for page load + extra `wait_ms`.
85
+
86
+ ### `session.screenshot()`
87
+
88
+ Returns a base64 JPEG data URI of the current page.
89
+
90
+ ### `session.extract(selector, attribute="textContent", all=False)`
91
+
92
+ Extract data from the page. Set `all=True` to get all matching elements.
93
+
94
+ ### `session.evaluate(expression)`
95
+
96
+ Run JavaScript and return the result.
97
+
98
+ ### `session.content()`
99
+
100
+ Get the full page HTML.
101
+
102
+ ### Behavioral Actions
103
+
104
+ ### `session.click(x, y, width=50)`
105
+
106
+ Move mouse and click at coordinates with Fitts' law timing and corrective submovements.
107
+
108
+ ### `session.type_text(text, context="form")`
109
+
110
+ Type text with QWERTY bigram-modeled keystroke timing. Context: `"form"`, `"search"`, `"url"`.
111
+
112
+ ### `session.scroll(distance)`
113
+
114
+ Scroll with momentum and inertia. Positive = down, negative = up.
115
+
116
+ ### `session.dwell(context="content")`
117
+
118
+ Wait a human-realistic amount of time. Context: `"content"`, `"search"`, `"form"`.
119
+
120
+ ### `session.idle(duration_ms=5000)`
121
+
122
+ Simulate idle browsing — mouse movements, scrolls, pauses driven by HMM behavioral model.
123
+
124
+ ### CDP Access
125
+
126
+ ### `session.playwright_url` / `session.puppeteer_url`
127
+
128
+ CDP WebSocket URL for direct browser control.
129
+
130
+ ### `session.close()`
131
+
132
+ Destroy the session.
133
+
134
+ ### `client.usage()`
135
+
136
+ Get plan usage stats.
137
+
138
+ ### `client.health()`
139
+
140
+ Check API health.
shadey-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
shadey-0.1.0/setup.py ADDED
@@ -0,0 +1,25 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="shadey",
5
+ version="0.1.0",
6
+ description="Stealth browser sessions for AI agents",
7
+ long_description=open("README.md").read(),
8
+ long_description_content_type="text/markdown",
9
+ author="Shadey",
10
+ url="https://shadey.dev",
11
+ packages=find_packages(),
12
+ python_requires=">=3.8",
13
+ install_requires=[
14
+ "aiohttp>=3.8",
15
+ ],
16
+ extras_require={
17
+ "playwright": ["playwright>=1.40"],
18
+ },
19
+ classifiers=[
20
+ "Development Status :: 3 - Alpha",
21
+ "Intended Audience :: Developers",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Programming Language :: Python :: 3",
24
+ ],
25
+ )
@@ -0,0 +1,5 @@
1
+ from .client import Shadey, ShadeyError
2
+ from .session import ShadeySession
3
+
4
+ __version__ = "0.1.0"
5
+ __all__ = ["Shadey", "ShadeyError", "ShadeySession"]
@@ -0,0 +1,85 @@
1
+ import aiohttp
2
+ from typing import Optional, List
3
+ from .session import ShadeySession
4
+
5
+
6
+ class Shadey:
7
+ def __init__(self, api_key: str, base_url: str = "https://api.shadey.dev"):
8
+ self.api_key = api_key
9
+ self.base_url = base_url.rstrip("/")
10
+ self._http: Optional[aiohttp.ClientSession] = None
11
+
12
+ async def __aenter__(self):
13
+ self._http = aiohttp.ClientSession(
14
+ headers={"Authorization": f"Bearer {self.api_key}"}
15
+ )
16
+ return self
17
+
18
+ async def __aexit__(self, *args):
19
+ if self._http:
20
+ await self._http.close()
21
+
22
+ def _ensure_http(self):
23
+ if not self._http:
24
+ self._http = aiohttp.ClientSession(
25
+ headers={"Authorization": f"Bearer {self.api_key}"}
26
+ )
27
+
28
+ async def _request(self, method: str, path: str, **kwargs) -> dict:
29
+ self._ensure_http()
30
+ async with self._http.request(method, f"{self.base_url}{path}", **kwargs) as resp:
31
+ if resp.status == 204:
32
+ return {}
33
+ data = await resp.json()
34
+ if resp.status >= 400:
35
+ raise ShadeyError(resp.status, data.get("error", str(data)))
36
+ return data
37
+
38
+ async def session(
39
+ self,
40
+ stealth: str = "full",
41
+ proxy: Optional[str] = None,
42
+ profile: Optional[str] = None,
43
+ ) -> "ShadeySession":
44
+ payload = {"stealth": stealth}
45
+ if proxy:
46
+ payload["proxy"] = proxy
47
+ if profile:
48
+ payload["profile_name"] = profile
49
+
50
+ data = await self._request("POST", "/v1/sessions", json=payload)
51
+ return ShadeySession(
52
+ id=data["id"],
53
+ cdp_url=data["cdp_url"],
54
+ client=self,
55
+ )
56
+
57
+ async def sessions(self) -> List[dict]:
58
+ return await self._request("GET", "/v1/sessions")
59
+
60
+ async def health(self) -> dict:
61
+ self._ensure_http()
62
+ async with self._http.get(f"{self.base_url}/health") as resp:
63
+ return await resp.json()
64
+
65
+ async def usage(self) -> dict:
66
+ return await self._request("GET", "/v1/usage")
67
+
68
+ async def profiles(self) -> list:
69
+ data = await self._request("GET", "/v1/profiles")
70
+ return data.get("profiles", [])
71
+
72
+ async def delete_profile(self, name: str):
73
+ await self._request("DELETE", f"/v1/profiles/{name}")
74
+
75
+ async def close(self):
76
+ if self._http:
77
+ await self._http.close()
78
+ self._http = None
79
+
80
+
81
+ class ShadeyError(Exception):
82
+ def __init__(self, status: int, message: str):
83
+ self.status = status
84
+ self.message = message
85
+ super().__init__(f"[{status}] {message}")
@@ -0,0 +1,116 @@
1
+ from __future__ import annotations
2
+ from typing import Optional, TYPE_CHECKING
3
+
4
+ if TYPE_CHECKING:
5
+ from .client import Shadey
6
+
7
+
8
+ class ShadeySession:
9
+ def __init__(self, id: str, cdp_url: str, client: "Shadey"):
10
+ self.id = id
11
+ self.cdp_url = cdp_url
12
+ self._client = client
13
+ self._closed = False
14
+
15
+ async def close(self, save: bool = False):
16
+ if not self._closed:
17
+ path = f"/v1/sessions/{self.id}"
18
+ if save:
19
+ path += "?save=true"
20
+ await self._client._request("DELETE", path)
21
+ self._closed = True
22
+
23
+ async def __aenter__(self):
24
+ return self
25
+
26
+ async def __aexit__(self, *args):
27
+ await self.close()
28
+
29
+ # -- behavioral actions --
30
+
31
+ async def click(self, x: float, y: float, width: float = 50.0) -> dict:
32
+ return await self._behave({
33
+ "action": "move_and_click",
34
+ "target_x": x,
35
+ "target_y": y,
36
+ "target_width": width,
37
+ })
38
+
39
+ async def type_text(self, text: str, context: str = "form") -> dict:
40
+ return await self._behave({
41
+ "action": "type",
42
+ "text": text,
43
+ "typing_context": context,
44
+ })
45
+
46
+ async def scroll(self, distance: float) -> dict:
47
+ return await self._behave({
48
+ "action": "scroll",
49
+ "distance": distance,
50
+ })
51
+
52
+ async def dwell(self, context: str = "content") -> dict:
53
+ return await self._behave({
54
+ "action": "dwell",
55
+ "page_context": context,
56
+ })
57
+
58
+ async def idle(self, duration_ms: int = 5000) -> dict:
59
+ return await self._behave({
60
+ "action": "idle_browse",
61
+ "duration_ms": duration_ms,
62
+ })
63
+
64
+ async def _behave(self, payload: dict) -> dict:
65
+ return await self._client._request(
66
+ "POST", f"/v1/sessions/{self.id}/behave", json=payload
67
+ )
68
+
69
+ # -- REST actions --
70
+
71
+ async def goto(self, url: str, wait_ms: int = 3000) -> dict:
72
+ return await self._client._request(
73
+ "POST", f"/v1/sessions/{self.id}/navigate",
74
+ json={"url": url, "wait_ms": wait_ms},
75
+ )
76
+
77
+ async def screenshot(self) -> str:
78
+ data = await self._client._request(
79
+ "POST", f"/v1/sessions/{self.id}/screenshot"
80
+ )
81
+ return data["data"]
82
+
83
+ async def extract(self, selector: str, attribute: str = "textContent", all: bool = False) -> dict:
84
+ return await self._client._request(
85
+ "POST", f"/v1/sessions/{self.id}/extract",
86
+ json={"selector": selector, "attribute": attribute, "all": all},
87
+ )
88
+
89
+ async def evaluate(self, expression: str) -> dict:
90
+ return await self._client._request(
91
+ "POST", f"/v1/sessions/{self.id}/evaluate",
92
+ json={"expression": expression},
93
+ )
94
+
95
+ async def content(self) -> dict:
96
+ return await self._client._request(
97
+ "POST", f"/v1/sessions/{self.id}/content"
98
+ )
99
+
100
+ # -- CDP access --
101
+
102
+ @property
103
+ def playwright_url(self) -> str:
104
+ return self.cdp_url
105
+
106
+ @property
107
+ def puppeteer_url(self) -> str:
108
+ return self.cdp_url
109
+
110
+ @property
111
+ def is_active(self) -> bool:
112
+ return not self._closed
113
+
114
+ def __repr__(self):
115
+ status = "active" if not self._closed else "closed"
116
+ return f"ShadeySession(id={self.id!r}, status={status!r})"
@@ -0,0 +1,165 @@
1
+ Metadata-Version: 2.4
2
+ Name: shadey
3
+ Version: 0.1.0
4
+ Summary: Stealth browser sessions for AI agents
5
+ Home-page: https://shadey.dev
6
+ Author: Shadey
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: aiohttp>=3.8
14
+ Provides-Extra: playwright
15
+ Requires-Dist: playwright>=1.40; extra == "playwright"
16
+ Dynamic: author
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: home-page
21
+ Dynamic: provides-extra
22
+ Dynamic: requires-dist
23
+ Dynamic: requires-python
24
+ Dynamic: summary
25
+
26
+ # shadey
27
+
28
+ Stealth browser sessions for AI agents. Pass Cloudflare, DataDome, PerimeterX, and Kasada from any IP.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install shadey
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ```python
39
+ import asyncio
40
+ from shadey import Shadey
41
+
42
+ async def main():
43
+ async with Shadey("sk_your_api_key") as client:
44
+ async with await client.session() as session:
45
+ # Navigate and extract — no CDP/Playwright needed
46
+ await session.goto("https://example.com")
47
+ title = await session.evaluate("document.title")
48
+ print(title["result"])
49
+
50
+ # Extract text from elements
51
+ headings = await session.extract("h1", all=True)
52
+ print(headings["data"])
53
+
54
+ # Get full page HTML
55
+ page = await session.content()
56
+ print(f"{page['length']} bytes")
57
+
58
+ # Screenshot
59
+ img = await session.screenshot() # base64 jpeg
60
+
61
+ asyncio.run(main())
62
+ ```
63
+
64
+ ## With Behavioral Actions
65
+
66
+ ```python
67
+ async with Shadey("sk_your_api_key") as client:
68
+ async with await client.session() as session:
69
+ await session.goto("https://nowsecure.nl")
70
+
71
+ # Human-like interactions to build trust
72
+ await session.idle(duration_ms=3000)
73
+ await session.scroll(400)
74
+ await session.click(500, 300)
75
+ await session.type_text("hello world", context="search")
76
+
77
+ # Then extract what you need
78
+ result = await session.extract(".result-title", all=True)
79
+ ```
80
+
81
+ ## With Playwright (CDP)
82
+
83
+ ```python
84
+ async with Shadey("sk_your_api_key") as client:
85
+ async with await client.session() as session:
86
+ # Full browser control via CDP
87
+ from playwright.async_api import async_playwright
88
+ async with async_playwright() as p:
89
+ browser = await p.chromium.connect_over_cdp(session.playwright_url)
90
+ page = browser.contexts[0].pages[0]
91
+ await page.goto("https://nowsecure.nl")
92
+ print(await page.title())
93
+ ```
94
+
95
+ ## API
96
+
97
+ ### `Shadey(api_key, base_url="https://api.shadey.dev")`
98
+
99
+ Create a client. Use as async context manager or call `.close()` manually.
100
+
101
+ ### `client.session(stealth="full", proxy=None, profile=None)`
102
+
103
+ Create a stealth browser session. Returns a `ShadeySession`.
104
+
105
+ ### REST Actions
106
+
107
+ ### `session.goto(url, wait_ms=3000)`
108
+
109
+ Navigate to a URL. Waits for page load + extra `wait_ms`.
110
+
111
+ ### `session.screenshot()`
112
+
113
+ Returns a base64 JPEG data URI of the current page.
114
+
115
+ ### `session.extract(selector, attribute="textContent", all=False)`
116
+
117
+ Extract data from the page. Set `all=True` to get all matching elements.
118
+
119
+ ### `session.evaluate(expression)`
120
+
121
+ Run JavaScript and return the result.
122
+
123
+ ### `session.content()`
124
+
125
+ Get the full page HTML.
126
+
127
+ ### Behavioral Actions
128
+
129
+ ### `session.click(x, y, width=50)`
130
+
131
+ Move mouse and click at coordinates with Fitts' law timing and corrective submovements.
132
+
133
+ ### `session.type_text(text, context="form")`
134
+
135
+ Type text with QWERTY bigram-modeled keystroke timing. Context: `"form"`, `"search"`, `"url"`.
136
+
137
+ ### `session.scroll(distance)`
138
+
139
+ Scroll with momentum and inertia. Positive = down, negative = up.
140
+
141
+ ### `session.dwell(context="content")`
142
+
143
+ Wait a human-realistic amount of time. Context: `"content"`, `"search"`, `"form"`.
144
+
145
+ ### `session.idle(duration_ms=5000)`
146
+
147
+ Simulate idle browsing — mouse movements, scrolls, pauses driven by HMM behavioral model.
148
+
149
+ ### CDP Access
150
+
151
+ ### `session.playwright_url` / `session.puppeteer_url`
152
+
153
+ CDP WebSocket URL for direct browser control.
154
+
155
+ ### `session.close()`
156
+
157
+ Destroy the session.
158
+
159
+ ### `client.usage()`
160
+
161
+ Get plan usage stats.
162
+
163
+ ### `client.health()`
164
+
165
+ Check API health.
@@ -0,0 +1,10 @@
1
+ README.md
2
+ setup.py
3
+ shadey/__init__.py
4
+ shadey/client.py
5
+ shadey/session.py
6
+ shadey.egg-info/PKG-INFO
7
+ shadey.egg-info/SOURCES.txt
8
+ shadey.egg-info/dependency_links.txt
9
+ shadey.egg-info/requires.txt
10
+ shadey.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ aiohttp>=3.8
2
+
3
+ [playwright]
4
+ playwright>=1.40
@@ -0,0 +1 @@
1
+ shadey