Skip to content

Commit 0d1edd0

Browse files
committed
Integrate proxyshard API
1 parent c3942de commit 0d1edd0

15 files changed

Lines changed: 1676 additions & 26 deletions

File tree

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<a href="https://www.npmjs.com/package/@proxyshard/shardx"><img alt="npm downloads" src="https://img.shields.io/npm/dt/@proxyshard/shardx?style=flat-square&logo=npm&logoColor=white&label=npm&color=brightgreen"></a>
1717
</p>
1818

19-
A project by the **[ProxyShard](https://proxyshard.com)** team — the
19+
A project by the **[ProxyShard](https://proxyshard.com?utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher)** team — the
2020
proxy service with full **SOCKS5 UDP relay** (RFC 1928 §7) and active
2121
**p0f TCP-fingerprint spoofing** on the exit (so the OS the proxy
2222
claims to be on actually matches the SYN/ACK shape sites see). ShardX
@@ -25,10 +25,10 @@ of those proxies: the launcher manages profiles, binds proxies, and
2525
ships the patched **Chromium 148** browser that does the actual
2626
spoofing at the engine level.
2727

28-
* **Site:** <https://proxyshard.com>
29-
* **Docs:** <https://docs.proxyshard.com>
30-
* **UDP info:** <https://docs.proxyshard.com/eng/our-products/about-udp>
31-
* **p0f info:** <https://docs.proxyshard.com/eng/our-products/p0f-spoofing>
28+
* **Site:** <https://proxyshard.com?utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher>
29+
* **Docs:** <https://docs.proxyshard.com?utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher>
30+
* **UDP info:** <https://docs.proxyshard.com/eng/our-products/about-udp?utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher>
31+
* **p0f info:** <https://docs.proxyshard.com/eng/our-products/p0f-spoofing?utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher>
3232

3333
Drive ShardX whichever way fits the job — all four read from the same
3434
on-disk state, so a profile is reachable from every entry-point with
@@ -72,7 +72,7 @@ geolocation from the proxy's exit country, isolated `user-data-dir`,
7272
persistent cookies, Widevine pre-warm, QUIC over the proxy's UDP
7373
relay, no real-IP leaks via WebRTC.
7474

75-
Free for any use — pair with [ProxyShard](https://proxyshard.com)
75+
Free for any use — pair with [ProxyShard](https://proxyshard.com?utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher)
7676
proxies for the QUIC + WebRTC stack to actually work end-to-end, or
7777
bring your own.
7878

@@ -160,7 +160,7 @@ across iframes, web workers, devtools and headless inspection.
160160
launches are zero-network.
161161
* **Local automation API** — axum HTTP server on `127.0.0.1`,
162162
JWT-Bearer auth. Full reference at
163-
[docs.proxyshard.com/eng/shardx-launcher-api](https://docs.proxyshard.com/eng/shardx-launcher-api/binding-and-lifecycle?fallback=true),
163+
[docs.proxyshard.com/eng/shardx-launcher-api](https://docs.proxyshard.com/eng/shardx-launcher-api/binding-and-lifecycle?fallback=true&utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher),
164164
raw schema in [openapi.yaml](openapi.yaml). Create / start / stop
165165
profiles and get a CDP WebSocket URL programmatically.
166166
* **MCP server bundled** — drop into Claude Desktop / IDE for
@@ -370,7 +370,7 @@ HTTP. Every endpoint except `GET /health` requires a Bearer JWT shown
370370
in *Settings → Automation API* (regenerate rotates the signing secret
371371
live).
372372

373-
* **Reference docs:** <https://docs.proxyshard.com/eng/shardx-launcher-api/binding-and-lifecycle>
373+
* **Reference docs:** <https://docs.proxyshard.com/eng/shardx-launcher-api/binding-and-lifecycle?utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher>
374374
* **OpenAPI schema:** [openapi.yaml](openapi.yaml)
375375

376376
Launching a profile and getting a CDP endpoint:

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "shardx-launcher",
33
"private": true,
4-
"version": "0.1.2",
4+
"version": "0.1.3",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

sdks/node/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# @proxyshard/shardx (Node)
22

33
Self-contained Node/TypeScript SDK for the **ShardX anti-detect
4-
browser** by the [ProxyShard](https://proxyshard.com) team.
4+
browser** by the [ProxyShard](https://proxyshard.com?utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher) team.
55

66
Does **not** depend on the desktop launcher. On first use it downloads
77
the patched Chromium 148 engine, Widevine CDM, and the 170-profile

sdks/node/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@proxyshard/shardx",
3-
"version": "0.1.4",
3+
"version": "0.1.5",
44
"description": "Self-contained Node SDK for the ShardX anti-detect browser — downloads the engine + Widevine + fingerprint library on first use, launches isolated profiles.",
55
"type": "module",
66
"main": "dist/index.js",

sdks/python/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# shardx (Python)
22

33
Self-contained Python SDK for the **ShardX anti-detect browser** by the
4-
[ProxyShard](https://proxyshard.com) team.
4+
[ProxyShard](https://proxyshard.com?utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher) team.
55

66
This package does **not** depend on the desktop launcher. On first use
77
it downloads the patched Chromium 148 engine, Widevine CDM, and the

sdks/python/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "shardx"
3-
version = "0.1.4"
3+
version = "0.1.5"
44
description = "Self-contained Python SDK for the ShardX anti-detect browser — downloads the engine + Widevine + fingerprint library on first use, launches isolated profiles."
55
authors = [{ name = "ProxyShard", email = "support@proxyshard.com" }]
66
license = { text = "MIT" }
@@ -28,7 +28,7 @@ dependencies = [
2828
[project.urls]
2929
Homepage = "https://github.com/ProxyShard/ShardBrowser"
3030
Repository = "https://github.com/ProxyShard/ShardBrowser"
31-
Documentation = "https://docs.proxyshard.com"
31+
Documentation = "https://docs.proxyshard.com?utm_source=shardx&utm_medium=referral&utm_campaign=shardx-launcher"
3232
Issues = "https://github.com/ProxyShard/ShardBrowser/issues"
3333

3434
[build-system]

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "shardx-launcher"
3-
version = "0.1.1"
3+
version = "0.1.3"
44
description = "A Tauri App"
55
authors = ["you"]
66
edition = "2021"

src-tauri/src/lib.rs

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod mcp_setup;
88
mod process;
99
mod profile;
1010
mod proxy;
11+
mod psapi;
1112
mod runtime;
1213
mod settings;
1314
mod store;
@@ -850,6 +851,230 @@ fn api_regenerate_token() -> Result<Value, String> {
850851
}))
851852
}
852853

854+
// ---- ProxyShard billing API ----
855+
856+
/// Saved billing-API key (empty string when unset).
857+
#[tauri::command]
858+
fn ps_get_key() -> Result<String, String> {
859+
psapi::get_key().map_err(|e| e.to_string())
860+
}
861+
862+
#[tauri::command]
863+
fn ps_set_key(key: String) -> Result<(), String> {
864+
psapi::set_key(key).map_err(|e| e.to_string())
865+
}
866+
867+
/// Account profile (email, active_orders, wallet_balance cents) — also acts
868+
/// as the "is the key valid?" probe.
869+
#[tauri::command]
870+
async fn ps_me() -> Result<Value, String> {
871+
psapi::call("GET", "/user/api/me", &[], None)
872+
.await
873+
.map_err(|e| e.to_string())
874+
}
875+
876+
#[tauri::command]
877+
async fn ps_orders(status: String, offset: Option<i64>, limit: Option<i64>) -> Result<Value, String> {
878+
let mut q = vec![("status".to_string(), status)];
879+
if let Some(o) = offset {
880+
q.push(("offset".into(), o.to_string()));
881+
}
882+
if let Some(l) = limit {
883+
q.push(("limit".into(), l.to_string()));
884+
}
885+
psapi::call("GET", "/user/api/orders", &q, None)
886+
.await
887+
.map_err(|e| e.to_string())
888+
}
889+
890+
#[tauri::command]
891+
async fn ps_order(id: i64) -> Result<Value, String> {
892+
psapi::call("GET", &format!("/user/api/orders/{id}"), &[], None)
893+
.await
894+
.map_err(|e| e.to_string())
895+
}
896+
897+
#[tauri::command]
898+
async fn ps_active(order_id: i64) -> Result<Value, String> {
899+
psapi::call(
900+
"GET",
901+
"/user/api/proxies/active",
902+
&[("order_id".into(), order_id.to_string())],
903+
None,
904+
)
905+
.await
906+
.map_err(|e| e.to_string())
907+
}
908+
909+
/// Pull an order's active proxies into the local proxy list. Returns count added.
910+
#[tauri::command]
911+
async fn ps_import_order(order_id: i64, kind: String) -> Result<usize, String> {
912+
psapi::import_order_proxies(order_id, kind)
913+
.await
914+
.map_err(|e| e.to_string())
915+
}
916+
917+
#[tauri::command]
918+
async fn ps_products() -> Result<Value, String> {
919+
psapi::call("GET", "/user/api/proxies/products", &[], None)
920+
.await
921+
.map_err(|e| e.to_string())
922+
}
923+
924+
#[tauri::command]
925+
async fn ps_available_count() -> Result<Value, String> {
926+
psapi::call("GET", "/user/api/proxies/available-count", &[], None)
927+
.await
928+
.map_err(|e| e.to_string())
929+
}
930+
931+
#[tauri::command]
932+
async fn ps_calculate(
933+
product: String,
934+
location: Option<String>,
935+
cycle: Option<String>,
936+
quantity: Option<i64>,
937+
promo_code: Option<String>,
938+
addons_json: Option<String>,
939+
) -> Result<Value, String> {
940+
let mut q = vec![("product".to_string(), product)];
941+
if let Some(v) = location.filter(|s| !s.is_empty()) {
942+
q.push(("location".into(), v));
943+
}
944+
if let Some(v) = cycle.filter(|s| !s.is_empty()) {
945+
q.push(("cycle".into(), v));
946+
}
947+
if let Some(v) = quantity {
948+
q.push(("quantity".into(), v.to_string()));
949+
}
950+
if let Some(v) = promo_code.filter(|s| !s.is_empty()) {
951+
q.push(("promo_code".into(), v));
952+
}
953+
// JSON array of add-ons, e.g. [{"addon_key":"p0f_slots","qty":5}].
954+
// reqwest URL-encodes the value.
955+
if let Some(v) = addons_json.filter(|s| !s.is_empty()) {
956+
q.push(("addons_json".into(), v));
957+
}
958+
psapi::call("GET", "/user/api/orders/calculate", &q, None)
959+
.await
960+
.map_err(|e| e.to_string())
961+
}
962+
963+
#[tauri::command]
964+
async fn ps_purchase(body: Value) -> Result<Value, String> {
965+
psapi::call("POST", "/user/api/orders/purchase", &[], Some(body))
966+
.await
967+
.map_err(|e| e.to_string())
968+
}
969+
970+
/// Buy extra GB of residential traffic for an order.
971+
#[tauri::command]
972+
async fn ps_add_bandwidth(id: i64, amount: i64, promo_code: Option<String>) -> Result<Value, String> {
973+
let mut body = serde_json::json!({ "amount": amount });
974+
if let Some(p) = promo_code.filter(|s| !s.is_empty()) {
975+
body["promo_code"] = Value::String(p);
976+
}
977+
psapi::call(
978+
"POST",
979+
&format!("/user/api/orders/{id}/add-bandwidth"),
980+
&[],
981+
Some(body),
982+
)
983+
.await
984+
.map_err(|e| e.to_string())
985+
}
986+
987+
/// Account-owner traffic for a residential proxy type ("standart" | "premium").
988+
#[tauri::command]
989+
async fn ps_profile_traffic(proxy_type: String) -> Result<Value, String> {
990+
psapi::call(
991+
"GET",
992+
"/user/api/proxies/profile",
993+
&[("proxy_type".into(), proxy_type)],
994+
None,
995+
)
996+
.await
997+
.map_err(|e| e.to_string())
998+
}
999+
1000+
#[tauri::command]
1001+
async fn ps_renew(id: i64) -> Result<Value, String> {
1002+
psapi::call("POST", &format!("/user/api/orders/{id}/renew"), &[], None)
1003+
.await
1004+
.map_err(|e| e.to_string())
1005+
}
1006+
1007+
/// Residential location reference data (for the proxy generator).
1008+
#[tauri::command]
1009+
async fn ps_countries(proxy_type: String) -> Result<Value, String> {
1010+
psapi::call(
1011+
"GET",
1012+
"/user/api/proxies/countries",
1013+
&[("proxy_type".into(), proxy_type)],
1014+
None,
1015+
)
1016+
.await
1017+
.map_err(|e| e.to_string())
1018+
}
1019+
1020+
#[tauri::command]
1021+
async fn ps_regions(proxy_type: String, country_code: String) -> Result<Value, String> {
1022+
psapi::call(
1023+
"GET",
1024+
"/user/api/proxies/regions",
1025+
&[
1026+
("proxy_type".into(), proxy_type),
1027+
("country_code".into(), country_code),
1028+
],
1029+
None,
1030+
)
1031+
.await
1032+
.map_err(|e| e.to_string())
1033+
}
1034+
1035+
#[tauri::command]
1036+
async fn ps_cities(proxy_type: String, country_code: String, region_code: String) -> Result<Value, String> {
1037+
psapi::call(
1038+
"GET",
1039+
"/user/api/proxies/cities",
1040+
&[
1041+
("proxy_type".into(), proxy_type),
1042+
("country_code".into(), country_code),
1043+
("region_code".into(), region_code),
1044+
],
1045+
None,
1046+
)
1047+
.await
1048+
.map_err(|e| e.to_string())
1049+
}
1050+
1051+
/// Assign OS-fingerprint signatures to proxy IPs (consumes p0f slots).
1052+
/// `items` is an array of `{ ip, signature }`.
1053+
#[tauri::command]
1054+
async fn ps_signature_set(order_id: i64, items: Value) -> Result<Value, String> {
1055+
psapi::call(
1056+
"POST",
1057+
&format!("/user/api/orders/{order_id}/signature/set"),
1058+
&[],
1059+
Some(serde_json::json!({ "items": items })),
1060+
)
1061+
.await
1062+
.map_err(|e| e.to_string())
1063+
}
1064+
1065+
/// Set/clear an order's tag.
1066+
#[tauri::command]
1067+
async fn ps_set_tag(id: i64, tag: String) -> Result<Value, String> {
1068+
psapi::call(
1069+
"POST",
1070+
&format!("/user/api/orders/{id}/tag"),
1071+
&[],
1072+
Some(serde_json::json!({ "tag": tag })),
1073+
)
1074+
.await
1075+
.map_err(|e| e.to_string())
1076+
}
1077+
8531078
#[cfg_attr(mobile, tauri::mobile_entry_point)]
8541079
pub fn run() {
8551080
tauri::Builder::default()
@@ -898,6 +1123,25 @@ pub fn run() {
8981123
settings_save,
8991124
api_info,
9001125
api_regenerate_token,
1126+
ps_get_key,
1127+
ps_set_key,
1128+
ps_me,
1129+
ps_orders,
1130+
ps_order,
1131+
ps_active,
1132+
ps_import_order,
1133+
ps_products,
1134+
ps_available_count,
1135+
ps_calculate,
1136+
ps_purchase,
1137+
ps_add_bandwidth,
1138+
ps_profile_traffic,
1139+
ps_renew,
1140+
ps_set_tag,
1141+
ps_countries,
1142+
ps_regions,
1143+
ps_cities,
1144+
ps_signature_set,
9011145
cookies_export,
9021146
cookies_export_to_file,
9031147
cookies_import,

0 commit comments

Comments
 (0)