@aluvia/sdk 1.1.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 +409 -285
  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 +365 -189
  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 +371 -195
  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 +48 -47
  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 -10
package/README.md CHANGED
@@ -5,430 +5,506 @@
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.
13
-
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.
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`
17
19
 
18
20
  ---
19
21
 
20
- ## Aluvia client
21
-
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.
23
-
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.
25
-
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
- └──────────────────────────┘
38
- ```
39
-
40
- **Benefits:**
22
+ ## Table of contents
41
23
 
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.
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)
46
33
 
47
34
  ---
48
35
 
49
36
  ## Quick start
50
37
 
51
- ### Understand the basics
38
+ ### 1. Get Aluvia API key
52
39
 
53
- - [What is Aluvia?](https://docs.aluvia.io/)
54
- - [Understanding connections](https://docs.aluvia.io/fundamentals/connections)
40
+ [Aluvia dashboard](https://dashboard.aluvia.io)
41
+
42
+ ### 2. Install
55
43
 
56
- ### Get Aluvia API key
44
+ ```bash
45
+ npm install @aluvia/sdk playwright
46
+ export ALUVIA_API_KEY="your-api-key"
47
+ ```
57
48
 
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**
49
+ ### 3. Run
60
50
 
61
- ### Install the SDK
51
+ Aluvia automatically detects website blocks and uses mobile IPs when necessary.
62
52
 
63
53
  ```bash
64
- npm install @aluvia/sdk
54
+ aluvia session start https://example.com --auto-unblock --run your-script.js
65
55
  ```
66
56
 
67
- **Requirements:** Node.js 18 or later
57
+ ---
68
58
 
69
- ### Example: Dynamic unblocking with Playwright
59
+ ## Skills
70
60
 
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.
61
+ - Claude code skll
62
+ - OpenClaw skill
72
63
 
73
- ```ts
74
- import { chromium } from "playwright";
75
- import { AluviaClient } from "@aluvia/sdk";
64
+ ---
76
65
 
77
- // Initialize the Aluvia client with your API key
78
- const client = new AluviaClient({ apiKey: process.env.ALUVIA_API_KEY! });
66
+ ## CLI reference
79
67
 
80
- // Start the client (launches local proxy, fetches connection config)
81
- const connection = await client.start();
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.
82
69
 
83
- // Configure geo targeting (use California IPs)
84
- await client.updateTargetGeo("us_ca");
70
+ ### `session start` Launch a browser session
85
71
 
86
- // Set session ID (requests with the same session ID use the same IP)
87
- await client.updateSessionId("agentsession1");
72
+ ```bash
73
+ aluvia session start <url> [options]
74
+ ```
88
75
 
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() });
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 |
92
84
 
93
- // Track hostnames we've added to proxy rules
94
- const proxiedHosts = new Set<string>();
85
+ **Examples:**
95
86
 
96
- async function visitWithRetry(url: string): Promise<string> {
97
- const page = await browser.newPage();
87
+ ```bash
88
+ # Launch with auto-unblocking
89
+ aluvia session start https://example.com --auto-unblock
98
90
 
99
- try {
100
- const response = await page.goto(url, { waitUntil: "domcontentloaded" });
101
- const hostname = new URL(url).hostname;
91
+ # Run a script inline
92
+ aluvia session start https://example.com --auto-unblock --run scrape.mjs
102
93
 
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");
94
+ # Debug with a visible browser window
95
+ aluvia session start https://example.com --headful
109
96
 
110
- if (isBlocked && !proxiedHosts.has(hostname)) {
111
- console.log(`Blocked by ${hostname} adding to proxy rules`);
97
+ # Reuse an existing connection
98
+ aluvia session start https://example.com --connection-id 3449
99
+ ```
112
100
 
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]);
101
+ ### `session close` Stop a session
117
102
 
118
- // Rotate to a fresh IP by changing the session ID
119
- await client.updateSessionId(`retry-${Date.now()}`);
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
+ ```
120
108
 
121
- await page.close();
122
- return visitWithRetry(url);
123
- }
109
+ ### `session list` — List active sessions
124
110
 
125
- return await page.content();
126
- } finally {
127
- await page.close();
128
- }
129
- }
111
+ ```bash
112
+ aluvia session list
113
+ ```
130
114
 
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();
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
139
129
  }
140
130
  ```
141
131
 
142
- ### Example: Auto-launch Playwright browser
143
-
144
- For even simpler setup, the SDK can automatically launch a Chromium browser that's already configured with the Aluvia proxy. This eliminates the need to manually import Playwright and configure proxy settings.
132
+ ### `session get` Full session details
145
133
 
146
- ```ts
147
- import { AluviaClient } from "@aluvia/sdk";
148
-
149
- // Initialize with startPlaywright option to auto-launch browser
150
- const client = new AluviaClient({
151
- apiKey: process.env.ALUVIA_API_KEY!,
152
- startPlaywright: true, // Automatically launch and configure Chromium
153
- });
134
+ ```bash
135
+ aluvia session get [--browser-session <name>]
136
+ ```
154
137
 
155
- // Start the client - this also launches the browser
156
- const connection = await client.start();
138
+ Returns session info enriched with block detection history and the full connection object from the API.
157
139
 
158
- // Browser is already configured with Aluvia proxy
159
- const browser = connection.browser;
160
- const page = await browser.newPage();
140
+ ### `session rotate-ip` Get a new IP
161
141
 
162
- // Configure geo targeting and session ID
163
- await client.updateTargetGeo("us_ca");
164
- await client.updateSessionId("session1");
142
+ ```bash
143
+ aluvia session rotate-ip [--browser-session <name>]
144
+ ```
165
145
 
166
- // Navigate directly - proxy is already configured
167
- await page.goto("https://example.com");
168
- console.log("Title:", await page.title());
146
+ ### `session set-geo` Target a specific region
169
147
 
170
- // Cleanup - automatically closes both browser and proxy
171
- await connection.close();
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
172
152
  ```
173
153
 
174
- **Note:** To use `startPlaywright: true`, you must install Playwright:
154
+ ### `session set-rules` Update routing rules
175
155
 
176
156
  ```bash
177
- npm install playwright
178
- npx playwright install chromium
157
+ aluvia session set-rules "example.com,api.example.com" # add rules
158
+ aluvia session set-rules --remove "example.com" # remove rules
179
159
  ```
180
160
 
181
- ### Integration guides
161
+ Rules are comma-separated. By default rules are appended; use `--remove` to remove specific rules.
182
162
 
183
- The Aluvia client provides ready-to-use adapters for popular automation and HTTP tools:
163
+ ### Account and other commands
184
164
 
185
- - [Playwright](docs/integrations/integration-playwright.md)
186
- - [Puppeteer](docs/integrations/integration-puppeteer.md)
187
- - [Selenium](docs/integrations/integration-selenium.md)
188
- - [Axios](docs/integrations/integration-axios.md)
189
- - [got](docs/integrations/integration-got.md)
190
- - [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
+ ```
191
174
 
192
175
  ---
193
176
 
194
- ## Architecture
177
+ ## Connecting to a running browser
195
178
 
196
- The client is split into two independent **planes**:
179
+ There are two ways to run code against a browser session started by the CLI.
197
180
 
198
- ```
199
- ┌─────────────────────────────────────────────────────────────────┐
200
- │ AluviaClient │
201
- ├─────────────────────────────┬───────────────────────────────────┤
202
- │ Control Plane │ Data Plane │
203
- │ (ConfigManager) │ (ProxyServer) │
204
- ├─────────────────────────────┼───────────────────────────────────┤
205
- │ • Fetches/creates config │ • Local HTTP proxy (proxy-chain) │
206
- │ • Polls for updates (ETag) │ • Per-request routing decisions │
207
- │ • PATCH updates (rules, │ • Uses rules engine to decide: │
208
- │ session, geo) │ direct vs gateway │
209
- └─────────────────────────────┴───────────────────────────────────┘
181
+ ### Option A: `--run` (simplest)
182
+
183
+ Pass a script to `session start`. The globals `page`, `browser`, and `context` are available — no imports needed:
184
+
185
+ ```bash
186
+ aluvia session start https://example.com --auto-unblock --run script.mjs
210
187
  ```
211
188
 
212
- ### Control Plane (ConfigManager)
189
+ ```js
190
+ // script.mjs
191
+ console.log("URL:", page.url());
213
192
 
214
- - Communicates with the Aluvia REST API (`/account/connections/...`)
215
- - Fetches proxy credentials and routing rules
216
- - Polls for configuration updates
217
- - Pushes updates (rules, session ID, geo)
193
+ const newPage = await context.newPage();
194
+ await newPage.goto("https://another-site.com");
195
+ console.log("Other site title:", await newPage.title());
196
+ ```
218
197
 
219
- ### Data Plane (ProxyServer)
198
+ The session starts, runs your script, and exits.
220
199
 
221
- - Runs a local HTTP proxy on `127.0.0.1`
222
- - For each request, uses the **rules engine** to decide whether to route direct or via Aluvia.
223
- - Because the proxy reads the latest config per-request, rule updates take effect immediately
200
+ ### Option B: `connect()` (for AI agents and long-running processes)
224
201
 
225
- ---
202
+ Start a session as a background daemon, then connect from your application:
226
203
 
227
- ## Operating modes
204
+ ```bash
205
+ aluvia session start https://example.com --auto-unblock
206
+ ```
228
207
 
229
- The Aluvia client has two operating modes: **Client Proxy Mode** (default) and **Gateway Mode**.
208
+ ```ts
209
+ import { connect } from "@aluvia/sdk";
230
210
 
231
- ### Client Proxy Mode
211
+ // Auto-discovers the running session
212
+ const { page, browser, context, disconnect } = await connect();
213
+ console.log("URL:", page.url());
232
214
 
233
- **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.
215
+ // When running multiple sessions, specify by name
216
+ const conn = await connect("swift-falcon");
217
+ console.log("URL:", conn.page.url());
234
218
 
235
- **Why use it:**
219
+ // Disconnect when done (the session keeps running)
220
+ await disconnect();
221
+ ```
236
222
 
237
- - Selective routing reduces cost and latency (only proxy what you need)
238
- - Credentials stay inside the SDK (nothing secret in your config)
239
- - Rule changes apply immediately (no restarts)
223
+ Use this when your agent generates automation code dynamically at runtime or needs a persistent browser across multiple operations.
240
224
 
241
- **Best for:** Using per-hostname routing rules.
225
+ ---
242
226
 
243
- ### Gateway Mode
227
+ ## Programmatic usage
244
228
 
245
- Set `localProxy: false` to enable.
229
+ For full control, use `AluviaClient` directly instead of the CLI.
246
230
 
247
- **How it works:** No local proxy. Your tools connect directly to `gateway.aluvia.io` and **ALL** traffic goes through Aluvia.
231
+ ### Basic example
248
232
 
249
- **Why use it:**
233
+ ```ts
234
+ import { AluviaClient } from "@aluvia/sdk";
235
+ import { chromium } from "playwright";
250
236
 
251
- - No local process to manage
252
- - Simpler setup for tools with native proxy auth support
237
+ const client = new AluviaClient({
238
+ apiKey: process.env.ALUVIA_API_KEY!,
239
+ });
253
240
 
254
- **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");
255
245
 
256
- ---
246
+ // ... do your work ...
257
247
 
258
- ## Using Aluvia client
248
+ await browser.close();
249
+ await connection.close();
250
+ ```
259
251
 
260
- ### 1. Create a client
252
+ ### With auto-launched browser
261
253
 
262
254
  ```ts
263
255
  const client = new AluviaClient({
264
256
  apiKey: process.env.ALUVIA_API_KEY!,
265
- connectionId: 123, // Optional: reuse an existing connection
266
- localProxy: true, // Optional: default true (recommended)
267
- startPlaywright: true, // Optional: auto-launch Chromium browser
257
+ startPlaywright: true,
258
+ blockDetection: {
259
+ enabled: true,
260
+ autoUnblock: true,
261
+ },
268
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
269
269
  ```
270
270
 
271
- For all options, see the [Client Technical Guide](docs/client-technical-guide.md#constructor-options).
271
+ ### Runtime updates
272
272
 
273
- ### 2. Start the client and get a connection
273
+ While your agent is running, update routing, rotate IPs, or change geo — no restarts needed:
274
274
 
275
275
  ```ts
276
- 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
277
279
  ```
278
280
 
279
- This starts the local proxy and returns a connection object you'll use with your tools.
280
- [Understanding the connection object](https://docs.aluvia.io/fundamentals/connections)
281
-
282
- ### 3. Use the connection with your tools
283
-
284
- Pass the connection to your automation tool using the appropriate adapter:
281
+ ### Constructor options
285
282
 
286
283
  ```ts
287
- 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
+ });
288
299
  ```
289
300
 
290
- ### 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.
308
+
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 |
291
324
 
292
- While your agent is running, you can update routing rules, rotate IPs, or change geo targeting—no restart needed:
325
+ ### Examples
293
326
 
294
327
  ```ts
295
- await client.updateRules(["blocked-site.com"]); // Add hostname to proxy rules
296
- await client.updateSessionId("newsession"); // Rotate to a new IP
297
- 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([]);
298
339
  ```
299
340
 
300
- ### 5. Clean up when done
341
+ Or from the CLI:
301
342
 
302
- ```ts
303
- 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"
304
346
  ```
305
347
 
306
348
  ---
307
349
 
308
- ## Routing rules
350
+ ## Block detection and auto-unblocking
309
351
 
310
- 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.
311
353
 
312
- Traffic can be sent either:
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.
313
355
 
314
- - direct (using the agent's datacenter/cloud IP) or,
315
- - through Aluvia's mobile proxy IPs,
356
+ ### Automatic unblocking (recommended)
316
357
 
317
- ### Benefits
358
+ When a block is detected, the SDK adds the hostname to proxy rules and reloads the page through Aluvia:
318
359
 
319
- - Selectively routing traffic to mobile proxies reduces proxy costs and connection latency.
320
- - Rules can be updated during runtime, allowing agents to work around website blocks on the fly.
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
+ ```
375
+
376
+ Or from the CLI:
321
377
 
322
- ### Example rules
378
+ ```bash
379
+ aluvia session start https://example.com --auto-unblock
380
+ ```
381
+
382
+ ### Detection-only mode
383
+
384
+ Run detection without automatic remediation. Your agent inspects the results and decides what to do:
323
385
 
324
386
  ```ts
325
- await client.updateRules(["*"]); // Proxy all traffic
326
- await client.updateRules(["target-site.com", "*.google.com"]); // Proxy specific hosts
327
- await client.updateRules(["*", "-api.stripe.com"]); // Proxy all except specified
328
- 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
+ }
329
395
  ```
330
396
 
331
- ### Supported routing rule patterns:
397
+ ### Block status scores
332
398
 
333
- | Pattern | Matches |
334
- | --------------- | ------------------------------------- |
335
- | `*` | All hostnames |
336
- | `example.com` | Exact match |
337
- | `*.example.com` | Subdomains of example.com |
338
- | `google.*` | google.com, google.co.uk, and similar |
339
- | `-example.com` | Exclude from proxying |
399
+ Each page analysis produces a score from 0.0 to 1.0:
340
400
 
341
- ---
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
342
410
 
343
- ## Dynamic unblocking
411
+ Detection runs in two passes:
344
412
 
345
- 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.
346
415
 
347
- **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.
348
417
 
349
- 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:
350
436
 
351
437
  ```ts
352
438
  const response = await page.goto(url);
353
439
 
354
440
  if (response?.status() === 403) {
355
- // Blocked! Add this hostname to proxy rules and retry
356
441
  await client.updateRules([...currentRules, new URL(url).hostname]);
357
- await page.goto(url); // This request goes through Aluvia
442
+ await page.goto(url); // retried through Aluvia
358
443
  }
359
444
  ```
360
445
 
361
- 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).
362
447
 
363
448
  ---
364
449
 
365
450
  ## Tool integration adapters
366
451
 
367
- 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
368
467
 
369
- | Tool | Method | Returns |
370
- | ------------ | ---------------------------- | ----------------------------------------------------------- |
371
- | Playwright | `connection.asPlaywright()` | `{ server, username?, password? }` |
372
- | Playwright | `connection.browser` | Auto-launched Chromium browser (if `startPlaywright: true`) |
373
- | Puppeteer | `connection.asPuppeteer()` | `['--proxy-server=...']` |
374
- | Selenium | `connection.asSelenium()` | `'--proxy-server=...'` |
375
- | Axios | `connection.asAxiosConfig()` | `{ proxy: false, httpAgent, httpsAgent }` |
376
- | got | `connection.asGotOptions()` | `{ agent: { http, https } }` |
377
- | fetch | `connection.asUndiciFetch()` | Proxy-enabled `fetch` function |
378
- | Node.js http | `connection.asNodeAgents()` | `{ http: Agent, https: Agent }` |
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() });
379
474
 
380
- **Playwright auto-launch:** Set `startPlaywright: true` in the client options to automatically launch a Chromium browser that's already configured with the Aluvia proxy. The browser is available via `connection.browser` and is automatically cleaned up when you call `connection.close()`.
475
+ // Axios
476
+ const axiosClient = axios.create(connection.asAxiosConfig());
477
+ await axiosClient.get("https://example.com");
478
+
479
+ // got
480
+ const gotClient = got.extend(connection.asGotOptions());
481
+ await gotClient("https://example.com");
482
+
483
+ // Node's fetch (via undici)
484
+ const myFetch = connection.asUndiciFetch();
485
+ await myFetch("https://example.com");
486
+ ```
381
487
 
382
- For more details regarding integration adapters, see the [Client Technical Guide](docs/client-technical-guide.md#tool-adapters).
488
+ For more details, see the [Client Technical Guide](docs/client-technical-guide.md#tool-adapters).
383
489
 
384
490
  ---
385
491
 
386
- ## Aluvia API
387
-
388
- `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.
389
-
390
- `AluviaApi` is built from modular layers:
391
-
392
- ```
393
- ┌───────────────────────────────────────────────────────────────┐
394
- │ AluviaApi │
395
- │ Constructor validates apiKey, creates namespace objects │
396
- ├───────────────────────────────────────────────────────────────┤
397
- │ │
398
- │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
399
- │ │ account │ │ geos │ │ request │
400
- │ │ namespace │ │ namespace │ │ (escape │ │
401
- │ │ │ │ │ │ hatch) │ │
402
- │ └─────────────┘ └─────────────┘ └─────────────┘ │
403
- │ │ │ │ │
404
- │ ▼ ▼ ▼ │
405
- │ ┌────────────────────────────────────────────────────┐ │
406
- │ │ requestAndUnwrap / ctx.request │ │
407
- │ │ (envelope unwrapping, error throwing) │ │
408
- │ └────────────────────────────────────────────────────┘ │
409
- │ │ │
410
- │ ▼ │
411
- │ ┌────────────────────────────────────────────────────┐ │
412
- │ │ requestCore │ │
413
- │ │ (URL building, headers, timeout, JSON parsing) │ │
414
- │ └────────────────────────────────────────────────────┘ │
415
- │ │ │
416
- │ ▼ │
417
- │ globalThis.fetch │
418
- └───────────────────────────────────────────────────────────────┘
419
- ```
420
-
421
- ### What you can do
422
-
423
- | Endpoint | Description |
424
- | ------------------------------------ | --------------------------------------- |
425
- | `api.account.get()` | Get account info (balance, usage) |
426
- | `api.account.connections.list()` | List all connections |
427
- | `api.account.connections.create()` | Create a new connection |
428
- | `api.account.connections.get(id)` | Get connection details |
429
- | `api.account.connections.patch(id)` | Update connection (rules, geo, session) |
430
- | `api.account.connections.delete(id)` | Delete a connection |
431
- | `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 |
432
508
 
433
509
  ### Example
434
510
 
@@ -442,12 +518,12 @@ const account = await api.account.get();
442
518
  console.log("Balance:", account.balance_gb, "GB");
443
519
 
444
520
  // Create a connection for a new agent
445
- const connection = await api.account.connections.create({
521
+ const conn = await api.account.connections.create({
446
522
  description: "pricing-scraper",
447
523
  rules: ["competitor-site.com"],
448
524
  target_geo: "us_ca",
449
525
  });
450
- console.log("Created:", connection.connection_id);
526
+ console.log("Created connection:", conn.connection_id);
451
527
 
452
528
  // List available geos
453
529
  const geos = await api.geos.list();
@@ -457,12 +533,60 @@ console.log(
457
533
  );
458
534
  ```
459
535
 
460
- **Tip:** `AluviaApi` is also available as `client.api` when using `AluviaClient`.
536
+ `AluviaApi` is also available as `client.api` when using `AluviaClient`.
461
537
 
462
- For the complete API reference, see [API Technical Guide](docs/api-technical-guide.md).
538
+ For the complete API reference, see the [API Technical Guide](docs/api-technical-guide.md).
463
539
 
464
540
  ---
465
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
+ └─────────────────────────────┴───────────────────────────────────┘
558
+ ```
559
+
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).
561
+
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
+ ```
577
+
578
+ ---
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
+
466
590
  ## License
467
591
 
468
592
  MIT — see [LICENSE](./LICENSE)