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.
- stealthhub-0.1.0/.gitignore +20 -0
- stealthhub-0.1.0/PKG-INFO +164 -0
- stealthhub-0.1.0/README.md +153 -0
- stealthhub-0.1.0/pyproject.toml +21 -0
- stealthhub-0.1.0/stealthhub/__init__.py +4 -0
- stealthhub-0.1.0/stealthhub/client.py +193 -0
|
@@ -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,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}")
|