@aluvia/sdk 1.0.0 → 1.3.0

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.
Files changed (77) hide show
  1. package/README.md +425 -256
  2. package/dist/cjs/api/account.js +10 -74
  3. package/dist/cjs/api/apiUtils.js +80 -0
  4. package/dist/cjs/api/geos.js +2 -63
  5. package/dist/cjs/api/request.js +8 -2
  6. package/dist/cjs/bin/account.js +31 -0
  7. package/dist/cjs/bin/api-helpers.js +58 -0
  8. package/dist/cjs/bin/cli.js +245 -0
  9. package/dist/cjs/bin/close.js +120 -0
  10. package/dist/cjs/bin/geos.js +10 -0
  11. package/dist/cjs/bin/mcp-helpers.js +57 -0
  12. package/dist/cjs/bin/mcp-server.js +220 -0
  13. package/dist/cjs/bin/mcp-tools.js +90 -0
  14. package/dist/cjs/bin/open.js +293 -0
  15. package/dist/cjs/bin/session.js +259 -0
  16. package/dist/cjs/client/AluviaClient.js +411 -150
  17. package/dist/cjs/client/BlockDetection.js +486 -0
  18. package/dist/cjs/client/ConfigManager.js +26 -23
  19. package/dist/cjs/client/PageLoadDetection.js +175 -0
  20. package/dist/cjs/client/ProxyServer.js +4 -2
  21. package/dist/cjs/client/logger.js +4 -0
  22. package/dist/cjs/client/rules.js +38 -49
  23. package/dist/cjs/connect.js +117 -0
  24. package/dist/cjs/errors.js +12 -1
  25. package/dist/cjs/index.js +5 -1
  26. package/dist/cjs/session/lock.js +186 -0
  27. package/dist/esm/api/account.js +2 -66
  28. package/dist/esm/api/apiUtils.js +71 -0
  29. package/dist/esm/api/geos.js +2 -63
  30. package/dist/esm/api/request.js +8 -2
  31. package/dist/esm/bin/account.js +28 -0
  32. package/dist/esm/bin/api-helpers.js +53 -0
  33. package/dist/esm/bin/cli.js +242 -0
  34. package/dist/esm/bin/close.js +117 -0
  35. package/dist/esm/bin/geos.js +7 -0
  36. package/dist/esm/bin/mcp-helpers.js +51 -0
  37. package/dist/esm/bin/mcp-server.js +185 -0
  38. package/dist/esm/bin/mcp-tools.js +78 -0
  39. package/dist/esm/bin/open.js +256 -0
  40. package/dist/esm/bin/session.js +252 -0
  41. package/dist/esm/client/AluviaClient.js +384 -156
  42. package/dist/esm/client/BlockDetection.js +482 -0
  43. package/dist/esm/client/ConfigManager.js +21 -18
  44. package/dist/esm/client/PageLoadDetection.js +171 -0
  45. package/dist/esm/client/ProxyServer.js +5 -3
  46. package/dist/esm/client/logger.js +4 -0
  47. package/dist/esm/client/rules.js +36 -49
  48. package/dist/esm/connect.js +81 -0
  49. package/dist/esm/errors.js +10 -0
  50. package/dist/esm/index.js +5 -3
  51. package/dist/esm/session/lock.js +142 -0
  52. package/dist/types/api/AluviaApi.d.ts +2 -7
  53. package/dist/types/api/account.d.ts +1 -16
  54. package/dist/types/api/apiUtils.d.ts +28 -0
  55. package/dist/types/api/geos.d.ts +1 -1
  56. package/dist/types/bin/account.d.ts +1 -0
  57. package/dist/types/bin/api-helpers.d.ts +20 -0
  58. package/dist/types/bin/cli.d.ts +2 -0
  59. package/dist/types/bin/close.d.ts +1 -0
  60. package/dist/types/bin/geos.d.ts +1 -0
  61. package/dist/types/bin/mcp-helpers.d.ts +28 -0
  62. package/dist/types/bin/mcp-server.d.ts +2 -0
  63. package/dist/types/bin/mcp-tools.d.ts +46 -0
  64. package/dist/types/bin/open.d.ts +21 -0
  65. package/dist/types/bin/session.d.ts +11 -0
  66. package/dist/types/client/AluviaClient.d.ts +51 -4
  67. package/dist/types/client/BlockDetection.d.ts +96 -0
  68. package/dist/types/client/ConfigManager.d.ts +6 -1
  69. package/dist/types/client/PageLoadDetection.d.ts +93 -0
  70. package/dist/types/client/logger.d.ts +2 -0
  71. package/dist/types/client/rules.d.ts +18 -0
  72. package/dist/types/client/types.d.ts +69 -46
  73. package/dist/types/connect.d.ts +18 -0
  74. package/dist/types/errors.d.ts +6 -0
  75. package/dist/types/index.d.ts +7 -5
  76. package/dist/types/session/lock.d.ts +43 -0
  77. package/package.json +11 -2
package/README.md CHANGED
@@ -5,419 +5,588 @@
5
5
  [![license](https://img.shields.io/npm/l/@aluvia/sdk.svg)](./LICENSE)
6
6
  [![node](https://img.shields.io/node/v/@aluvia/sdk.svg)](./package.json)
7
7
 
8
- ## Introduction
8
+ **Stop getting blocked.** Aluvia routes your AI agent's web traffic through premium US mobile carrier IPs — the same IPs used by real people on their phones. Websites trust them, so your agent stops hitting 403s, CAPTCHAs, and rate limits.
9
9
 
10
- AI agents require reliable web access, yet they often encounter 403 blocks, CAPTCHAs, and rate limits. Real humans don't live in datacenters, so websites often treat agent coming from datacenter/cloud IPs as suspicious.
10
+ This SDK gives you everything you need:
11
11
 
12
- **Aluvia solves this problem** by connecting agents to the web through premium mobile IPs on US carrier networks. Unlike datacenter IPs, these reputable IPs are used by real humans, and they don’t get blocked by websites.
12
+ - **CLI for browser automation** launch headless Chromium sessions from the command line, with JSON output designed for AI agent frameworks
13
+ - **Automatic block detection and unblocking** — the SDK detects 403s, WAF challenges, and CAPTCHAs, then reroutes through Aluvia and reloads the page automatically
14
+ - **Smart routing** — proxy only the sites that block you; everything else goes direct to save cost and latency
15
+ - **Runtime rule updates** — add hostnames to proxy rules on the fly, no restarts or redeployments
16
+ - **Adapters for every tool** — Playwright, Puppeteer, Selenium, Axios, got, and Node's fetch
17
+ - **IP rotation and geo targeting** — rotate IPs or target specific US regions at runtime
18
+ - **REST API wrapper** — manage connections, check usage, and build custom tooling with `AluviaApi`
13
19
 
14
- **This Node.js SDK** makes it simple to integrate Aluvia into your agent workflow. There are two key components:
15
- 1. `AluviaClient` - a local client for connecting to Aluvia.
16
- 2. `AluviaApi` - a lightweight JavaScript/TypeScript wrapper for the Aluvia REST API.
20
+ ---
21
+
22
+ ## Table of contents
23
+
24
+ - [Quick start](#quick-start)
25
+ - [CLI reference](#cli-reference)
26
+ - [Connecting to a running browser](#connecting-to-a-running-browser)
27
+ - [Programmatic usage (AluviaClient)](#programmatic-usage)
28
+ - [Routing rules](#routing-rules)
29
+ - [Block detection and auto-unblocking](#block-detection-and-auto-unblocking)
30
+ - [Tool integration adapters](#tool-integration-adapters)
31
+ - [REST API (AluviaApi)](#rest-api)
32
+ - [Architecture](#architecture)
17
33
 
18
34
  ---
19
35
 
20
- ## Aluvia client
36
+ ## Quick start
21
37
 
22
- The Aluvia client runs a local rules-based proxy server on your agent's host, handles authentication and connection management, and provides ready-to-use adapters for popular tools like Playwright, Puppeteer, and Axios.
38
+ ### 1. Get Aluvia API key
23
39
 
24
- Simply point your automation tool at the local proxy address (`127.0.0.1`) and the client handles the rest. For each request, the client checks the destination hostname against user-defined (or agent-defined) routing rules and decides whether to send it through Aluvia's mobile IPs or direct to the destination.
40
+ [Aluvia dashboard](https://dashboard.aluvia.io)
25
41
 
26
- ```
27
- ┌──────────────────┐ ┌──────────────────────────┐ ┌──────────────────────┐
28
- │ │ │ │ │ │
29
- │ Your Agent │─────▶ Aluvia Client ─────▶ gateway.aluvia.io │
30
- │ │ │ 127.0.0.1:port │ │ (Mobile IPs) │
31
- │ │ │ │ │ │
32
- └──────────────────┘ │ Per-request routing: │ └──────────────────────┘
33
- │ │
34
- │ not-blocked.com ──────────────▶ Direct
35
- │ blocked-site.com ─────────────▶ Via Aluvia
36
- │ │
37
- └──────────────────────────┘
42
+ ### 2. Install
43
+
44
+ ```bash
45
+ npm install @aluvia/sdk playwright
46
+ export ALUVIA_API_KEY="your-api-key"
38
47
  ```
39
48
 
49
+ ### 3. Run
40
50
 
41
- **Benefits:**
42
- - **Avoid blocks:** Websites flag datacenter IPs as bot traffic, leading to 403s, CAPTCHAs, and rate limits. Mobile IPs appear as real users, so requests go through.
43
- - **Reduce costs and latency:** Hostname-based routing rules let you proxy only the sites that need it. Traffic to non-blocked sites goes direct, saving money and reducing latency.
44
- - **Unblock without restarts:** Rules update at runtime. When a site blocks your agent, add it to the proxy rules and retry—no need to restart workers or redeploy.
45
- - **Simplify integration:** One SDK with ready-to-use adapters for Playwright, Puppeteer, Selenium, Axios, got, and Node's fetch.
51
+ Aluvia automatically detects website blocks and uses mobile IPs when necessary.
46
52
 
53
+ ```bash
54
+ aluvia session start https://example.com --auto-unblock --run your-script.js
55
+ ```
47
56
 
48
57
  ---
49
58
 
50
- ## Quick start
59
+ ## Skills
60
+
61
+ - Claude code skll
62
+ - OpenClaw skill
51
63
 
52
- ### Understand the basics
53
- * [What is Aluvia?](https://docs.aluvia.io/)
54
- * [Understanding connections](https://docs.aluvia.io/fundamentals/connections)
64
+ ---
55
65
 
56
- ### Get Aluvia API key
66
+ ## CLI reference
57
67
 
58
- 1. Create an account at [dashboard.aluvia.io](https://dashboard.aluvia.io)
59
- 2. Go to **API and SDKs** and get your **API Key**
68
+ The CLI outputs JSON for easy integration with AI agent frameworks. All commands are available via the `aluvia` binary. Run `aluvia help --json` for machine-readable help.
60
69
 
61
- ### Install the SDK
70
+ ### `session start` — Launch a browser session
62
71
 
63
72
  ```bash
64
- npm install @aluvia/sdk
73
+ aluvia session start <url> [options]
65
74
  ```
66
75
 
67
- **Requirements:** Node.js 18 or later
76
+ | Option | Description |
77
+ | --------------------------- | ------------------------------------------------------------------------ |
78
+ | `--auto-unblock` | Auto-detect blocks and reload through Aluvia |
79
+ | `--run <script>` | Run a script with `page`, `browser`, `context` injected; exits when done |
80
+ | `--headful` | Show the browser window (default: headless) |
81
+ | `--browser-session <name>` | Name this session (auto-generated if omitted, e.g. `swift-falcon`) |
82
+ | `--connection-id <id>` | Reuse an existing Aluvia connection |
83
+ | `--disable-block-detection` | Disable block detection entirely |
68
84
 
69
- ### Example: Dynamic unblocking with Playwright
85
+ **Examples:**
70
86
 
71
- This example shows how an agent can use the Aluvia client to dynamically unblock websites. It demonstrates starting the client, using the Playwright integration adapter, configuring geo targeting and session ID, detecting blocks, and updating routing rules on the fly.
87
+ ```bash
88
+ # Launch with auto-unblocking
89
+ aluvia session start https://example.com --auto-unblock
72
90
 
73
- ```ts
74
- import { chromium } from 'playwright';
75
- import { AluviaClient } from '@aluvia/sdk';
91
+ # Run a script inline
92
+ aluvia session start https://example.com --auto-unblock --run scrape.mjs
76
93
 
77
- // Initialize the Aluvia client with your API key
78
- const client = new AluviaClient({ apiKey: process.env.ALUVIA_API_KEY! });
94
+ # Debug with a visible browser window
95
+ aluvia session start https://example.com --headful
79
96
 
80
- // Start the client (launches local proxy, fetches connection config)
81
- const connection = await client.start();
97
+ # Reuse an existing connection
98
+ aluvia session start https://example.com --connection-id 3449
99
+ ```
82
100
 
83
- // Configure geo targeting (use California IPs)
84
- await client.updateTargetGeo('us_ca');
101
+ ### `session close` Stop a session
85
102
 
86
- // Set session ID (requests with the same session ID use the same IP)
87
- await client.updateSessionId('agentsession1');
103
+ ```bash
104
+ aluvia session close # auto-selects if only one is running
105
+ aluvia session close --browser-session swift-falcon # close by name
106
+ aluvia session close --all # close all sessions
107
+ ```
88
108
 
89
- // Launch browser using the Playwright integration adapter
90
- // The adapter returns proxy settings in Playwright's expected format
91
- const browser = await chromium.launch({ proxy: connection.asPlaywright() });
109
+ ### `session list` List active sessions
92
110
 
93
- // Track hostnames we've added to proxy rules
94
- const proxiedHosts = new Set<string>();
111
+ ```bash
112
+ aluvia session list
113
+ ```
95
114
 
96
- async function visitWithRetry(url: string): Promise<string> {
97
- const page = await browser.newPage();
115
+ ```json
116
+ {
117
+ "sessions": [
118
+ {
119
+ "browserSession": "swift-falcon",
120
+ "pid": 12345,
121
+ "startUrl": "https://example.com",
122
+ "cdpUrl": "http://127.0.0.1:38209",
123
+ "connectionId": 3449,
124
+ "blockDetection": true,
125
+ "autoUnblock": true
126
+ }
127
+ ],
128
+ "count": 1
129
+ }
130
+ ```
98
131
 
99
- try {
100
- const response = await page.goto(url, { waitUntil: 'domcontentloaded' });
101
- const hostname = new URL(url).hostname;
132
+ ### `session get` — Full session details
102
133
 
103
- // Detect if the site blocked us (403, 429, or challenge page)
104
- const status = response?.status() ?? 0;
105
- const isBlocked =
106
- status === 403 ||
107
- status === 429 ||
108
- (await page.title()).toLowerCase().includes('blocked');
134
+ ```bash
135
+ aluvia session get [--browser-session <name>]
136
+ ```
109
137
 
110
- if (isBlocked && !proxiedHosts.has(hostname)) {
111
- console.log(`Blocked by ${hostname} — adding to proxy rules`);
138
+ Returns session info enriched with block detection history and the full connection object from the API.
112
139
 
113
- // Update routing rules to proxy this hostname through Aluvia
114
- // Rules update at runtime—no need to restart the browser
115
- proxiedHosts.add(hostname);
116
- await client.updateRules([...proxiedHosts]);
140
+ ### `session rotate-ip` Get a new IP
117
141
 
118
- // Rotate to a fresh IP by changing the session ID
119
- await client.updateSessionId(`retry-${Date.now()}`);
142
+ ```bash
143
+ aluvia session rotate-ip [--browser-session <name>]
144
+ ```
120
145
 
121
- await page.close();
122
- return visitWithRetry(url);
123
- }
146
+ ### `session set-geo` — Target a specific region
124
147
 
125
- return await page.content();
126
- } finally {
127
- await page.close();
128
- }
129
- }
148
+ ```bash
149
+ aluvia session set-geo US # target US IPs
150
+ aluvia session set-geo us_ca # target California
151
+ aluvia session set-geo --clear # clear geo targeting
152
+ ```
130
153
 
131
- try {
132
- // First attempt goes direct; if blocked, retries through Aluvia
133
- const html = await visitWithRetry('https://example.com/data');
134
- console.log('Success:', html.slice(0, 200));
135
- } finally {
136
- // Always close the browser and connection when done
137
- await browser.close();
138
- await connection.close();
139
- }
154
+ ### `session set-rules` — Update routing rules
155
+
156
+ ```bash
157
+ aluvia session set-rules "example.com,api.example.com" # add rules
158
+ aluvia session set-rules --remove "example.com" # remove rules
140
159
  ```
141
160
 
142
- ### Integration guides
161
+ Rules are comma-separated. By default rules are appended; use `--remove` to remove specific rules.
143
162
 
144
- The Aluvia client provides ready-to-use adapters for popular automation and HTTP tools:
163
+ ### Account and other commands
145
164
 
146
- - [Playwright](docs/integrations/integration-playwright.md)
147
- - [Puppeteer](docs/integrations/integration-puppeteer.md)
148
- - [Selenium](docs/integrations/integration-selenium.md)
149
- - [Axios](docs/integrations/integration-axios.md)
150
- - [got](docs/integrations/integration-got.md)
151
- - [fetch (Node 18+)](docs/integrations/integration-fetch.md)
165
+ ```bash
166
+ aluvia account # account info
167
+ aluvia account usage # usage stats
168
+ aluvia account usage --start 2025-01-01T00:00:00Z --end 2025-02-01T00:00:00Z
169
+
170
+ aluvia geos # list available geo-targeting options
171
+ aluvia help # plain text help
172
+ aluvia help --json # machine-readable help
173
+ ```
152
174
 
153
175
  ---
154
176
 
177
+ ## Connecting to a running browser
155
178
 
179
+ There are two ways to run code against a browser session started by the CLI.
156
180
 
157
- ## Architecture
181
+ ### Option A: `--run` (simplest)
158
182
 
159
- The client is split into two independent **planes**:
183
+ Pass a script to `session start`. The globals `page`, `browser`, and `context` are available — no imports needed:
160
184
 
185
+ ```bash
186
+ aluvia session start https://example.com --auto-unblock --run script.mjs
161
187
  ```
162
- ┌─────────────────────────────────────────────────────────────────┐
163
- │ AluviaClient │
164
- ├─────────────────────────────┬───────────────────────────────────┤
165
- │ Control Plane │ Data Plane │
166
- │ (ConfigManager) │ (ProxyServer) │
167
- ├─────────────────────────────┼───────────────────────────────────┤
168
- • Fetches/creates config │ • Local HTTP proxy (proxy-chain)
169
- Polls for updates (ETag) │ • Per-request routing decisions │
170
- │ • PATCH updates (rules, │ • Uses rules engine to decide: │
171
- │ session, geo) │ direct vs gateway │
172
- └─────────────────────────────┴───────────────────────────────────┘
188
+
189
+ ```js
190
+ // script.mjs
191
+ console.log("URL:", page.url());
192
+
193
+ const newPage = await context.newPage();
194
+ await newPage.goto("https://another-site.com");
195
+ console.log("Other site title:", await newPage.title());
173
196
  ```
174
197
 
175
- ### Control Plane (ConfigManager)
198
+ The session starts, runs your script, and exits.
176
199
 
177
- - Communicates with the Aluvia REST API (`/account/connections/...`)
178
- - Fetches proxy credentials and routing rules
179
- - Polls for configuration updates
180
- - Pushes updates (rules, session ID, geo)
200
+ ### Option B: `connect()` (for AI agents and long-running processes)
181
201
 
182
- ### Data Plane (ProxyServer)
202
+ Start a session as a background daemon, then connect from your application:
183
203
 
184
- - Runs a local HTTP proxy on `127.0.0.1`
185
- - For each request, uses the **rules engine** to decide whether to route direct or via Aluvia.
186
- - Because the proxy reads the latest config per-request, rule updates take effect immediately
204
+ ```bash
205
+ aluvia session start https://example.com --auto-unblock
206
+ ```
187
207
 
188
- ---
208
+ ```ts
209
+ import { connect } from "@aluvia/sdk";
189
210
 
190
- ## Operating modes
211
+ // Auto-discovers the running session
212
+ const { page, browser, context, disconnect } = await connect();
213
+ console.log("URL:", page.url());
191
214
 
192
- The Aluvia client has two operating modes: **Client Proxy Mode** (default) and **Gateway Mode**.
215
+ // When running multiple sessions, specify by name
216
+ const conn = await connect("swift-falcon");
217
+ console.log("URL:", conn.page.url());
193
218
 
194
- ### Client Proxy Mode
219
+ // Disconnect when done (the session keeps running)
220
+ await disconnect();
221
+ ```
195
222
 
196
- **How it works:** The SDK runs a local proxy on `127.0.0.1`. For each request, it checks your routing rules and sends traffic either direct or through Aluvia.
223
+ Use this when your agent generates automation code dynamically at runtime or needs a persistent browser across multiple operations.
197
224
 
198
- **Why use it:**
199
- - Selective routing reduces cost and latency (only proxy what you need)
200
- - Credentials stay inside the SDK (nothing secret in your config)
201
- - Rule changes apply immediately (no restarts)
225
+ ---
202
226
 
203
- **Best for:** Using per-hostname routing rules.
227
+ ## Programmatic usage
204
228
 
205
- ### Gateway Mode
229
+ For full control, use `AluviaClient` directly instead of the CLI.
206
230
 
207
- Set `localProxy: false` to enable.
231
+ ### Basic example
208
232
 
209
- **How it works:** No local proxy. Your tools connect directly to `gateway.aluvia.io` and **ALL** traffic goes through Aluvia.
233
+ ```ts
234
+ import { AluviaClient } from "@aluvia/sdk";
235
+ import { chromium } from "playwright";
210
236
 
211
- **Why use it:**
212
- - No local process to manage
213
- - Simpler setup for tools with native proxy auth support
237
+ const client = new AluviaClient({
238
+ apiKey: process.env.ALUVIA_API_KEY!,
239
+ });
214
240
 
215
- **Best for:** When you want all traffic proxied without selective routing.
241
+ const connection = await client.start();
242
+ const browser = await chromium.launch({ proxy: connection.asPlaywright() });
243
+ const page = await browser.newPage();
244
+ await page.goto("https://example.com");
216
245
 
217
- ---
246
+ // ... do your work ...
218
247
 
219
- ## Using Aluvia client
248
+ await browser.close();
249
+ await connection.close();
250
+ ```
220
251
 
221
- ### 1. Create a client
252
+ ### With auto-launched browser
222
253
 
223
254
  ```ts
224
255
  const client = new AluviaClient({
225
256
  apiKey: process.env.ALUVIA_API_KEY!,
226
- connectionId: 123, // Optional: reuse an existing connection
227
- localProxy: true, // Optional: default true (recommended)
257
+ startPlaywright: true,
258
+ blockDetection: {
259
+ enabled: true,
260
+ autoUnblock: true,
261
+ },
228
262
  });
263
+
264
+ const connection = await client.start();
265
+ const page = await connection.browser.newPage();
266
+ await page.goto("https://example.com"); // auto-reloads through Aluvia if blocked
267
+
268
+ await connection.close(); // stops proxy, closes browser, releases resources
229
269
  ```
230
270
 
231
- For all options, see the [Client Technical Guide](docs/client-technical-guide.md#constructor-options).
271
+ ### Runtime updates
232
272
 
233
- ### 2. Start the client and get a connection
273
+ While your agent is running, update routing, rotate IPs, or change geo — no restarts needed:
234
274
 
235
275
  ```ts
236
- const connection = await client.start();
276
+ await client.updateRules(["blocked-site.com"]); // proxy this hostname
277
+ await client.updateSessionId("new-session-id"); // rotate to a new IP
278
+ await client.updateTargetGeo("us_ca"); // target California IPs
237
279
  ```
238
280
 
239
- This starts the local proxy and returns a connection object you'll use with your tools.
240
- [Understanding the connection object](https://docs.aluvia.io/fundamentals/connections)
241
-
242
- ### 3. Use the connection with your tools
243
-
244
- Pass the connection to your automation tool using the appropriate adapter:
281
+ ### Constructor options
245
282
 
246
283
  ```ts
247
- const browser = await chromium.launch({ proxy: connection.asPlaywright() });
284
+ new AluviaClient({
285
+ apiKey: string; // Required
286
+ connectionId?: number; // Reuse an existing connection
287
+ startPlaywright?: boolean; // Auto-launch Chromium browser
288
+ headless?: boolean; // Default: true (only with startPlaywright)
289
+ blockDetection?: BlockDetectionConfig; // See "Block detection" section
290
+ localPort?: number; // Local proxy port (auto-assigned if omitted)
291
+ gatewayProtocol?: "http" | "https"; // Default: "http"
292
+ gatewayPort?: number; // Default: 8080 (http) or 8443 (https)
293
+ pollIntervalMs?: number; // Config poll interval (default: 5000ms)
294
+ timeoutMs?: number; // API request timeout
295
+ logLevel?: "silent" | "info" | "debug";
296
+ strict?: boolean; // Throw if config fails to load (default: true)
297
+ apiBaseUrl?: string; // Default: "https://api.aluvia.io/v1"
298
+ });
248
299
  ```
249
300
 
250
- ### 4. Update routing as necessary
301
+ For all options in detail, see the [Client Technical Guide](docs/client-technical-guide.md#constructor-options).
302
+
303
+ ---
304
+
305
+ ## Routing rules
306
+
307
+ The local proxy routes each request based on hostname rules. Only hostnames matching a rule go through Aluvia; everything else goes direct.
251
308
 
252
- While your agent is running, you can update routing rules, rotate IPs, or change geo targeting—no restart needed:
309
+ ### Why this matters
310
+
311
+ - **Save money** — proxy only the sites that block you
312
+ - **Lower latency** — non-blocked sites skip the proxy entirely
313
+ - **Adapt on the fly** — rules update at runtime, no restarts needed
314
+
315
+ ### Rule patterns
316
+
317
+ | Pattern | Matches |
318
+ | --------------- | ------------------------------ |
319
+ | `*` | All hostnames |
320
+ | `example.com` | Exact match |
321
+ | `*.example.com` | Subdomains of example.com |
322
+ | `google.*` | google.com, google.co.uk, etc. |
323
+ | `-example.com` | Exclude from proxying |
324
+
325
+ ### Examples
253
326
 
254
327
  ```ts
255
- await client.updateRules(['blocked-site.com']); // Add hostname to proxy rules
256
- await client.updateSessionId('newsession'); // Rotate to a new IP
257
- await client.updateTargetGeo('us_ca'); // Target California IPs
328
+ // Proxy all traffic
329
+ await client.updateRules(["*"]);
330
+
331
+ // Proxy specific hosts only
332
+ await client.updateRules(["target-site.com", "*.google.com"]);
333
+
334
+ // Proxy everything except Stripe
335
+ await client.updateRules(["*", "-api.stripe.com"]);
336
+
337
+ // Route all traffic direct (no proxy)
338
+ await client.updateRules([]);
258
339
  ```
259
340
 
260
- ### 5. Clean up when done
341
+ Or from the CLI:
261
342
 
262
- ```ts
263
- await connection.close(); // Stops proxy, polling, and releases resources
343
+ ```bash
344
+ aluvia session set-rules "target-site.com,*.google.com"
345
+ aluvia session set-rules --remove "target-site.com"
264
346
  ```
265
347
 
266
348
  ---
267
349
 
268
- ## Routing rules
350
+ ## Block detection and auto-unblocking
269
351
 
270
- The Aluvia Client starts a local proxy server that routes each request based on hostname rules that you (or our agent) set. **Rules can be updated at runtime without restarting the agent.**
352
+ Most proxy solutions require you to decide upfront which sites to proxy. If a site blocks you later, you're stuck.
353
+
354
+ Aluvia detects blocks automatically and can unblock your agent on the fly. The SDK analyzes every page load using a weighted scoring system across multiple signals — HTTP status codes, WAF headers, CAPTCHA selectors, page content, redirect chains, and more.
355
+
356
+ ### Automatic unblocking (recommended)
357
+
358
+ When a block is detected, the SDK adds the hostname to proxy rules and reloads the page through Aluvia:
359
+
360
+ ```ts
361
+ const client = new AluviaClient({
362
+ apiKey: process.env.ALUVIA_API_KEY!,
363
+ startPlaywright: true,
364
+ blockDetection: {
365
+ enabled: true,
366
+ autoUnblock: true,
367
+ onDetection: (result, page) => {
368
+ console.log(
369
+ `${result.blockStatus} on ${result.hostname} (score: ${result.score})`,
370
+ );
371
+ },
372
+ },
373
+ });
374
+ ```
271
375
 
272
- Traffic can be sent either:
273
- * direct (using the agent's datacenter/cloud IP) or,
274
- * through Aluvia's mobile proxy IPs,
376
+ Or from the CLI:
275
377
 
276
- ### Benefits
378
+ ```bash
379
+ aluvia session start https://example.com --auto-unblock
380
+ ```
277
381
 
278
- * Selectively routing traffic to mobile proxies reduces proxy costs and connection latency.
279
- * Rules can be updated during runtime, allowing agents to work around website blocks on the fly.
382
+ ### Detection-only mode
280
383
 
281
- ### Example rules
384
+ Run detection without automatic remediation. Your agent inspects the results and decides what to do:
282
385
 
283
386
  ```ts
284
- await client.updateRules(['*']); // Proxy all traffic
285
- await client.updateRules(['target-site.com', '*.google.com']); // Proxy specific hosts
286
- await client.updateRules(['*', '-api.stripe.com']); // Proxy all except specified
287
- await client.updateRules([]); // Route all traffic direct
387
+ blockDetection: {
388
+ enabled: true,
389
+ onDetection: (result, page) => {
390
+ if (result.blockStatus === "blocked") {
391
+ // Agent decides: retry, rotate IP, update rules, etc.
392
+ }
393
+ },
394
+ }
288
395
  ```
289
396
 
290
- ### Supported routing rule patterns:
397
+ ### Block status scores
291
398
 
292
- | Pattern | Matches |
293
- |---------|---------|
294
- | `*` | All hostnames |
295
- | `example.com` | Exact match |
296
- | `*.example.com` | Subdomains of example.com |
297
- | `google.*` | google.com, google.co.uk, and similar |
298
- | `-example.com` | Exclude from proxying |
399
+ Each page analysis produces a score from 0.0 to 1.0:
299
400
 
300
- ---
401
+ | Score | Status | Meaning |
402
+ | ------ | ------------- | --------------------------------------------------------------- |
403
+ | >= 0.7 | `"blocked"` | High confidence block. Auto-reloads when `autoUnblock: true`. |
404
+ | >= 0.4 | `"suspected"` | Possible block. Reloads only if `autoUnblockOnSuspected: true`. |
405
+ | < 0.4 | `"clear"` | No block detected. |
406
+
407
+ Scores use probabilistic combination (`1 - product(1 - weight)`) so weak signals don't stack into false positives.
408
+
409
+ ### How detection works
301
410
 
302
- ## Dynamic unblocking
411
+ Detection runs in two passes:
303
412
 
304
- Most proxy solutions require you to decide upfront which sites to proxy. If a site blocks you later, you're stuck—restart your workers, redeploy your fleet, or lose the workflow.
413
+ 1. **Fast pass** (at `domcontentloaded`) checks HTTP status codes and WAF response headers. High-confidence blocks (score >= 0.9) trigger immediate remediation.
414
+ 2. **Full pass** (after `networkidle`) — analyzes page title, visible text, challenge selectors, meta refreshes, and redirect chains.
305
415
 
306
- **With Aluvia, your agent can unblock itself.** When a request fails with a 403 or 429, your agent adds that hostname to its routing rules and retries. The update takes effect immediately—no restart, no redeployment, no lost state.
416
+ The SDK also detects SPA navigations and tracks persistent blocks per hostname to prevent infinite retry loops.
307
417
 
308
- This turns blocking from a workflow-ending failure into a minor speed bump.
418
+ ### Detection config options
419
+
420
+ ```ts
421
+ blockDetection: {
422
+ enabled?: boolean; // Default: true
423
+ autoUnblock?: boolean; // Auto-remediate blocked pages
424
+ autoUnblockOnSuspected?: boolean; // Also remediate "suspected" pages
425
+ challengeSelectors?: string[]; // Custom CSS selectors for challenge detection
426
+ extraKeywords?: string[]; // Additional keywords for text analysis
427
+ extraStatusCodes?: number[]; // Additional HTTP status codes to flag
428
+ networkIdleTimeoutMs?: number; // Default: 3000ms
429
+ onDetection?: (result, page) => void | Promise<void>;
430
+ }
431
+ ```
432
+
433
+ ### Manual detection
434
+
435
+ You can also check responses yourself and update rules on the fly:
309
436
 
310
437
  ```ts
311
438
  const response = await page.goto(url);
312
439
 
313
440
  if (response?.status() === 403) {
314
- // Blocked! Add this hostname to proxy rules and retry
315
441
  await client.updateRules([...currentRules, new URL(url).hostname]);
316
- await page.goto(url); // This request goes through Aluvia
442
+ await page.goto(url); // retried through Aluvia
317
443
  }
318
444
  ```
319
445
 
320
- Your agent learns which sites need proxying as it runs. Sites that don't block you stay direct (faster, cheaper). Sites that do block you get routed through mobile IPs automatically.
446
+ For the full list of signal detectors and weights, see the [Client Technical Guide](docs/client-technical-guide.md#signal-detectors).
321
447
 
322
448
  ---
323
449
 
324
450
  ## Tool integration adapters
325
451
 
326
- Every tool has its own way of configuring proxies—Playwright wants { server, username, password }, Puppeteer wants CLI args, Axios wants agents, and Node's native fetch doesn't support proxies at all. The SDK handles all of this for you:
452
+ The SDK handles proxy configuration for every major tool:
453
+
454
+ | Tool | Method | Returns |
455
+ | ------------ | ---------------------------- | ----------------------------------------------------- |
456
+ | Playwright | `connection.asPlaywright()` | `{ server, username?, password? }` |
457
+ | Playwright | `connection.browser` | Auto-launched Chromium (with `startPlaywright: true`) |
458
+ | Playwright | `connection.cdpUrl` | CDP endpoint for `connectOverCDP()` |
459
+ | Puppeteer | `connection.asPuppeteer()` | `['--proxy-server=...']` |
460
+ | Selenium | `connection.asSelenium()` | `'--proxy-server=...'` |
461
+ | Axios | `connection.asAxiosConfig()` | `{ proxy: false, httpAgent, httpsAgent }` |
462
+ | got | `connection.asGotOptions()` | `{ agent: { http, https } }` |
463
+ | fetch | `connection.asUndiciFetch()` | Proxy-enabled `fetch` function |
464
+ | Node.js http | `connection.asNodeAgents()` | `{ http: Agent, https: Agent }` |
465
+
466
+ ### Examples
467
+
468
+ ```ts
469
+ // Playwright
470
+ const browser = await chromium.launch({ proxy: connection.asPlaywright() });
471
+
472
+ // Puppeteer
473
+ const browser = await puppeteer.launch({ args: connection.asPuppeteer() });
327
474
 
328
- | Tool | Method | Returns |
329
- |------|--------|---------|
330
- | Playwright | `connection.asPlaywright()` | `{ server, username?, password? }` |
331
- | Puppeteer | `connection.asPuppeteer()` | `['--proxy-server=...']` |
332
- | Selenium | `connection.asSelenium()` | `'--proxy-server=...'` |
333
- | Axios | `connection.asAxiosConfig()` | `{ proxy: false, httpAgent, httpsAgent }` |
334
- | got | `connection.asGotOptions()` | `{ agent: { http, https } }` |
335
- | fetch | `connection.asUndiciFetch()` | Proxy-enabled `fetch` function |
336
- | Node.js http | `connection.asNodeAgents()` | `{ http: Agent, https: Agent }` |
475
+ // Axios
476
+ const axiosClient = axios.create(connection.asAxiosConfig());
477
+ await axiosClient.get("https://example.com");
337
478
 
338
- For more details regarding integration adapters, see the [Client Technical Guide](docs/client-technical-guide.md#tool-adapters).
479
+ // got
480
+ const gotClient = got.extend(connection.asGotOptions());
481
+ await gotClient("https://example.com");
339
482
 
483
+ // Node's fetch (via undici)
484
+ const myFetch = connection.asUndiciFetch();
485
+ await myFetch("https://example.com");
486
+ ```
340
487
 
488
+ For more details, see the [Client Technical Guide](docs/client-technical-guide.md#tool-adapters).
341
489
 
342
490
  ---
343
491
 
344
- ## Aluvia API
345
-
346
- `AluviaApi` is a typed wrapper for the Aluvia REST API. Use it to manage connections, query account info, or build custom tooling—without starting a proxy.
347
-
348
- `AluviaApi` is built from modular layers:
349
-
350
- ```
351
- ┌───────────────────────────────────────────────────────────────┐
352
- │ AluviaApi │
353
- │ Constructor validates apiKey, creates namespace objects │
354
- ├───────────────────────────────────────────────────────────────┤
355
- │ │
356
- │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
357
- │ │ account │ │ geos │ │ request │
358
- │ │ namespace │ │ namespace │ │ (escape │ │
359
- │ │ │ │ │ │ hatch) │ │
360
- │ └─────────────┘ └─────────────┘ └─────────────┘ │
361
- │ │ │ │ │
362
- │ ▼ ▼ ▼ │
363
- │ ┌────────────────────────────────────────────────────┐ │
364
- │ │ requestAndUnwrap / ctx.request │ │
365
- │ │ (envelope unwrapping, error throwing) │ │
366
- │ └────────────────────────────────────────────────────┘ │
367
- │ │ │
368
- │ ▼ │
369
- │ ┌────────────────────────────────────────────────────┐ │
370
- │ │ requestCore │ │
371
- │ │ (URL building, headers, timeout, JSON parsing) │ │
372
- │ └────────────────────────────────────────────────────┘ │
373
- │ │ │
374
- │ ▼ │
375
- │ globalThis.fetch │
376
- └───────────────────────────────────────────────────────────────┘
377
- ```
378
-
379
- ### What you can do
380
-
381
- | Endpoint | Description |
382
- |----------|-------------|
383
- | `api.account.get()` | Get account info (balance, usage) |
384
- | `api.account.connections.list()` | List all connections |
385
- | `api.account.connections.create()` | Create a new connection |
386
- | `api.account.connections.get(id)` | Get connection details |
387
- | `api.account.connections.patch(id)` | Update connection (rules, geo, session) |
388
- | `api.account.connections.delete(id)` | Delete a connection |
389
- | `api.geos.list()` | List available geo-targeting options |
492
+ ## REST API
493
+
494
+ `AluviaApi` is a typed wrapper for the Aluvia REST API. Use it to manage connections, check account info, or build custom tooling without starting a proxy.
495
+
496
+ ### Endpoints
497
+
498
+ | Method | Description |
499
+ | ----------------------------------------- | --------------------------------------- |
500
+ | `api.account.get()` | Get account info (balance, usage) |
501
+ | `api.account.connections.list()` | List all connections |
502
+ | `api.account.connections.create(body)` | Create a new connection |
503
+ | `api.account.connections.get(id)` | Get connection details |
504
+ | `api.account.connections.patch(id, body)` | Update connection (rules, geo, session) |
505
+ | `api.account.connections.delete(id)` | Delete a connection |
506
+ | `api.account.usage.get(params?)` | Get usage stats |
507
+ | `api.geos.list()` | List available geo-targeting options |
390
508
 
391
509
  ### Example
392
510
 
393
511
  ```ts
394
- import { AluviaApi } from '@aluvia/sdk';
512
+ import { AluviaApi } from "@aluvia/sdk";
395
513
 
396
514
  const api = new AluviaApi({ apiKey: process.env.ALUVIA_API_KEY! });
397
515
 
398
516
  // Check account balance
399
517
  const account = await api.account.get();
400
- console.log('Balance:', account.balance_gb, 'GB');
518
+ console.log("Balance:", account.balance_gb, "GB");
401
519
 
402
520
  // Create a connection for a new agent
403
- const connection = await api.account.connections.create({
404
- description: 'pricing-scraper',
405
- rules: ['competitor-site.com'],
406
- target_geo: 'us_ca',
521
+ const conn = await api.account.connections.create({
522
+ description: "pricing-scraper",
523
+ rules: ["competitor-site.com"],
524
+ target_geo: "us_ca",
407
525
  });
408
- console.log('Created:', connection.connection_id);
526
+ console.log("Created connection:", conn.connection_id);
409
527
 
410
528
  // List available geos
411
529
  const geos = await api.geos.list();
412
- console.log('Geos:', geos.map(g => g.code));
530
+ console.log(
531
+ "Geos:",
532
+ geos.map((g) => g.code),
533
+ );
534
+ ```
535
+
536
+ `AluviaApi` is also available as `client.api` when using `AluviaClient`.
537
+
538
+ For the complete API reference, see the [API Technical Guide](docs/api-technical-guide.md).
539
+
540
+ ---
541
+
542
+ ## Architecture
543
+
544
+ The client is split into two independent planes:
545
+
546
+ ```
547
+ ┌─────────────────────────────────────────────────────────────────┐
548
+ │ AluviaClient │
549
+ ├─────────────────────────────┬───────────────────────────────────┤
550
+ │ Control Plane │ Data Plane │
551
+ │ (ConfigManager) │ (ProxyServer) │
552
+ ├─────────────────────────────┼───────────────────────────────────┤
553
+ │ • Fetches/creates config │ • Local HTTP proxy (proxy-chain) │
554
+ │ • Polls for updates (ETag) │ • Per-request routing decisions │
555
+ │ • PATCH updates (rules, │ • Uses rules engine to decide: │
556
+ │ session, geo) │ direct vs gateway │
557
+ └─────────────────────────────┴───────────────────────────────────┘
413
558
  ```
414
559
 
415
- **Tip:** `AluviaApi` is also available as `client.api` when using `AluviaClient`.
560
+ **Control Plane (ConfigManager)** communicates with the Aluvia REST API to fetch proxy credentials and routing rules, polls for configuration updates using ETags, and pushes updates (rules, session ID, geo).
416
561
 
417
- For the complete API reference, see [API Technical Guide](docs/api-technical-guide.md).
562
+ **Data Plane (ProxyServer)** — runs a local HTTP proxy on `127.0.0.1` that reads the latest config per-request, so rule updates take effect immediately without restarts.
563
+
564
+ ```
565
+ ┌──────────────────┐ ┌──────────────────────────┐ ┌──────────────────────┐
566
+ │ │ │ │ │ │
567
+ │ Your Agent │─────▶│ Aluvia Client │─────▶│ gateway.aluvia.io │
568
+ │ │ │ 127.0.0.1:port │ │ (Mobile IPs) │
569
+ │ │ │ │ │ │
570
+ └──────────────────┘ │ Per-request routing: │ └──────────────────────┘
571
+ │ │
572
+ │ not-blocked.com ──────────────▶ Direct
573
+ │ blocked-site.com ─────────────▶ Via Aluvia
574
+ │ │
575
+ └──────────────────────────┘
576
+ ```
418
577
 
419
578
  ---
420
579
 
580
+ ## Learn more
581
+
582
+ - [What is Aluvia?](https://docs.aluvia.io/)
583
+ - [Understanding connections](https://docs.aluvia.io/fundamentals/connections)
584
+ - [Playwright integration guide](https://docs.aluvia.io/integrations/integration-playwright)
585
+ - [Puppeteer](https://docs.aluvia.io/integrations/integration-puppeteer), [Selenium](https://docs.aluvia.io/integrations/integration-selenium), [Axios](https://docs.aluvia.io/integrations/integration-axios), [got](https://docs.aluvia.io/integrations/integration-got), [fetch](https://docs.aluvia.io/integrations/integration-fetch)
586
+ - [CLI Technical Guide](docs/cli-technical-guide.md)
587
+ - [Client Technical Guide](docs/client-technical-guide.md)
588
+ - [API Technical Guide](docs/api-technical-guide.md)
589
+
421
590
  ## License
422
591
 
423
592
  MIT — see [LICENSE](./LICENSE)