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