shardx 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,48 @@
1
+ # ===== Logs =====
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ # ===== Node / Vite =====
11
+ node_modules
12
+ dist
13
+ dist-ssr
14
+ *.local
15
+ *.tsbuildinfo
16
+ .cache/
17
+ .parcel-cache/
18
+ coverage/
19
+
20
+ # ===== Rust / Tauri build artifacts =====
21
+ src-tauri/target/
22
+ src-tauri/Cargo.lock.bak
23
+ src-tauri/.cargo-lock
24
+ src-tauri/gen/
25
+ # Per-platform release bundles — built by the release workflow,
26
+ # uploaded to GitHub Releases, never committed.
27
+ src-tauri/target/release/bundle/
28
+
29
+ # ===== Editor / OS junk =====
30
+ .vscode/*
31
+ !.vscode/extensions.json
32
+ !.vscode/settings.json
33
+ .idea/
34
+ *.suo
35
+ *.ntvs*
36
+ *.njsproj
37
+ *.sln
38
+ *.sw?
39
+ .DS_Store
40
+ Thumbs.db
41
+
42
+ # ===== Local secrets / env =====
43
+ .env
44
+ .env.*
45
+ !.env.example
46
+
47
+ # ===== MCP server local install (downloaded by the user post-install) =====
48
+ mcp/node_modules/
shardx-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,255 @@
1
+ Metadata-Version: 2.4
2
+ Name: shardx
3
+ Version: 0.1.0
4
+ Summary: Self-contained Python SDK for the ShardX anti-detect browser — downloads the engine + Widevine + fingerprint library on first use, launches isolated profiles.
5
+ Project-URL: Homepage, https://github.com/ProxyShard/ShardBrowser
6
+ Project-URL: Documentation, https://docs.proxyshard.com
7
+ Project-URL: Issues, https://github.com/ProxyShard/ShardBrowser/issues
8
+ Author-email: ProxyShard <support@proxyshard.com>
9
+ License: MIT
10
+ Keywords: anti-detect,automation,browser,chromium,fingerprint,multi-accounting,scraping
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.9
21
+ Requires-Dist: httpx[socks]>=0.27
22
+ Requires-Dist: patchright>=1.49
23
+ Description-Content-Type: text/markdown
24
+
25
+ # shardx (Python)
26
+
27
+ Self-contained Python SDK for the **ShardX anti-detect browser** by the
28
+ [ProxyShard](https://proxyshard.com) team.
29
+
30
+ This package does **not** depend on the desktop launcher. On first use
31
+ it downloads the patched Chromium 148 engine, Widevine CDM, and the
32
+ 170-profile fingerprint library from our CDN into a local cache, then
33
+ launches isolated browser sessions on demand.
34
+
35
+ Driven by [patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright-python)
36
+ (stealth-patched Playwright) — `sdk.session()` returns a ready-to-use
37
+ `Browser` instance, no manual `connect_over_cdp` plumbing.
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pip install shardx
43
+ ```
44
+
45
+ Supported hosts: **macOS arm64**, **Windows x64**, **Linux x64**.
46
+
47
+ ## Quick start
48
+
49
+ ```python
50
+ import asyncio
51
+ from shardx import ShardX
52
+
53
+ async def main():
54
+ sdk = ShardX()
55
+ # Engine + Widevine + fingerprint library auto-download from CDN on
56
+ # the first `session`/`launch`/`list_profiles` call (~170 MB once,
57
+ # etag-cached afterward). No separate install step.
58
+
59
+ # Launch + drive in one call. Yields a patchright `Browser`.
60
+ async with sdk.session("win-rtx4060", proxy="socks5://user:pass@host:port") as browser:
61
+ ctx = browser.contexts[0]
62
+ page = await ctx.new_page()
63
+ await page.goto("https://browserleaks.com/quic")
64
+ print(await page.title())
65
+
66
+ # Inspect what the SDK resolved before launch:
67
+ sess = browser._shardx
68
+ print(sess.geo) # GeoInfo(...) from ip-api / ipapi.co
69
+ print(sess.proxy_udp_ms, # UDP RTT in ms or None
70
+ sess.quic_enabled, # bool
71
+ sess.webrtc_mode) # "auto" | "tcp_only" | "block"
72
+ # browser + udd shut down cleanly on exit
73
+
74
+ asyncio.run(main())
75
+ ```
76
+
77
+ ### Random profile when none specified
78
+
79
+ ```python
80
+ async with sdk.session(platform="Windows", randomize=True) as browser:
81
+ # picks a random win-* profile, re-randomises hardware_concurrency /
82
+ # device_memory / platform_version using the host as a basis
83
+ page = await browser.contexts[0].new_page()
84
+ ...
85
+ ```
86
+
87
+ ### Discover bundled profiles
88
+
89
+ ```python
90
+ print(sdk.list_profiles()[:5])
91
+ # ['linux-gt1030', 'linux-gtx1050', 'mac-m1-air13', 'mac-m1-imac24', 'mac-m1-max-mbp14']
92
+
93
+ print(sdk.list_profiles(platform="Windows")[:5])
94
+
95
+ profile = sdk.random_profile(platform="macOS")
96
+ print(profile.id, profile.config["webgl"]["renderer"])
97
+ ```
98
+
99
+ ### Validate a proxy before binding
100
+
101
+ ```python
102
+ print(sdk.check_proxy("socks5://user:pass@host:port"))
103
+ # {
104
+ # 'udp_ms': 142.3,
105
+ # 'geo': GeoInfo(country_code='DE', timezone='Europe/Berlin', ...),
106
+ # 'would_enable_quic': True,
107
+ # 'would_set_webrtc': 'auto',
108
+ # }
109
+ ```
110
+
111
+ ## Pre-launch checks
112
+
113
+ Every call to `sdk.session()` / `sdk.launch()` runs the same pre-spawn
114
+ pipeline the desktop launcher uses:
115
+
116
+ 1. **`resolve_auto_fields`** — if the profile has `"auto"` sentinels for
117
+ `timezone`, `navigator.language`, or `geolocation.mode`, the SDK
118
+ makes a live geo lookup through the bound proxy (`ip-api.com` by
119
+ default). It then writes concrete values: timezone (from the API,
120
+ never a static table), `accept_language` chain, `languages`,
121
+ `icu_locale` (always overwritten so `Intl.*` matches
122
+ `navigator.language`), and lat/lng. Proxy-via failure → direct geo
123
+ → host `$LANG` / `$TZ` as last-resort fallback. The chosen geo is
124
+ surfaced on `session.geo`.
125
+ 2. **`apply_screen_strategy`** — see below.
126
+ 3. **`probe_udp`** — SOCKS5 UDP_ASSOCIATE round-trip. If it fails, QUIC
127
+ is force-disabled and WebRTC switches to `tcp_only` automatically.
128
+
129
+ ### Screen strategy
130
+
131
+ `screen_mode` kw to `session()` / `launch()`:
132
+
133
+ * **`"profile"`** — keep whatever the fingerprint claims.
134
+ * **`"cap_to_host"`** — *macOS default.* If the host monitor is smaller
135
+ than the FP screen, scale `screen.*` + `window.*` down proportionally;
136
+ otherwise no-op.
137
+ * **`"use_host"`** — *Windows/Linux default.* Overwrite `screen.*` with
138
+ the real monitor (minus a 40 px Windows taskbar) and recompute
139
+ `window.outer_*` / `window.inner_*` accordingly.
140
+
141
+ Default mode is picked from `navigator.platform`. Override per launch:
142
+
143
+ ```python
144
+ async with sdk.session("win-rtx4060", screen_mode="profile") as browser:
145
+ ...
146
+ ```
147
+
148
+ ### Host-aware hardware randomisation
149
+
150
+ `randomize=True` re-picks `hardware_concurrency`, `device_memory`, and
151
+ `platform_version` before the launch — using the same logic as the
152
+ desktop launcher (`randomize_hardware` in `lib.rs`):
153
+
154
+ * **macOS** profiles use the curated `MAC_HW_CONFIGS` table by id.
155
+ * **Windows / Linux** profiles bracket the host's logical CPU count
156
+ within `[host − 4, host + 2]` from the real x86 set
157
+ `[4, 6, 8, 12, 16, 20, 24, 28, 32]`; `device_memory` is floored by
158
+ core count (≥ 12 → 16, else 8) and capped by `host_ram_bucket_gb()`
159
+ (8 / 16 / 32 GiB bucketed from `sysctl hw.memsize` / `/proc/meminfo`
160
+ / `Get-CimInstance Win32_ComputerSystem`).
161
+
162
+ So a profile launched on an 8-core / 16 GB laptop will never claim
163
+ 32 cores / 128 GB of RAM — keeps fingerprints internally consistent
164
+ with real-world hardware.
165
+
166
+ ### Override fingerprint fields
167
+
168
+ ```python
169
+ profile = sdk.library.load("win-rtx4060").with_override(
170
+ name="my-account",
171
+ timezone="Europe/Berlin",
172
+ navigator={"language": "de-DE"},
173
+ )
174
+ async with sdk.session(profile, proxy="socks5://...") as browser:
175
+ ...
176
+ ```
177
+
178
+ ### Use your own fingerprint JSON
179
+
180
+ ```python
181
+ from shardx import Profile
182
+
183
+ profile = Profile.from_file("/path/to/my-custom.json")
184
+ async with sdk.session(profile) as browser:
185
+ ...
186
+ ```
187
+
188
+ ### WebRTC policy
189
+
190
+ ```python
191
+ async with sdk.session(
192
+ "win-rtx4060",
193
+ proxy="socks5://...",
194
+ webrtc="tcp_only", # or "block" | "auto" (default)
195
+ webrtc_public_ip="203.0.113.42", # advertised in ICE candidates
196
+ ) as browser:
197
+ ...
198
+ ```
199
+
200
+ ## Advanced: raw launch without patchright
201
+
202
+ If you'd rather drive the browser with a different CDP client (raw
203
+ `pychrome`, `pyppeteer`, your own WebSocket), skip `session()` and use
204
+ `launch()` directly:
205
+
206
+ ```python
207
+ sess = sdk.launch("win-rtx4060", proxy="socks5://...", cdp=True)
208
+ print(sess.cdp_url) # ws://127.0.0.1:54113/devtools/browser/c0a3…
209
+ # … drive it yourself …
210
+ sess.stop()
211
+ ```
212
+
213
+ `launch()` does the same pre-launch pipeline (auto-resolve, screen
214
+ strategy, UDP probe, hw randomisation) and returns a `BrowserSession`
215
+ with `cdp_url`, `geo`, `proxy_udp_ms`, `quic_enabled`, `webrtc_mode`,
216
+ `user_data_dir`, and `stop()`.
217
+
218
+ ## Cache layout
219
+
220
+ ```
221
+ ~/Library/Application Support/shardx-sdk/ (mac)
222
+ %LOCALAPPDATA%\shardx-sdk\ (win)
223
+ ~/.cache/shardx-sdk/ (linux)
224
+ ├── manifest.json ← etag cache for browser/widevine/fingerprints
225
+ ├── ShardX-Mac-arm64/ ← extracted engine
226
+ │ └── ShardX.app/…
227
+ ├── fingerprints/ ← 170 bundled .json profiles
228
+ │ ├── win-rtx4060.json
229
+ │ └── …
230
+ └── profiles/ ← per-launch user-data-dir (cookies, IndexedDB, cache)
231
+ └── <profile-id>/
232
+ ```
233
+
234
+ Override the cache root:
235
+
236
+ ```python
237
+ sdk = ShardX(cache_dir="/data/shardx")
238
+ ```
239
+
240
+ ## Update the runtime
241
+
242
+ The SDK auto-checks remote etags on the first `session`/`launch`/`list_profiles`
243
+ call of each process and re-downloads anything that changed. To force a
244
+ re-download mid-process (e.g. CI scenarios):
245
+
246
+ ```python
247
+ sdk.runtime.install(force=True)
248
+ ```
249
+
250
+ ## License
251
+
252
+ MIT (this SDK). The Chromium-fork engine binary downloaded at runtime
253
+ is a closed-source product — see the
254
+ [main repo](https://github.com/ProxyShard/ShardBrowser) for engine
255
+ licensing.
shardx-0.1.0/README.md ADDED
@@ -0,0 +1,231 @@
1
+ # shardx (Python)
2
+
3
+ Self-contained Python SDK for the **ShardX anti-detect browser** by the
4
+ [ProxyShard](https://proxyshard.com) team.
5
+
6
+ This package does **not** depend on the desktop launcher. On first use
7
+ it downloads the patched Chromium 148 engine, Widevine CDM, and the
8
+ 170-profile fingerprint library from our CDN into a local cache, then
9
+ launches isolated browser sessions on demand.
10
+
11
+ Driven by [patchright](https://github.com/Kaliiiiiiiiii-Vinyzu/patchright-python)
12
+ (stealth-patched Playwright) — `sdk.session()` returns a ready-to-use
13
+ `Browser` instance, no manual `connect_over_cdp` plumbing.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pip install shardx
19
+ ```
20
+
21
+ Supported hosts: **macOS arm64**, **Windows x64**, **Linux x64**.
22
+
23
+ ## Quick start
24
+
25
+ ```python
26
+ import asyncio
27
+ from shardx import ShardX
28
+
29
+ async def main():
30
+ sdk = ShardX()
31
+ # Engine + Widevine + fingerprint library auto-download from CDN on
32
+ # the first `session`/`launch`/`list_profiles` call (~170 MB once,
33
+ # etag-cached afterward). No separate install step.
34
+
35
+ # Launch + drive in one call. Yields a patchright `Browser`.
36
+ async with sdk.session("win-rtx4060", proxy="socks5://user:pass@host:port") as browser:
37
+ ctx = browser.contexts[0]
38
+ page = await ctx.new_page()
39
+ await page.goto("https://browserleaks.com/quic")
40
+ print(await page.title())
41
+
42
+ # Inspect what the SDK resolved before launch:
43
+ sess = browser._shardx
44
+ print(sess.geo) # GeoInfo(...) from ip-api / ipapi.co
45
+ print(sess.proxy_udp_ms, # UDP RTT in ms or None
46
+ sess.quic_enabled, # bool
47
+ sess.webrtc_mode) # "auto" | "tcp_only" | "block"
48
+ # browser + udd shut down cleanly on exit
49
+
50
+ asyncio.run(main())
51
+ ```
52
+
53
+ ### Random profile when none specified
54
+
55
+ ```python
56
+ async with sdk.session(platform="Windows", randomize=True) as browser:
57
+ # picks a random win-* profile, re-randomises hardware_concurrency /
58
+ # device_memory / platform_version using the host as a basis
59
+ page = await browser.contexts[0].new_page()
60
+ ...
61
+ ```
62
+
63
+ ### Discover bundled profiles
64
+
65
+ ```python
66
+ print(sdk.list_profiles()[:5])
67
+ # ['linux-gt1030', 'linux-gtx1050', 'mac-m1-air13', 'mac-m1-imac24', 'mac-m1-max-mbp14']
68
+
69
+ print(sdk.list_profiles(platform="Windows")[:5])
70
+
71
+ profile = sdk.random_profile(platform="macOS")
72
+ print(profile.id, profile.config["webgl"]["renderer"])
73
+ ```
74
+
75
+ ### Validate a proxy before binding
76
+
77
+ ```python
78
+ print(sdk.check_proxy("socks5://user:pass@host:port"))
79
+ # {
80
+ # 'udp_ms': 142.3,
81
+ # 'geo': GeoInfo(country_code='DE', timezone='Europe/Berlin', ...),
82
+ # 'would_enable_quic': True,
83
+ # 'would_set_webrtc': 'auto',
84
+ # }
85
+ ```
86
+
87
+ ## Pre-launch checks
88
+
89
+ Every call to `sdk.session()` / `sdk.launch()` runs the same pre-spawn
90
+ pipeline the desktop launcher uses:
91
+
92
+ 1. **`resolve_auto_fields`** — if the profile has `"auto"` sentinels for
93
+ `timezone`, `navigator.language`, or `geolocation.mode`, the SDK
94
+ makes a live geo lookup through the bound proxy (`ip-api.com` by
95
+ default). It then writes concrete values: timezone (from the API,
96
+ never a static table), `accept_language` chain, `languages`,
97
+ `icu_locale` (always overwritten so `Intl.*` matches
98
+ `navigator.language`), and lat/lng. Proxy-via failure → direct geo
99
+ → host `$LANG` / `$TZ` as last-resort fallback. The chosen geo is
100
+ surfaced on `session.geo`.
101
+ 2. **`apply_screen_strategy`** — see below.
102
+ 3. **`probe_udp`** — SOCKS5 UDP_ASSOCIATE round-trip. If it fails, QUIC
103
+ is force-disabled and WebRTC switches to `tcp_only` automatically.
104
+
105
+ ### Screen strategy
106
+
107
+ `screen_mode` kw to `session()` / `launch()`:
108
+
109
+ * **`"profile"`** — keep whatever the fingerprint claims.
110
+ * **`"cap_to_host"`** — *macOS default.* If the host monitor is smaller
111
+ than the FP screen, scale `screen.*` + `window.*` down proportionally;
112
+ otherwise no-op.
113
+ * **`"use_host"`** — *Windows/Linux default.* Overwrite `screen.*` with
114
+ the real monitor (minus a 40 px Windows taskbar) and recompute
115
+ `window.outer_*` / `window.inner_*` accordingly.
116
+
117
+ Default mode is picked from `navigator.platform`. Override per launch:
118
+
119
+ ```python
120
+ async with sdk.session("win-rtx4060", screen_mode="profile") as browser:
121
+ ...
122
+ ```
123
+
124
+ ### Host-aware hardware randomisation
125
+
126
+ `randomize=True` re-picks `hardware_concurrency`, `device_memory`, and
127
+ `platform_version` before the launch — using the same logic as the
128
+ desktop launcher (`randomize_hardware` in `lib.rs`):
129
+
130
+ * **macOS** profiles use the curated `MAC_HW_CONFIGS` table by id.
131
+ * **Windows / Linux** profiles bracket the host's logical CPU count
132
+ within `[host − 4, host + 2]` from the real x86 set
133
+ `[4, 6, 8, 12, 16, 20, 24, 28, 32]`; `device_memory` is floored by
134
+ core count (≥ 12 → 16, else 8) and capped by `host_ram_bucket_gb()`
135
+ (8 / 16 / 32 GiB bucketed from `sysctl hw.memsize` / `/proc/meminfo`
136
+ / `Get-CimInstance Win32_ComputerSystem`).
137
+
138
+ So a profile launched on an 8-core / 16 GB laptop will never claim
139
+ 32 cores / 128 GB of RAM — keeps fingerprints internally consistent
140
+ with real-world hardware.
141
+
142
+ ### Override fingerprint fields
143
+
144
+ ```python
145
+ profile = sdk.library.load("win-rtx4060").with_override(
146
+ name="my-account",
147
+ timezone="Europe/Berlin",
148
+ navigator={"language": "de-DE"},
149
+ )
150
+ async with sdk.session(profile, proxy="socks5://...") as browser:
151
+ ...
152
+ ```
153
+
154
+ ### Use your own fingerprint JSON
155
+
156
+ ```python
157
+ from shardx import Profile
158
+
159
+ profile = Profile.from_file("/path/to/my-custom.json")
160
+ async with sdk.session(profile) as browser:
161
+ ...
162
+ ```
163
+
164
+ ### WebRTC policy
165
+
166
+ ```python
167
+ async with sdk.session(
168
+ "win-rtx4060",
169
+ proxy="socks5://...",
170
+ webrtc="tcp_only", # or "block" | "auto" (default)
171
+ webrtc_public_ip="203.0.113.42", # advertised in ICE candidates
172
+ ) as browser:
173
+ ...
174
+ ```
175
+
176
+ ## Advanced: raw launch without patchright
177
+
178
+ If you'd rather drive the browser with a different CDP client (raw
179
+ `pychrome`, `pyppeteer`, your own WebSocket), skip `session()` and use
180
+ `launch()` directly:
181
+
182
+ ```python
183
+ sess = sdk.launch("win-rtx4060", proxy="socks5://...", cdp=True)
184
+ print(sess.cdp_url) # ws://127.0.0.1:54113/devtools/browser/c0a3…
185
+ # … drive it yourself …
186
+ sess.stop()
187
+ ```
188
+
189
+ `launch()` does the same pre-launch pipeline (auto-resolve, screen
190
+ strategy, UDP probe, hw randomisation) and returns a `BrowserSession`
191
+ with `cdp_url`, `geo`, `proxy_udp_ms`, `quic_enabled`, `webrtc_mode`,
192
+ `user_data_dir`, and `stop()`.
193
+
194
+ ## Cache layout
195
+
196
+ ```
197
+ ~/Library/Application Support/shardx-sdk/ (mac)
198
+ %LOCALAPPDATA%\shardx-sdk\ (win)
199
+ ~/.cache/shardx-sdk/ (linux)
200
+ ├── manifest.json ← etag cache for browser/widevine/fingerprints
201
+ ├── ShardX-Mac-arm64/ ← extracted engine
202
+ │ └── ShardX.app/…
203
+ ├── fingerprints/ ← 170 bundled .json profiles
204
+ │ ├── win-rtx4060.json
205
+ │ └── …
206
+ └── profiles/ ← per-launch user-data-dir (cookies, IndexedDB, cache)
207
+ └── <profile-id>/
208
+ ```
209
+
210
+ Override the cache root:
211
+
212
+ ```python
213
+ sdk = ShardX(cache_dir="/data/shardx")
214
+ ```
215
+
216
+ ## Update the runtime
217
+
218
+ The SDK auto-checks remote etags on the first `session`/`launch`/`list_profiles`
219
+ call of each process and re-downloads anything that changed. To force a
220
+ re-download mid-process (e.g. CI scenarios):
221
+
222
+ ```python
223
+ sdk.runtime.install(force=True)
224
+ ```
225
+
226
+ ## License
227
+
228
+ MIT (this SDK). The Chromium-fork engine binary downloaded at runtime
229
+ is a closed-source product — see the
230
+ [main repo](https://github.com/ProxyShard/ShardBrowser) for engine
231
+ licensing.
@@ -0,0 +1,38 @@
1
+ [project]
2
+ name = "shardx"
3
+ version = "0.1.0"
4
+ description = "Self-contained Python SDK for the ShardX anti-detect browser — downloads the engine + Widevine + fingerprint library on first use, launches isolated profiles."
5
+ authors = [{ name = "ProxyShard", email = "support@proxyshard.com" }]
6
+ license = { text = "MIT" }
7
+ readme = "README.md"
8
+ requires-python = ">=3.9"
9
+ keywords = ["anti-detect", "browser", "fingerprint", "chromium", "scraping", "multi-accounting", "automation"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Operating System :: MacOS",
15
+ "Operating System :: Microsoft :: Windows",
16
+ "Operating System :: POSIX :: Linux",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Topic :: Internet :: WWW/HTTP :: Browsers",
19
+ "Topic :: Software Development :: Libraries :: Python Modules",
20
+ ]
21
+ dependencies = [
22
+ "httpx[socks]>=0.27",
23
+ # patchright is a stealth-patched Playwright fork; `sdk.session()`
24
+ # returns a patchright Browser ready to drive (no manual CDP wiring).
25
+ "patchright>=1.49",
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/ProxyShard/ShardBrowser"
30
+ Documentation = "https://docs.proxyshard.com"
31
+ Issues = "https://github.com/ProxyShard/ShardBrowser/issues"
32
+
33
+ [build-system]
34
+ requires = ["hatchling"]
35
+ build-backend = "hatchling.build"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["shardx"]