stealthhub 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.
@@ -0,0 +1,20 @@
1
+ # Ignore environment variables
2
+ .env
3
+ .env.prod
4
+
5
+ # Ignore binaries
6
+ server
7
+ server.exe
8
+ worker
9
+ worker.exe
10
+
11
+ # Ignore node_modules in scripts
12
+ scripts/node_modules/
13
+
14
+ scripts/screenshot_test.png
15
+
16
+ sdks/nodejs/node_modules/
17
+ tests/fingerprint-test/node_modules
18
+
19
+ tests/e2e/node_modules/*
20
+ tests/e2e/node_modules
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.5
2
+ Name: stealthhub
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for StealthHub - Headless Browser as a Service
5
+ Author: StealthHub
6
+ License-Expression: MIT
7
+ Keywords: antidetect,headless-browser,scraper,stealthhub
8
+ Requires-Python: >=3.9
9
+ Requires-Dist: requests>=2.28.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ # stealthhub — Python SDK
13
+
14
+ The official Python SDK for [StealthHub](https://stealthhub.io) — Headless Browser as a Service.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pip install stealthhub
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```python
25
+ from stealthhub import StealthHub
26
+
27
+ hub = StealthHub(api_key="sk_your_api_key")
28
+
29
+ # Create a session
30
+ session = hub.create_session(os_type="windows")
31
+ cdp_url = hub.get_cdp_url(session["id"])
32
+ print(f"CDP URL: {cdp_url}")
33
+
34
+ # Use with Playwright
35
+ from playwright.sync_api import sync_playwright
36
+ with sync_playwright() as p:
37
+ browser = p.chromium.connect_over_cdp(cdp_url)
38
+ page = browser.new_page()
39
+ page.goto("https://example.com")
40
+ print(page.title())
41
+
42
+ # Kill session
43
+ hub.kill_session(session["id"])
44
+ ```
45
+
46
+ ## Context Manager
47
+
48
+ ```python
49
+ with StealthHub(api_key="sk_your_api_key") as hub:
50
+ session = hub.create_session()
51
+ # ... use session ...
52
+ hub.kill_session(session["id"])
53
+ # Connection is automatically closed
54
+ ```
55
+
56
+ ## API Reference
57
+
58
+ ### Sessions
59
+
60
+ ```python
61
+ hub.create_session(profile_id=None, os_type="windows", proxy_url=None, team_id=None, record=False)
62
+ hub.kill_session(session_id)
63
+ hub.list_sessions()
64
+ hub.get_cdp_url(session_id)
65
+ ```
66
+
67
+ ### Fetch & Scrape
68
+
69
+ ```python
70
+ # Lightweight HTTP fetch (no browser, fast, 1 credit)
71
+ hub.fetch(url, format="html", proxy_url=None, headers=None, extract=None)
72
+
73
+ # Full browser render (JS/SPA support, credits per minute)
74
+ hub.scrape(url, format="html", wait_for=None, timeout=30000, screenshot=False, proxy_url=None, extract=None)
75
+ ```
76
+
77
+ **Structured data extraction:**
78
+ ```python
79
+ result = hub.fetch(
80
+ url="https://shopee.vn/product/123",
81
+ extract={
82
+ "title": "h1",
83
+ "price": ".price",
84
+ "images": "img[src]"
85
+ }
86
+ )
87
+ # result["structured_data"] = {"title": "iPhone 15", "price": "29.990.000₫", "images": [...]}
88
+ ```
89
+
90
+ ### Profiles
91
+
92
+ ```python
93
+ hub.create_profile(name, os_type="windows")
94
+ hub.list_profiles()
95
+ hub.delete_profile(profile_id)
96
+ ```
97
+
98
+ ### Billing
99
+
100
+ ```python
101
+ hub.get_balance()
102
+ hub.get_history(page=1, limit=20)
103
+ hub.create_deposit(amount) # VietQR deposit, amount in VND (min 10,000)
104
+ ```
105
+
106
+ ### CAPTCHA
107
+
108
+ ```python
109
+ hub.solve_captcha(session_id, captcha_type, site_key, page_url)
110
+ ```
111
+
112
+ ### OTP
113
+
114
+ ```python
115
+ hub.request_otp(service, country="vn")
116
+ hub.list_otps()
117
+ ```
118
+
119
+ ### Webhooks
120
+
121
+ ```python
122
+ hub.register_webhook(url, secret, events=None)
123
+ hub.list_webhooks()
124
+ hub.delete_webhook(webhook_id)
125
+ ```
126
+
127
+ ### Teams
128
+
129
+ ```python
130
+ hub.get_teams()
131
+ hub.create_team(name)
132
+ hub.add_team_member(team_id, email, role="member")
133
+ ```
134
+
135
+ ### API Keys
136
+
137
+ ```python
138
+ hub.create_api_key(name)
139
+ hub.list_api_keys()
140
+ hub.revoke_api_key(key_id)
141
+ ```
142
+
143
+ ### Credentials
144
+
145
+ ```python
146
+ hub.create_credential(name, username, password, notes=None)
147
+ hub.list_credentials()
148
+ hub.delete_credential(credential_id)
149
+ ```
150
+
151
+ ## Environment Variables
152
+
153
+ | Variable | Description |
154
+ |----------|-------------|
155
+ | `STEALTHHUB_API_KEY` | API key (alternative to constructor argument) |
156
+
157
+ ## Requirements
158
+
159
+ - Python 3.10+
160
+ - `requests` library
161
+
162
+ ## License
163
+
164
+ MIT
@@ -0,0 +1,153 @@
1
+ # stealthhub — Python SDK
2
+
3
+ The official Python SDK for [StealthHub](https://stealthhub.io) — Headless Browser as a Service.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install stealthhub
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from stealthhub import StealthHub
15
+
16
+ hub = StealthHub(api_key="sk_your_api_key")
17
+
18
+ # Create a session
19
+ session = hub.create_session(os_type="windows")
20
+ cdp_url = hub.get_cdp_url(session["id"])
21
+ print(f"CDP URL: {cdp_url}")
22
+
23
+ # Use with Playwright
24
+ from playwright.sync_api import sync_playwright
25
+ with sync_playwright() as p:
26
+ browser = p.chromium.connect_over_cdp(cdp_url)
27
+ page = browser.new_page()
28
+ page.goto("https://example.com")
29
+ print(page.title())
30
+
31
+ # Kill session
32
+ hub.kill_session(session["id"])
33
+ ```
34
+
35
+ ## Context Manager
36
+
37
+ ```python
38
+ with StealthHub(api_key="sk_your_api_key") as hub:
39
+ session = hub.create_session()
40
+ # ... use session ...
41
+ hub.kill_session(session["id"])
42
+ # Connection is automatically closed
43
+ ```
44
+
45
+ ## API Reference
46
+
47
+ ### Sessions
48
+
49
+ ```python
50
+ hub.create_session(profile_id=None, os_type="windows", proxy_url=None, team_id=None, record=False)
51
+ hub.kill_session(session_id)
52
+ hub.list_sessions()
53
+ hub.get_cdp_url(session_id)
54
+ ```
55
+
56
+ ### Fetch & Scrape
57
+
58
+ ```python
59
+ # Lightweight HTTP fetch (no browser, fast, 1 credit)
60
+ hub.fetch(url, format="html", proxy_url=None, headers=None, extract=None)
61
+
62
+ # Full browser render (JS/SPA support, credits per minute)
63
+ hub.scrape(url, format="html", wait_for=None, timeout=30000, screenshot=False, proxy_url=None, extract=None)
64
+ ```
65
+
66
+ **Structured data extraction:**
67
+ ```python
68
+ result = hub.fetch(
69
+ url="https://shopee.vn/product/123",
70
+ extract={
71
+ "title": "h1",
72
+ "price": ".price",
73
+ "images": "img[src]"
74
+ }
75
+ )
76
+ # result["structured_data"] = {"title": "iPhone 15", "price": "29.990.000₫", "images": [...]}
77
+ ```
78
+
79
+ ### Profiles
80
+
81
+ ```python
82
+ hub.create_profile(name, os_type="windows")
83
+ hub.list_profiles()
84
+ hub.delete_profile(profile_id)
85
+ ```
86
+
87
+ ### Billing
88
+
89
+ ```python
90
+ hub.get_balance()
91
+ hub.get_history(page=1, limit=20)
92
+ hub.create_deposit(amount) # VietQR deposit, amount in VND (min 10,000)
93
+ ```
94
+
95
+ ### CAPTCHA
96
+
97
+ ```python
98
+ hub.solve_captcha(session_id, captcha_type, site_key, page_url)
99
+ ```
100
+
101
+ ### OTP
102
+
103
+ ```python
104
+ hub.request_otp(service, country="vn")
105
+ hub.list_otps()
106
+ ```
107
+
108
+ ### Webhooks
109
+
110
+ ```python
111
+ hub.register_webhook(url, secret, events=None)
112
+ hub.list_webhooks()
113
+ hub.delete_webhook(webhook_id)
114
+ ```
115
+
116
+ ### Teams
117
+
118
+ ```python
119
+ hub.get_teams()
120
+ hub.create_team(name)
121
+ hub.add_team_member(team_id, email, role="member")
122
+ ```
123
+
124
+ ### API Keys
125
+
126
+ ```python
127
+ hub.create_api_key(name)
128
+ hub.list_api_keys()
129
+ hub.revoke_api_key(key_id)
130
+ ```
131
+
132
+ ### Credentials
133
+
134
+ ```python
135
+ hub.create_credential(name, username, password, notes=None)
136
+ hub.list_credentials()
137
+ hub.delete_credential(credential_id)
138
+ ```
139
+
140
+ ## Environment Variables
141
+
142
+ | Variable | Description |
143
+ |----------|-------------|
144
+ | `STEALTHHUB_API_KEY` | API key (alternative to constructor argument) |
145
+
146
+ ## Requirements
147
+
148
+ - Python 3.10+
149
+ - `requests` library
150
+
151
+ ## License
152
+
153
+ MIT
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "stealthhub"
7
+ version = "0.1.0"
8
+ description = "Official Python SDK for StealthHub - Headless Browser as a Service"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ dependencies = [
13
+ "requests>=2.28.0"
14
+ ]
15
+ authors = [
16
+ { name="StealthHub" }
17
+ ]
18
+ keywords = ["stealthhub", "headless-browser", "scraper", "antidetect"]
19
+
20
+ [tool.hatch.build.targets.wheel]
21
+ packages = ["stealthhub"]
@@ -0,0 +1,4 @@
1
+ from .client import StealthHub
2
+
3
+ __version__ = "0.1.0"
4
+ __all__ = ["StealthHub"]
@@ -0,0 +1,193 @@
1
+ import os
2
+ from typing import Any, Optional
3
+
4
+ import requests
5
+
6
+
7
+ class StealthHub:
8
+ """StealthHub Python SDK — Full API client for Headless Browser as a Service."""
9
+
10
+ def __init__(self, api_key: str | None = None, base_url: str = "http://localhost:8080/v1"):
11
+ self.api_key = api_key or os.environ.get("STEALTHHUB_API_KEY")
12
+ if not self.api_key:
13
+ raise ValueError("API key must be provided")
14
+ self.base_url = base_url
15
+ self.session = requests.Session()
16
+ self.session.headers.update({
17
+ "Authorization": f"Bearer {self.api_key}",
18
+ "Content-Type": "application/json",
19
+ })
20
+
21
+ def __enter__(self):
22
+ return self
23
+
24
+ def __exit__(self, *args):
25
+ self.session.close()
26
+
27
+ def _request(self, method: str, path: str, **kwargs) -> Any:
28
+ res = self.session.request(method, f"{self.base_url}{path}", **kwargs)
29
+ res.raise_for_status()
30
+ data = res.json()
31
+ return data.get("data", data)
32
+
33
+ # --- Sessions ---
34
+
35
+ def create_session(
36
+ self,
37
+ profile_id: str | None = None,
38
+ os_type: str = "windows",
39
+ proxy_url: str | None = None,
40
+ team_id: str | None = None,
41
+ record: bool = False,
42
+ ) -> dict[str, Any]:
43
+ """Creates a new headless browser session."""
44
+ payload: dict[str, Any] = {"os": os_type, "record": record}
45
+ if profile_id:
46
+ payload["profile_id"] = profile_id
47
+ if proxy_url:
48
+ payload["proxy_url"] = proxy_url
49
+ if team_id:
50
+ payload["team_id"] = team_id
51
+ return self._request("POST", "/sessions", json=payload)
52
+
53
+ def kill_session(self, session_id: str) -> bool:
54
+ """Kills an active session."""
55
+ self._request("DELETE", f"/sessions/{session_id}")
56
+ return True
57
+
58
+ def list_sessions(self) -> list[dict[str, Any]]:
59
+ """Lists active sessions."""
60
+ return self._request("GET", "/sessions/list")
61
+
62
+ def get_cdp_url(self, session_id: str) -> str:
63
+ """Helper to construct CDP URL for Playwright/Puppeteer."""
64
+ ws_base = self.base_url.replace("http", "ws", 1)
65
+ return f"{ws_base}/cdp/{session_id}?api_key={self.api_key}"
66
+
67
+ # --- Fetch & Scrape ---
68
+
69
+ def fetch(
70
+ self,
71
+ url: str,
72
+ format: str = "html",
73
+ proxy_url: str | None = None,
74
+ headers: dict[str, str] | None = None,
75
+ extract: dict[str, str] | None = None,
76
+ ) -> dict[str, Any]:
77
+ """Lightweight HTTP fetch (no browser). Fast and cheap."""
78
+ payload: dict[str, Any] = {"url": url, "format": format}
79
+ if proxy_url:
80
+ payload["proxy_url"] = proxy_url
81
+ if headers:
82
+ payload["headers"] = headers
83
+ if extract:
84
+ payload["extract"] = extract
85
+ return self._request("POST", "/fetch", json=payload)
86
+
87
+ def scrape(
88
+ self,
89
+ url: str,
90
+ format: str = "html",
91
+ wait_for: str | None = None,
92
+ timeout: int = 30000,
93
+ screenshot: bool = False,
94
+ proxy_url: str | None = None,
95
+ extract: dict[str, str] | None = None,
96
+ ) -> dict[str, Any]:
97
+ """Full browser render + extract. Accurate for SPAs and JS-heavy pages."""
98
+ payload: dict[str, Any] = {"url": url, "format": format, "timeout": timeout, "screenshot": screenshot}
99
+ if wait_for:
100
+ payload["wait_for"] = wait_for
101
+ if proxy_url:
102
+ payload["proxy_url"] = proxy_url
103
+ if extract:
104
+ payload["extract"] = extract
105
+ return self._request("POST", "/scrape", json=payload)
106
+
107
+ # --- Profiles ---
108
+
109
+ def create_profile(self, name: str, os_type: str = "windows") -> dict[str, Any]:
110
+ return self._request("POST", "/profiles/create", json={"name": name, "os": os_type})
111
+
112
+ def list_profiles(self) -> list[dict[str, Any]]:
113
+ return self._request("GET", "/profiles")
114
+
115
+ def delete_profile(self, profile_id: str) -> dict[str, Any]:
116
+ return self._request("POST", "/profiles/delete", json={"id": profile_id})
117
+
118
+ # --- Billing ---
119
+
120
+ def get_balance(self) -> dict[str, Any]:
121
+ return self._request("GET", "/billing/balance")
122
+
123
+ def get_history(self, page: int = 1, limit: int = 20) -> list[dict[str, Any]]:
124
+ return self._request("GET", f"/billing/history?page={page}&limit={limit}")
125
+
126
+ def create_deposit(self, amount: int) -> dict[str, Any]:
127
+ """Create VietQR deposit order. Amount in VND (min 10,000)."""
128
+ return self._request("POST", "/billing/deposit", json={"amount": amount})
129
+
130
+ # --- CAPTCHA ---
131
+
132
+ def solve_captcha(self, session_id: str, captcha_type: str, site_key: str, page_url: str) -> dict[str, Any]:
133
+ return self._request("POST", "/captcha/solve", json={
134
+ "session_id": session_id,
135
+ "type": captcha_type,
136
+ "site_key": site_key,
137
+ "page_url": page_url,
138
+ })
139
+
140
+ # --- OTP ---
141
+
142
+ def request_otp(self, service: str, country: str = "vn") -> dict[str, Any]:
143
+ return self._request("POST", "/otp/request", json={"service": service, "country": country})
144
+
145
+ def list_otps(self) -> list[dict[str, Any]]:
146
+ return self._request("GET", "/otp/list")
147
+
148
+ # --- Webhooks ---
149
+
150
+ def register_webhook(self, url: str, secret: str, events: list[str] | None = None) -> dict[str, Any]:
151
+ return self._request("POST", "/webhooks", json={"url": url, "secret": secret, "events": events or ["*"]})
152
+
153
+ def list_webhooks(self) -> list[dict[str, Any]]:
154
+ return self._request("GET", "/webhooks")
155
+
156
+ def delete_webhook(self, webhook_id: str) -> dict[str, Any]:
157
+ return self._request("DELETE", f"/webhooks/{webhook_id}")
158
+
159
+ # --- Teams ---
160
+
161
+ def get_teams(self) -> list[dict[str, Any]]:
162
+ return self._request("GET", "/teams")
163
+
164
+ def create_team(self, name: str) -> dict[str, Any]:
165
+ return self._request("POST", "/teams/create", json={"name": name})
166
+
167
+ def add_team_member(self, team_id: str, email: str, role: str = "member") -> dict[str, Any]:
168
+ return self._request("POST", "/teams/add-member", json={"team_id": team_id, "email": email, "role": role})
169
+
170
+ # --- API Keys ---
171
+
172
+ def create_api_key(self, name: str) -> dict[str, Any]:
173
+ return self._request("POST", "/api-keys", json={"name": name})
174
+
175
+ def list_api_keys(self) -> list[dict[str, Any]]:
176
+ return self._request("GET", "/api-keys")
177
+
178
+ def revoke_api_key(self, key_id: str) -> dict[str, Any]:
179
+ return self._request("DELETE", f"/api-keys/{key_id}")
180
+
181
+ # --- Credentials ---
182
+
183
+ def create_credential(self, name: str, username: str, password: str, notes: str | None = None) -> dict[str, Any]:
184
+ payload: dict[str, Any] = {"name": name, "username": username, "password": password}
185
+ if notes:
186
+ payload["notes"] = notes
187
+ return self._request("POST", "/credentials", json=payload)
188
+
189
+ def list_credentials(self) -> list[dict[str, Any]]:
190
+ return self._request("GET", "/credentials")
191
+
192
+ def delete_credential(self, credential_id: str) -> dict[str, Any]:
193
+ return self._request("DELETE", f"/credentials/{credential_id}")