novada-proxy-core 0.0.1
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.
- package/build/adapters/brightdata.d.ts +24 -0
- package/build/adapters/brightdata.js +56 -0
- package/build/adapters/generic.d.ts +32 -0
- package/build/adapters/generic.js +63 -0
- package/build/adapters/index.d.ts +16 -0
- package/build/adapters/index.js +42 -0
- package/build/adapters/novada.d.ts +23 -0
- package/build/adapters/novada.js +61 -0
- package/build/adapters/oxylabs.d.ts +22 -0
- package/build/adapters/oxylabs.js +54 -0
- package/build/adapters/smartproxy.d.ts +22 -0
- package/build/adapters/smartproxy.js +54 -0
- package/build/adapters/types.d.ts +58 -0
- package/build/adapters/types.js +7 -0
- package/build/config.d.ts +4 -0
- package/build/config.js +7 -0
- package/build/errors.d.ts +2 -0
- package/build/errors.js +58 -0
- package/build/index.d.ts +28 -0
- package/build/index.js +22 -0
- package/build/redact.d.ts +2 -0
- package/build/redact.js +24 -0
- package/build/tools/batch.d.ts +24 -0
- package/build/tools/batch.js +156 -0
- package/build/tools/crawl.d.ts +33 -0
- package/build/tools/crawl.js +604 -0
- package/build/tools/extract.d.ts +22 -0
- package/build/tools/extract.js +454 -0
- package/build/tools/fetch.d.ts +17 -0
- package/build/tools/fetch.js +243 -0
- package/build/tools/index.d.ts +19 -0
- package/build/tools/index.js +10 -0
- package/build/tools/map.d.ts +19 -0
- package/build/tools/map.js +131 -0
- package/build/tools/render.d.ts +8 -0
- package/build/tools/render.js +98 -0
- package/build/tools/research.d.ts +9 -0
- package/build/tools/research.js +126 -0
- package/build/tools/search.d.ts +9 -0
- package/build/tools/search.js +104 -0
- package/build/tools/session.d.ts +12 -0
- package/build/tools/session.js +108 -0
- package/build/tools/status.d.ts +2 -0
- package/build/tools/status.js +66 -0
- package/build/types.d.ts +34 -0
- package/build/types.js +1 -0
- package/build/utils.d.ts +18 -0
- package/build/utils.js +151 -0
- package/build/validation.d.ts +4 -0
- package/build/validation.js +6 -0
- package/package.json +50 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ProxyAdapter } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* BrightData residential proxy adapter.
|
|
4
|
+
*
|
|
5
|
+
* BrightData (formerly Luminati) is the largest residential proxy network.
|
|
6
|
+
* This adapter encodes country, city, and session targeting automatically
|
|
7
|
+
* using BrightData's username-suffix format.
|
|
8
|
+
*
|
|
9
|
+
* Auth format:
|
|
10
|
+
* BASE_USERNAME[-country-XX][-city-CITY][-session-ID]:PASS@HOST:PORT
|
|
11
|
+
*
|
|
12
|
+
* Where BASE_USERNAME is your full BrightData username including zone:
|
|
13
|
+
* e.g. brd-customer-abc123-zone-residential
|
|
14
|
+
*
|
|
15
|
+
* Get credentials:
|
|
16
|
+
* brightdata.com → Proxies & Scraping → Residential → Access Parameters
|
|
17
|
+
*
|
|
18
|
+
* Env vars:
|
|
19
|
+
* BRIGHTDATA_USER — required (full username, e.g. brd-customer-abc123-zone-residential)
|
|
20
|
+
* BRIGHTDATA_PASS — required
|
|
21
|
+
* BRIGHTDATA_HOST — optional (default: zproxy.lum-superproxy.io)
|
|
22
|
+
* BRIGHTDATA_PORT — optional (default: 22225)
|
|
23
|
+
*/
|
|
24
|
+
export declare const BrightDataAdapter: ProxyAdapter;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BrightData residential proxy adapter.
|
|
3
|
+
*
|
|
4
|
+
* BrightData (formerly Luminati) is the largest residential proxy network.
|
|
5
|
+
* This adapter encodes country, city, and session targeting automatically
|
|
6
|
+
* using BrightData's username-suffix format.
|
|
7
|
+
*
|
|
8
|
+
* Auth format:
|
|
9
|
+
* BASE_USERNAME[-country-XX][-city-CITY][-session-ID]:PASS@HOST:PORT
|
|
10
|
+
*
|
|
11
|
+
* Where BASE_USERNAME is your full BrightData username including zone:
|
|
12
|
+
* e.g. brd-customer-abc123-zone-residential
|
|
13
|
+
*
|
|
14
|
+
* Get credentials:
|
|
15
|
+
* brightdata.com → Proxies & Scraping → Residential → Access Parameters
|
|
16
|
+
*
|
|
17
|
+
* Env vars:
|
|
18
|
+
* BRIGHTDATA_USER — required (full username, e.g. brd-customer-abc123-zone-residential)
|
|
19
|
+
* BRIGHTDATA_PASS — required
|
|
20
|
+
* BRIGHTDATA_HOST — optional (default: zproxy.lum-superproxy.io)
|
|
21
|
+
* BRIGHTDATA_PORT — optional (default: 22225)
|
|
22
|
+
*/
|
|
23
|
+
export const BrightDataAdapter = {
|
|
24
|
+
name: "brightdata",
|
|
25
|
+
displayName: "BrightData",
|
|
26
|
+
lastVerified: "2026-04-09",
|
|
27
|
+
capabilities: { country: true, city: true, sticky: true },
|
|
28
|
+
credentialDocs: "brightdata.com → Proxies & Scraping → Residential → Access Parameters",
|
|
29
|
+
sensitiveFields: ["pass"],
|
|
30
|
+
loadCredentials(env) {
|
|
31
|
+
const user = env.BRIGHTDATA_USER;
|
|
32
|
+
const pass = env.BRIGHTDATA_PASS;
|
|
33
|
+
if (!user || !pass)
|
|
34
|
+
return null;
|
|
35
|
+
const rawPort = Number(env.BRIGHTDATA_PORT);
|
|
36
|
+
const port = Number.isInteger(rawPort) && rawPort > 0 && rawPort < 65536
|
|
37
|
+
? rawPort : 22225;
|
|
38
|
+
return {
|
|
39
|
+
user,
|
|
40
|
+
pass,
|
|
41
|
+
host: env.BRIGHTDATA_HOST || "zproxy.lum-superproxy.io",
|
|
42
|
+
port: String(port),
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
buildProxyUrl(credentials, params) {
|
|
46
|
+
// BrightData appends targeting params to the username with `-` delimiter
|
|
47
|
+
let username = credentials.user;
|
|
48
|
+
if (params.country)
|
|
49
|
+
username += `-country-${params.country.toLowerCase()}`;
|
|
50
|
+
if (params.city)
|
|
51
|
+
username += `-city-${params.city.toLowerCase()}`;
|
|
52
|
+
if (params.session_id)
|
|
53
|
+
username += `-sid-${params.session_id}`;
|
|
54
|
+
return `http://${encodeURIComponent(username)}:${encodeURIComponent(credentials.pass)}@${credentials.host}:${credentials.port}`;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ProxyAdapter } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generic HTTP Proxy adapter.
|
|
4
|
+
*
|
|
5
|
+
* Set PROXY_URL=http://user:pass@host:port to use any standard HTTP proxy.
|
|
6
|
+
*
|
|
7
|
+
* Compatible with (among others):
|
|
8
|
+
* BrightData — https://brightdata.com (zproxy.lum-superproxy.io:22225)
|
|
9
|
+
* Smartproxy — https://smartproxy.com (gate.smartproxy.com:10001)
|
|
10
|
+
* Oxylabs — https://oxylabs.io (pr.oxylabs.io:7777)
|
|
11
|
+
* IPRoyal — https://iproyal.com (geo.iproyal.com:12321)
|
|
12
|
+
* Any HTTP CONNECT-capable proxy
|
|
13
|
+
*
|
|
14
|
+
* ⚠️ Country, city, and session_id targeting parameters are NOT automatically
|
|
15
|
+
* applied — encode them directly in PROXY_URL per your provider's format.
|
|
16
|
+
* For full targeting support with automatic parameter encoding, use a
|
|
17
|
+
* provider-specific adapter (e.g. Novada via NOVADA_PROXY_USER/PASS).
|
|
18
|
+
*
|
|
19
|
+
* Examples:
|
|
20
|
+
* # BrightData with US targeting (encoded in username)
|
|
21
|
+
* PROXY_URL=http://user-country-us:pass@zproxy.lum-superproxy.io:22225
|
|
22
|
+
*
|
|
23
|
+
* # Smartproxy with DE targeting
|
|
24
|
+
* PROXY_URL=http://user-country-DE:pass@gate.smartproxy.com:10001
|
|
25
|
+
*
|
|
26
|
+
* # Plain proxy (rotating, no targeting)
|
|
27
|
+
* PROXY_URL=http://user:pass@proxy.example.com:8080
|
|
28
|
+
*
|
|
29
|
+
* Env vars:
|
|
30
|
+
* PROXY_URL — required. Must start with http:// or https://
|
|
31
|
+
*/
|
|
32
|
+
export declare const GenericHttpAdapter: ProxyAdapter;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic HTTP Proxy adapter.
|
|
3
|
+
*
|
|
4
|
+
* Set PROXY_URL=http://user:pass@host:port to use any standard HTTP proxy.
|
|
5
|
+
*
|
|
6
|
+
* Compatible with (among others):
|
|
7
|
+
* BrightData — https://brightdata.com (zproxy.lum-superproxy.io:22225)
|
|
8
|
+
* Smartproxy — https://smartproxy.com (gate.smartproxy.com:10001)
|
|
9
|
+
* Oxylabs — https://oxylabs.io (pr.oxylabs.io:7777)
|
|
10
|
+
* IPRoyal — https://iproyal.com (geo.iproyal.com:12321)
|
|
11
|
+
* Any HTTP CONNECT-capable proxy
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ Country, city, and session_id targeting parameters are NOT automatically
|
|
14
|
+
* applied — encode them directly in PROXY_URL per your provider's format.
|
|
15
|
+
* For full targeting support with automatic parameter encoding, use a
|
|
16
|
+
* provider-specific adapter (e.g. Novada via NOVADA_PROXY_USER/PASS).
|
|
17
|
+
*
|
|
18
|
+
* Examples:
|
|
19
|
+
* # BrightData with US targeting (encoded in username)
|
|
20
|
+
* PROXY_URL=http://user-country-us:pass@zproxy.lum-superproxy.io:22225
|
|
21
|
+
*
|
|
22
|
+
* # Smartproxy with DE targeting
|
|
23
|
+
* PROXY_URL=http://user-country-DE:pass@gate.smartproxy.com:10001
|
|
24
|
+
*
|
|
25
|
+
* # Plain proxy (rotating, no targeting)
|
|
26
|
+
* PROXY_URL=http://user:pass@proxy.example.com:8080
|
|
27
|
+
*
|
|
28
|
+
* Env vars:
|
|
29
|
+
* PROXY_URL — required. Must start with http:// or https://
|
|
30
|
+
*/
|
|
31
|
+
export const GenericHttpAdapter = {
|
|
32
|
+
name: "generic",
|
|
33
|
+
displayName: "Generic HTTP Proxy",
|
|
34
|
+
lastVerified: "2026-04-09",
|
|
35
|
+
capabilities: { country: false, city: false, sticky: false },
|
|
36
|
+
credentialDocs: "Set PROXY_URL=http://user:pass@host:port",
|
|
37
|
+
sensitiveFields: ["pass", "proxyUrl"],
|
|
38
|
+
loadCredentials(env) {
|
|
39
|
+
const raw = env.PROXY_URL;
|
|
40
|
+
if (!raw)
|
|
41
|
+
return null;
|
|
42
|
+
let parsed;
|
|
43
|
+
try {
|
|
44
|
+
parsed = new URL(raw);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Malformed URL — do not throw, just decline so the error surfaces cleanly
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
51
|
+
return null;
|
|
52
|
+
return {
|
|
53
|
+
proxyUrl: raw,
|
|
54
|
+
user: parsed.username || "",
|
|
55
|
+
pass: parsed.password || "",
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
buildProxyUrl(credentials, _params) {
|
|
59
|
+
// Return the URL as-is. Country/city/session_id must be encoded
|
|
60
|
+
// in the URL by the user per their provider's specific format.
|
|
61
|
+
return credentials.proxyUrl;
|
|
62
|
+
},
|
|
63
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ProxyAdapter, ProxyCredentials } from "./types.js";
|
|
2
|
+
export type { ProxyAdapter, ProxyCredentials, ProxyRequestParams, AdapterCapabilities } from "./types.js";
|
|
3
|
+
export interface ResolvedAdapter {
|
|
4
|
+
adapter: ProxyAdapter;
|
|
5
|
+
credentials: ProxyCredentials;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Resolve which proxy adapter to use based on available environment variables.
|
|
9
|
+
* Returns the first configured adapter (Novada wins if multiple are set).
|
|
10
|
+
* Returns null if no proxy provider is configured.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveAdapter(env: NodeJS.ProcessEnv): ResolvedAdapter | null;
|
|
13
|
+
/**
|
|
14
|
+
* List all registered adapters (for --help and status output).
|
|
15
|
+
*/
|
|
16
|
+
export declare function listAdapters(): ProxyAdapter[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { NovadaAdapter } from "./novada.js";
|
|
2
|
+
import { GenericHttpAdapter } from "./generic.js";
|
|
3
|
+
import { BrightDataAdapter } from "./brightdata.js";
|
|
4
|
+
import { SmartproxyAdapter } from "./smartproxy.js";
|
|
5
|
+
import { OxylabsAdapter } from "./oxylabs.js";
|
|
6
|
+
/**
|
|
7
|
+
* Registered proxy adapters in priority order.
|
|
8
|
+
*
|
|
9
|
+
* Resolution: the first adapter whose loadCredentials() returns non-null wins.
|
|
10
|
+
* Novada is always first — it's our default and priority provider.
|
|
11
|
+
*
|
|
12
|
+
* To add a provider:
|
|
13
|
+
* 1. Create src/adapters/<provider>.ts implementing ProxyAdapter
|
|
14
|
+
* 2. Import it here and add it to the array below
|
|
15
|
+
* 3. Nothing else changes
|
|
16
|
+
*/
|
|
17
|
+
const ADAPTERS = [
|
|
18
|
+
NovadaAdapter, // Always first — default, deepest integration
|
|
19
|
+
BrightDataAdapter, // BRIGHTDATA_USER + BRIGHTDATA_PASS
|
|
20
|
+
SmartproxyAdapter, // SMARTPROXY_USER + SMARTPROXY_PASS
|
|
21
|
+
OxylabsAdapter, // OXYLABS_USER + OXYLABS_PASS
|
|
22
|
+
GenericHttpAdapter, // Always last — PROXY_URL fallback (no auto-targeting)
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* Resolve which proxy adapter to use based on available environment variables.
|
|
26
|
+
* Returns the first configured adapter (Novada wins if multiple are set).
|
|
27
|
+
* Returns null if no proxy provider is configured.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveAdapter(env) {
|
|
30
|
+
for (const adapter of ADAPTERS) {
|
|
31
|
+
const credentials = adapter.loadCredentials(env);
|
|
32
|
+
if (credentials)
|
|
33
|
+
return { adapter, credentials };
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* List all registered adapters (for --help and status output).
|
|
39
|
+
*/
|
|
40
|
+
export function listAdapters() {
|
|
41
|
+
return [...ADAPTERS];
|
|
42
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ProxyAdapter } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Novada residential proxy adapter.
|
|
4
|
+
*
|
|
5
|
+
* Auth format: USERNAME-zone-ZONE[-region-XX][-city-CITY][-session-ID-sessTime-N]:PASS@HOST:PORT
|
|
6
|
+
*
|
|
7
|
+
* Rules enforced here (not in tool validators — they handle user-facing input):
|
|
8
|
+
* - Hyphen `-` is Novada's segment delimiter → never appears in country/city/session_id
|
|
9
|
+
* (enforced upstream in validateFetchParams / validateSessionParams)
|
|
10
|
+
* - session_id → `-session-ID-sessTime-N` suffix (sessTime required for sticky IP)
|
|
11
|
+
* - country → `-region-XX` suffix (lowercased)
|
|
12
|
+
* - city → `-city-CITY` suffix (lowercased)
|
|
13
|
+
*
|
|
14
|
+
* Env vars:
|
|
15
|
+
* NOVADA_PROXY_USER — required
|
|
16
|
+
* NOVADA_PROXY_PASS — required
|
|
17
|
+
* NOVADA_PROXY_HOST — optional; defaults to super.novada.pro (shared load balancer)
|
|
18
|
+
* Set to your account-specific host for reliable sticky sessions.
|
|
19
|
+
* NOVADA_PROXY_PORT — optional; defaults to 7777
|
|
20
|
+
* NOVADA_PROXY_ZONE — optional; defaults to "res" (residential).
|
|
21
|
+
* Other zones: "isp" (rotating ISP), "dcp" (rotating datacenter)
|
|
22
|
+
*/
|
|
23
|
+
export declare const NovadaAdapter: ProxyAdapter;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Novada residential proxy adapter.
|
|
3
|
+
*
|
|
4
|
+
* Auth format: USERNAME-zone-ZONE[-region-XX][-city-CITY][-session-ID-sessTime-N]:PASS@HOST:PORT
|
|
5
|
+
*
|
|
6
|
+
* Rules enforced here (not in tool validators — they handle user-facing input):
|
|
7
|
+
* - Hyphen `-` is Novada's segment delimiter → never appears in country/city/session_id
|
|
8
|
+
* (enforced upstream in validateFetchParams / validateSessionParams)
|
|
9
|
+
* - session_id → `-session-ID-sessTime-N` suffix (sessTime required for sticky IP)
|
|
10
|
+
* - country → `-region-XX` suffix (lowercased)
|
|
11
|
+
* - city → `-city-CITY` suffix (lowercased)
|
|
12
|
+
*
|
|
13
|
+
* Env vars:
|
|
14
|
+
* NOVADA_PROXY_USER — required
|
|
15
|
+
* NOVADA_PROXY_PASS — required
|
|
16
|
+
* NOVADA_PROXY_HOST — optional; defaults to super.novada.pro (shared load balancer)
|
|
17
|
+
* Set to your account-specific host for reliable sticky sessions.
|
|
18
|
+
* NOVADA_PROXY_PORT — optional; defaults to 7777
|
|
19
|
+
* NOVADA_PROXY_ZONE — optional; defaults to "res" (residential).
|
|
20
|
+
* Other zones: "isp" (rotating ISP), "dcp" (rotating datacenter)
|
|
21
|
+
*/
|
|
22
|
+
export const NovadaAdapter = {
|
|
23
|
+
name: "novada",
|
|
24
|
+
displayName: "Novada",
|
|
25
|
+
lastVerified: "2026-04-09",
|
|
26
|
+
capabilities: { country: true, city: true, sticky: true },
|
|
27
|
+
credentialDocs: "novada.com → Dashboard → Residential Proxies → Endpoint Generator",
|
|
28
|
+
sensitiveFields: ["pass"],
|
|
29
|
+
loadCredentials(env) {
|
|
30
|
+
const user = env.NOVADA_PROXY_USER;
|
|
31
|
+
const pass = env.NOVADA_PROXY_PASS;
|
|
32
|
+
if (!user || !pass)
|
|
33
|
+
return null;
|
|
34
|
+
const rawPort = Number(env.NOVADA_PROXY_PORT);
|
|
35
|
+
const port = Number.isInteger(rawPort) && rawPort > 0 && rawPort < 65536
|
|
36
|
+
? rawPort : 7777;
|
|
37
|
+
const zone = env.NOVADA_PROXY_ZONE || "res";
|
|
38
|
+
return {
|
|
39
|
+
user,
|
|
40
|
+
pass,
|
|
41
|
+
host: env.NOVADA_PROXY_HOST || "super.novada.pro",
|
|
42
|
+
port: String(port),
|
|
43
|
+
zone,
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
buildProxyUrl(credentials, params) {
|
|
47
|
+
const zone = credentials.zone || "res";
|
|
48
|
+
let username = `${credentials.user}-zone-${zone}`;
|
|
49
|
+
if (params.country)
|
|
50
|
+
username += `-region-${params.country.toLowerCase()}`;
|
|
51
|
+
if (params.city)
|
|
52
|
+
username += `-city-${params.city.toLowerCase()}`;
|
|
53
|
+
if (params.session_id) {
|
|
54
|
+
// sessTime is required for sticky IP on all Novada zones.
|
|
55
|
+
// Default: 5 min for res/dcp, 120 min for isp. Max: 120 min res, 360 min isp, 30 min dcp.
|
|
56
|
+
const defaultSessTime = zone === "isp" ? 120 : 5;
|
|
57
|
+
username += `-session-${params.session_id}-sessTime-${defaultSessTime}`;
|
|
58
|
+
}
|
|
59
|
+
return `http://${encodeURIComponent(username)}:${encodeURIComponent(credentials.pass)}@${credentials.host}:${credentials.port}`;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ProxyAdapter } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Oxylabs residential proxy adapter.
|
|
4
|
+
*
|
|
5
|
+
* Oxylabs uses a different format: country and city are encoded as
|
|
6
|
+
* `-cc-XX` and `-city-CITY` suffixes. Session stickiness uses `-sessid-ID`.
|
|
7
|
+
*
|
|
8
|
+
* Auth format:
|
|
9
|
+
* USER[-cc-XX][-city-CITY][-sessid-ID]:PASS@pr.oxylabs.io:PORT
|
|
10
|
+
*
|
|
11
|
+
* Default port: 7777.
|
|
12
|
+
*
|
|
13
|
+
* Get credentials:
|
|
14
|
+
* oxylabs.io → Dashboard → Residential Proxies → Access Details
|
|
15
|
+
*
|
|
16
|
+
* Env vars:
|
|
17
|
+
* OXYLABS_USER — required
|
|
18
|
+
* OXYLABS_PASS — required
|
|
19
|
+
* OXYLABS_HOST — optional (default: pr.oxylabs.io)
|
|
20
|
+
* OXYLABS_PORT — optional (default: 7777)
|
|
21
|
+
*/
|
|
22
|
+
export declare const OxylabsAdapter: ProxyAdapter;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oxylabs residential proxy adapter.
|
|
3
|
+
*
|
|
4
|
+
* Oxylabs uses a different format: country and city are encoded as
|
|
5
|
+
* `-cc-XX` and `-city-CITY` suffixes. Session stickiness uses `-sessid-ID`.
|
|
6
|
+
*
|
|
7
|
+
* Auth format:
|
|
8
|
+
* USER[-cc-XX][-city-CITY][-sessid-ID]:PASS@pr.oxylabs.io:PORT
|
|
9
|
+
*
|
|
10
|
+
* Default port: 7777.
|
|
11
|
+
*
|
|
12
|
+
* Get credentials:
|
|
13
|
+
* oxylabs.io → Dashboard → Residential Proxies → Access Details
|
|
14
|
+
*
|
|
15
|
+
* Env vars:
|
|
16
|
+
* OXYLABS_USER — required
|
|
17
|
+
* OXYLABS_PASS — required
|
|
18
|
+
* OXYLABS_HOST — optional (default: pr.oxylabs.io)
|
|
19
|
+
* OXYLABS_PORT — optional (default: 7777)
|
|
20
|
+
*/
|
|
21
|
+
export const OxylabsAdapter = {
|
|
22
|
+
name: "oxylabs",
|
|
23
|
+
displayName: "Oxylabs",
|
|
24
|
+
lastVerified: "2026-04-09",
|
|
25
|
+
capabilities: { country: true, city: true, sticky: true },
|
|
26
|
+
credentialDocs: "oxylabs.io → Dashboard → Residential Proxies → Access Details",
|
|
27
|
+
sensitiveFields: ["pass"],
|
|
28
|
+
loadCredentials(env) {
|
|
29
|
+
const user = env.OXYLABS_USER;
|
|
30
|
+
const pass = env.OXYLABS_PASS;
|
|
31
|
+
if (!user || !pass)
|
|
32
|
+
return null;
|
|
33
|
+
const rawPort = Number(env.OXYLABS_PORT);
|
|
34
|
+
const port = Number.isInteger(rawPort) && rawPort > 0 && rawPort < 65536
|
|
35
|
+
? rawPort : 7777;
|
|
36
|
+
return {
|
|
37
|
+
user,
|
|
38
|
+
pass,
|
|
39
|
+
host: env.OXYLABS_HOST || "pr.oxylabs.io",
|
|
40
|
+
port: String(port),
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
buildProxyUrl(credentials, params) {
|
|
44
|
+
// Oxylabs uses -cc-XX for country, -city-CITY for city, -sessid-ID for sticky
|
|
45
|
+
let username = credentials.user;
|
|
46
|
+
if (params.country)
|
|
47
|
+
username += `-cc-${params.country.toUpperCase()}`;
|
|
48
|
+
if (params.city)
|
|
49
|
+
username += `-city-${params.city.toLowerCase()}`;
|
|
50
|
+
if (params.session_id)
|
|
51
|
+
username += `-sessid-${params.session_id}`;
|
|
52
|
+
return `http://${encodeURIComponent(username)}:${encodeURIComponent(credentials.pass)}@${credentials.host}:${credentials.port}`;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ProxyAdapter } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Smartproxy residential proxy adapter.
|
|
4
|
+
*
|
|
5
|
+
* Smartproxy uses a username-suffix format similar to BrightData but with
|
|
6
|
+
* different segment names. Country is encoded as `-country-XX` (uppercase).
|
|
7
|
+
*
|
|
8
|
+
* Auth format:
|
|
9
|
+
* USER[-country-XX][-city-CITY][-session-SESSIONID]:PASS@gate.smartproxy.com:PORT
|
|
10
|
+
*
|
|
11
|
+
* Default port: 10001 (rotating). Use 10000 for US-only pool.
|
|
12
|
+
*
|
|
13
|
+
* Get credentials:
|
|
14
|
+
* smartproxy.com → Dashboard → Residential → Endpoint Generator
|
|
15
|
+
*
|
|
16
|
+
* Env vars:
|
|
17
|
+
* SMARTPROXY_USER — required
|
|
18
|
+
* SMARTPROXY_PASS — required
|
|
19
|
+
* SMARTPROXY_HOST — optional (default: gate.smartproxy.com)
|
|
20
|
+
* SMARTPROXY_PORT — optional (default: 10001)
|
|
21
|
+
*/
|
|
22
|
+
export declare const SmartproxyAdapter: ProxyAdapter;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smartproxy residential proxy adapter.
|
|
3
|
+
*
|
|
4
|
+
* Smartproxy uses a username-suffix format similar to BrightData but with
|
|
5
|
+
* different segment names. Country is encoded as `-country-XX` (uppercase).
|
|
6
|
+
*
|
|
7
|
+
* Auth format:
|
|
8
|
+
* USER[-country-XX][-city-CITY][-session-SESSIONID]:PASS@gate.smartproxy.com:PORT
|
|
9
|
+
*
|
|
10
|
+
* Default port: 10001 (rotating). Use 10000 for US-only pool.
|
|
11
|
+
*
|
|
12
|
+
* Get credentials:
|
|
13
|
+
* smartproxy.com → Dashboard → Residential → Endpoint Generator
|
|
14
|
+
*
|
|
15
|
+
* Env vars:
|
|
16
|
+
* SMARTPROXY_USER — required
|
|
17
|
+
* SMARTPROXY_PASS — required
|
|
18
|
+
* SMARTPROXY_HOST — optional (default: gate.smartproxy.com)
|
|
19
|
+
* SMARTPROXY_PORT — optional (default: 10001)
|
|
20
|
+
*/
|
|
21
|
+
export const SmartproxyAdapter = {
|
|
22
|
+
name: "smartproxy",
|
|
23
|
+
displayName: "Smartproxy",
|
|
24
|
+
lastVerified: "2026-04-09",
|
|
25
|
+
capabilities: { country: true, city: true, sticky: true },
|
|
26
|
+
credentialDocs: "smartproxy.com → Dashboard → Residential → Endpoint Generator",
|
|
27
|
+
sensitiveFields: ["pass"],
|
|
28
|
+
loadCredentials(env) {
|
|
29
|
+
const user = env.SMARTPROXY_USER;
|
|
30
|
+
const pass = env.SMARTPROXY_PASS;
|
|
31
|
+
if (!user || !pass)
|
|
32
|
+
return null;
|
|
33
|
+
const rawPort = Number(env.SMARTPROXY_PORT);
|
|
34
|
+
const port = Number.isInteger(rawPort) && rawPort > 0 && rawPort < 65536
|
|
35
|
+
? rawPort : 10001;
|
|
36
|
+
return {
|
|
37
|
+
user,
|
|
38
|
+
pass,
|
|
39
|
+
host: env.SMARTPROXY_HOST || "gate.smartproxy.com",
|
|
40
|
+
port: String(port),
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
buildProxyUrl(credentials, params) {
|
|
44
|
+
// Smartproxy appends targeting params to the username with `-` delimiter
|
|
45
|
+
let username = credentials.user;
|
|
46
|
+
if (params.country)
|
|
47
|
+
username += `-country-${params.country.toUpperCase()}`;
|
|
48
|
+
if (params.city)
|
|
49
|
+
username += `-city-${params.city.toLowerCase()}`;
|
|
50
|
+
if (params.session_id)
|
|
51
|
+
username += `-session-${params.session_id}`;
|
|
52
|
+
return `http://${encodeURIComponent(username)}:${encodeURIComponent(credentials.pass)}@${credentials.host}:${credentials.port}`;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProxyAdapter — the contract every proxy provider must satisfy.
|
|
3
|
+
*
|
|
4
|
+
* Adding a new provider = implement this interface in a new file,
|
|
5
|
+
* register it in index.ts. Nothing else changes.
|
|
6
|
+
*/
|
|
7
|
+
export interface ProxyCredentials {
|
|
8
|
+
/** Provider-specific key/value pairs (e.g. user, pass, host, port). */
|
|
9
|
+
[key: string]: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AdapterCapabilities {
|
|
12
|
+
/** Supports 2-letter country-level geo-targeting. */
|
|
13
|
+
country: boolean;
|
|
14
|
+
/** Supports city-level geo-targeting. */
|
|
15
|
+
city: boolean;
|
|
16
|
+
/** Supports sticky sessions (same IP across requests with same session_id). */
|
|
17
|
+
sticky: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface ProxyRequestParams {
|
|
20
|
+
country?: string;
|
|
21
|
+
city?: string;
|
|
22
|
+
session_id?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ProxyAdapter {
|
|
25
|
+
/** Internal identifier. Snake-case, lowercase. e.g. "novada", "brightdata" */
|
|
26
|
+
readonly name: string;
|
|
27
|
+
/** Human-readable name shown in error messages and status output. */
|
|
28
|
+
readonly displayName: string;
|
|
29
|
+
/**
|
|
30
|
+
* ISO date when this adapter was last tested against live credentials.
|
|
31
|
+
* Shown to users so they know how fresh the integration is.
|
|
32
|
+
*/
|
|
33
|
+
readonly lastVerified: string;
|
|
34
|
+
/** Which targeting features this provider supports. */
|
|
35
|
+
readonly capabilities: AdapterCapabilities;
|
|
36
|
+
/** URL or description of where users get credentials for this provider. */
|
|
37
|
+
readonly credentialDocs: string;
|
|
38
|
+
/**
|
|
39
|
+
* Keys in ProxyCredentials that contain secrets.
|
|
40
|
+
* Used to redact sensitive values from error messages before surfacing to agents.
|
|
41
|
+
*/
|
|
42
|
+
readonly sensitiveFields: ReadonlyArray<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Reads environment variables and returns a credentials object,
|
|
45
|
+
* or null if this provider is not configured (required env vars missing).
|
|
46
|
+
*
|
|
47
|
+
* Implementations should not throw — return null for missing/incomplete config.
|
|
48
|
+
*/
|
|
49
|
+
loadCredentials(env: NodeJS.ProcessEnv): ProxyCredentials | null;
|
|
50
|
+
/**
|
|
51
|
+
* Builds the full HTTP proxy URL for a given request.
|
|
52
|
+
* Called once per request (or per retry batch).
|
|
53
|
+
*
|
|
54
|
+
* Must return a URL in the form:
|
|
55
|
+
* http://[encoded-user]:[encoded-pass]@[host]:[port]
|
|
56
|
+
*/
|
|
57
|
+
buildProxyUrl(credentials: ProxyCredentials, params: ProxyRequestParams): string;
|
|
58
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const VERSION = "1.8.2";
|
|
2
|
+
export declare const NPM_PACKAGE = "novada-proxy-mcp";
|
|
3
|
+
export declare const NOVADA_SEARCH_URL = "https://scraperapi.novada.com/search";
|
|
4
|
+
export declare const DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
|
package/build/config.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const VERSION = "1.8.2";
|
|
2
|
+
// npm package name — used in CLI help and error messages
|
|
3
|
+
export const NPM_PACKAGE = "novada-proxy-mcp";
|
|
4
|
+
// Novada Scraper API — web search
|
|
5
|
+
export const NOVADA_SEARCH_URL = "https://scraperapi.novada.com/search";
|
|
6
|
+
// Default User-Agent for all HTTP requests through the proxy
|
|
7
|
+
export const DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
|
package/build/errors.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
export function classifyError(err) {
|
|
3
|
+
const ax = axios.isAxiosError(err);
|
|
4
|
+
const status = ax ? err.response?.status : undefined;
|
|
5
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6
|
+
if (ax && status === 429)
|
|
7
|
+
return { ok: false, error: {
|
|
8
|
+
code: "RATE_LIMITED", message: "HTTP 429 — rate limited",
|
|
9
|
+
recoverable: true,
|
|
10
|
+
agent_instruction: "Wait 5 seconds and retry. Consider reducing request frequency.",
|
|
11
|
+
retry_after_seconds: 5
|
|
12
|
+
} };
|
|
13
|
+
if (ax && status && status >= 400 && status < 500)
|
|
14
|
+
return { ok: false, error: {
|
|
15
|
+
code: "BOT_DETECTION_SUSPECTED",
|
|
16
|
+
message: `HTTP ${status} — request blocked by target`,
|
|
17
|
+
recoverable: true,
|
|
18
|
+
agent_instruction: "Try novada_proxy_render (real browser). Or retry with a different country/session_id. If render also returns 4xx, the page may genuinely not exist — stop retrying."
|
|
19
|
+
} };
|
|
20
|
+
if (msg.includes("timeout") || msg.includes("ECONNABORTED"))
|
|
21
|
+
return { ok: false, error: {
|
|
22
|
+
code: "TIMEOUT", message: "Request timed out",
|
|
23
|
+
recoverable: true,
|
|
24
|
+
agent_instruction: "Increase the timeout parameter or retry. For JS-heavy pages, use novada_proxy_render.",
|
|
25
|
+
retry_after_seconds: 2
|
|
26
|
+
} };
|
|
27
|
+
if (msg.includes("ENOTFOUND") || msg.includes("EAI_AGAIN") || msg.includes("getaddrinfo"))
|
|
28
|
+
return { ok: false, error: {
|
|
29
|
+
code: "NETWORK_ERROR", message: "DNS resolution failed — hostname not found",
|
|
30
|
+
recoverable: false,
|
|
31
|
+
agent_instruction: "The hostname could not be resolved. Verify the URL is correct and the domain exists.",
|
|
32
|
+
} };
|
|
33
|
+
if (msg.includes("TLS") || msg.includes("SSL") || msg.includes("socket disconnect") || msg.includes("secure TLS") || msg.includes("certificate") || msg.includes("issuer cert"))
|
|
34
|
+
return { ok: false, error: {
|
|
35
|
+
code: "TLS_ERROR", message: "TLS/SSL connection failed",
|
|
36
|
+
recoverable: true,
|
|
37
|
+
agent_instruction: "The target rejected the proxy connection. Retry with a different country parameter or use novada_proxy_render. If this is an unrecognized domain, verify it exists — proxy-side DNS failures may appear as TLS errors.",
|
|
38
|
+
retry_after_seconds: 2
|
|
39
|
+
} };
|
|
40
|
+
if (msg.includes("No proxy provider") || msg.includes("not configured"))
|
|
41
|
+
return { ok: false, error: {
|
|
42
|
+
code: "PROVIDER_NOT_CONFIGURED", message: msg,
|
|
43
|
+
recoverable: false,
|
|
44
|
+
agent_instruction: "Set NOVADA_PROXY_USER and NOVADA_PROXY_PASS env vars and restart the MCP server."
|
|
45
|
+
} };
|
|
46
|
+
const INPUT_ERROR_PHRASES = ["is required", "must be", "must start with", "must contain", "letters, numbers", "max 64", "max 50", "between 1 and"];
|
|
47
|
+
if (INPUT_ERROR_PHRASES.some(p => msg.includes(p)))
|
|
48
|
+
return { ok: false, error: {
|
|
49
|
+
code: "INVALID_INPUT", message: msg,
|
|
50
|
+
recoverable: false,
|
|
51
|
+
agent_instruction: "Fix the input parameters and retry. Check the tool's inputSchema for valid values."
|
|
52
|
+
} };
|
|
53
|
+
return { ok: false, error: {
|
|
54
|
+
code: "UNKNOWN_ERROR", message: msg,
|
|
55
|
+
recoverable: true,
|
|
56
|
+
agent_instruction: "Retry the request. Check novada_proxy_status for network health."
|
|
57
|
+
} };
|
|
58
|
+
}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type { ProxyErrorCode, QuotaMeta, ProxySuccessResponse, ProxyErrorResponse, ProxyResponse, } from "./types.js";
|
|
2
|
+
export { VERSION, NPM_PACKAGE } from "./config.js";
|
|
3
|
+
export { classifyError } from "./errors.js";
|
|
4
|
+
export type { ProxyAdapter, ProxyCredentials, ProxyRequestParams, AdapterCapabilities, } from "./adapters/index.js";
|
|
5
|
+
export { resolveAdapter, listAdapters } from "./adapters/index.js";
|
|
6
|
+
export type { ResolvedAdapter } from "./adapters/index.js";
|
|
7
|
+
export { novadaProxyFetch, validateFetchParams, getCacheTtl, makeCacheKey, clearResponseCache, } from "./tools/fetch.js";
|
|
8
|
+
export type { FetchParams } from "./tools/fetch.js";
|
|
9
|
+
export { novadaProxyBatchFetch, validateBatchFetchParams } from "./tools/batch.js";
|
|
10
|
+
export type { BatchFetchParams, BatchFetchResult } from "./tools/batch.js";
|
|
11
|
+
export { novadaProxySearch, validateSearchParams } from "./tools/search.js";
|
|
12
|
+
export type { SearchParams } from "./tools/search.js";
|
|
13
|
+
export { novadaProxySession, validateSessionParams } from "./tools/session.js";
|
|
14
|
+
export type { SessionParams } from "./tools/session.js";
|
|
15
|
+
export { novadaProxyStatus } from "./tools/status.js";
|
|
16
|
+
export { novadaProxyRender, validateRenderParams } from "./tools/render.js";
|
|
17
|
+
export type { RenderParams } from "./tools/render.js";
|
|
18
|
+
export { novadaProxyExtract, validateExtractParams, extractField, deepFind, shouldEscalateToRender, } from "./tools/extract.js";
|
|
19
|
+
export type { ExtractParams } from "./tools/extract.js";
|
|
20
|
+
export { novadaProxyMap, validateMapParams } from "./tools/map.js";
|
|
21
|
+
export type { MapParams } from "./tools/map.js";
|
|
22
|
+
export { novadaProxyCrawl, validateCrawlParams } from "./tools/crawl.js";
|
|
23
|
+
export type { CrawlParams, CrawlPageResult } from "./tools/crawl.js";
|
|
24
|
+
export { novadaProxyResearch, validateResearchParams } from "./tools/research.js";
|
|
25
|
+
export type { ResearchParams } from "./tools/research.js";
|
|
26
|
+
export { unicodeSafeTruncate, decodeHtmlEntities, htmlToMarkdown, htmlToText, stripNoiseElements, countHtmlTags, contentDensity, } from "./utils.js";
|
|
27
|
+
export { SAFE_COUNTRY, SAFE_CITY, SAFE_SESSION_ID, QUOTA_NOTE } from "./validation.js";
|
|
28
|
+
export { redactCredentials } from "./redact.js";
|