capskip 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,55 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.2] - 2026-07-15
9
+
10
+ ### Fixed
11
+
12
+ - Submitting with `json=1` no longer fails with `ApiException: cannot recognize
13
+ response`. With `json=1` CapSkip's `in.php` returns `{"status":1,"request":"<id>"}`
14
+ instead of `OK|<id>`; `send()` now parses both forms, so `normal(file, { json: 1 })`
15
+ and the equivalent reCAPTCHA/Turnstile calls submit and solve correctly.
16
+
17
+ ## [1.0.1] - 2026-07-14
18
+
19
+ ### Fixed
20
+
21
+ - Polling no longer crashes with `ApiException: cannot recognize response` when
22
+ CapSkip returns an empty response body. CapSkip returns an empty body while no
23
+ result is available yet — briefly right after a captcha is submitted (before it
24
+ reports `CAPCHA_NOT_READY`), for an unknown id, and after a solved token has
25
+ already been read. The SDK now treats an empty body as "not ready" and keeps
26
+ polling, so `recaptcha()`, `turnstile()`, and `normal()` solve reliably instead
27
+ of failing on the first poll.
28
+
29
+ ## [1.0.0] - 2026-07-14
30
+
31
+ ### Added
32
+
33
+ - Initial release of the CapSkip Node.js SDK
34
+ - `CapSkip` client for the local CapSkip API — every method returns a Promise
35
+ - `AsyncCapSkip` / `AsyncApiClient` exported as aliases for cross-SDK parity
36
+ - Image CAPTCHA solving via `normal()` (file, URL, base64, or data-URI)
37
+ - reCAPTCHA v2 / v3 solving via `recaptcha()` (invisible, enterprise, proxy)
38
+ - Cloudflare Turnstile solving via `turnstile()` (widget and challenge page)
39
+ - Turnstile automatically polls with `json=1` and returns `userAgent` when provided
40
+ - Manual workflow: `send()`, `getResult()`, `solve()`
41
+ - Adaptive result polling — starts at 0.25s and backs off (doubling) up to the
42
+ configured `pollingInterval`, so fast solves (e.g. image captchas) return in a
43
+ fraction of a second; `pollingInterval` also accepts sub-second values
44
+ - Familiar parameter aliases (`url`→`pageurl`, `score`→`min_score`, etc.)
45
+ - Proxy support via object format `{ type: '...', uri: '...' }`
46
+ - Strict per-captcha parameter validation — only documented parameters are accepted
47
+ - Error hierarchy: `ValidationException`, `NetworkException`, `ApiException`, `TimeoutException`
48
+ - Bundled TypeScript type definitions
49
+ - Example scripts for every supported captcha type
50
+ - Unit and integration tests using Node's built-in test runner — no runtime dependencies
51
+ - Documentation: Tutorial, Getting Started, API Reference, Troubleshooting
52
+
53
+ [1.0.2]: https://github.com/capskip/capskip-node/releases/tag/v1.0.2
54
+ [1.0.1]: https://github.com/capskip/capskip-node/releases/tag/v1.0.1
55
+ [1.0.0]: https://github.com/capskip/capskip-node/releases/tag/v1.0.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CapSkip
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,274 @@
1
+ # CapSkip Node.js SDK
2
+
3
+ [![Node.js 18+](https://img.shields.io/badge/node-18%2B-brightgreen.svg)](https://nodejs.org/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
5
+ [![Tests](https://github.com/capskip/capskip-node/actions/workflows/ci.yml/badge.svg)](https://github.com/capskip/capskip-node/actions/workflows/ci.yml)
6
+
7
+ Official Node.js client for the [CapSkip](https://capskip.com) **local** captcha solver.
8
+
9
+ CapSkip runs on your machine and exposes a standard captcha-solver HTTP API (the familiar `in.php` / `res.php` endpoints). This SDK wraps that API with clean, familiar method names, so you can solve captchas locally — no cloud service and no per-solve API fees beyond your CapSkip license.
10
+
11
+ ---
12
+
13
+ ## Quick start (5 minutes)
14
+
15
+ ### 1. Install CapSkip
16
+
17
+ Download and run the CapSkip desktop app from [capskip.com](https://capskip.com). Leave it running in the background.
18
+
19
+ In CapSkip settings, note:
20
+
21
+ - **API port** (default: `8080`)
22
+ - **API key** (optional — if validation is disabled, any string works)
23
+
24
+ ### 2. Install the SDK
25
+
26
+ ```bash
27
+ npm install capskip
28
+ ```
29
+
30
+ Or from source:
31
+
32
+ ```bash
33
+ git clone https://github.com/capskip/capskip-node.git
34
+ cd capskip-node
35
+ npm install
36
+ ```
37
+
38
+ ### 3. Solve your first captcha
39
+
40
+ ```js
41
+ const { CapSkip } = require('capskip');
42
+
43
+ const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });
44
+
45
+ (async () => {
46
+ const result = await solver.recaptcha(
47
+ 'YOUR_SITEKEY',
48
+ 'https://example.com/page-with-recaptcha',
49
+ );
50
+
51
+ console.log(result.code); // g-recaptcha-response token
52
+ })();
53
+ ```
54
+
55
+ > **Prerequisite:** CapSkip must be running before you call the SDK. If you see a connection error, see [Troubleshooting](docs/TROUBLESHOOTING.md).
56
+
57
+ Every solve method returns a Promise — use `await` or `.then()`.
58
+
59
+ ---
60
+
61
+ ## Supported captcha types
62
+
63
+ | Type | SDK method |
64
+ |---|---|
65
+ | Image CAPTCHA (distorted text) | `solver.normal(file)` |
66
+ | reCAPTCHA v2 (checkbox) | `solver.recaptcha(sitekey, url)` |
67
+ | reCAPTCHA v2 Invisible | `solver.recaptcha(sitekey, url, { invisible: 1 })` |
68
+ | reCAPTCHA v2 Enterprise | `solver.recaptcha(sitekey, url, { enterprise: 1 })` |
69
+ | reCAPTCHA v3 | `solver.recaptcha(sitekey, url, { version: 'v3' })` |
70
+ | reCAPTCHA v3 Enterprise | `solver.recaptcha(sitekey, url, { version: 'v3', enterprise: 1 })` |
71
+ | Cloudflare Turnstile (widget) | `solver.turnstile(sitekey, url)` |
72
+ | Cloudflare Turnstile (challenge page) | `solver.turnstile(sitekey, url, { data, pagedata })` |
73
+
74
+ ---
75
+
76
+ ## Documentation
77
+
78
+ | Guide | Description |
79
+ |---|---|
80
+ | [Tutorial](docs/TUTORIAL.md) | Complete walkthrough of every captcha type |
81
+ | [Getting Started](docs/GETTING_STARTED.md) | Full setup: CapSkip app, SDK install, first script |
82
+ | [API Reference](docs/API_REFERENCE.md) | All classes, methods, parameters, and return values |
83
+ | [Examples](examples/) | Ready-to-run scripts for every captcha type |
84
+ | [Troubleshooting](docs/TROUBLESHOOTING.md) | Connection errors, timeouts, proxy issues |
85
+ | [Contributing](CONTRIBUTING.md) | Development setup, tests, pull requests |
86
+ | [Changelog](CHANGELOG.md) | Release history |
87
+
88
+ ---
89
+
90
+ ## Configuration
91
+
92
+ ```js
93
+ const { CapSkip } = require('capskip');
94
+
95
+ const solver = new CapSkip({
96
+ apiKey: 'capskip', // your CapSkip API key (or any string if validation is off)
97
+ host: '127.0.0.1', // CapSkip host
98
+ port: 8080, // CapSkip port from app settings
99
+ defaultTimeout: 120, // seconds — image captcha polling timeout
100
+ recaptchaTimeout: 300, // seconds — reCAPTCHA / Turnstile polling timeout
101
+ pollingInterval: 5, // max seconds between res.php polls (starts at 0.25s, backs off to this)
102
+ });
103
+ ```
104
+
105
+ Use environment variables in production:
106
+
107
+ ```bash
108
+ # Linux / macOS
109
+ export CAPSKIP_API_KEY="your-key"
110
+ export CAPSKIP_HOST="127.0.0.1"
111
+ export CAPSKIP_PORT="8080"
112
+ ```
113
+
114
+ ```powershell
115
+ # Windows PowerShell
116
+ $env:CAPSKIP_API_KEY = "your-key"
117
+ $env:CAPSKIP_HOST = "127.0.0.1"
118
+ $env:CAPSKIP_PORT = "8080"
119
+ ```
120
+
121
+ ```js
122
+ const { CapSkip } = require('capskip');
123
+
124
+ const solver = new CapSkip({
125
+ apiKey: process.env.CAPSKIP_API_KEY || 'capskip',
126
+ host: process.env.CAPSKIP_HOST || '127.0.0.1',
127
+ port: parseInt(process.env.CAPSKIP_PORT || '8080', 10),
128
+ });
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Usage examples
134
+
135
+ ### Image captcha
136
+
137
+ ```js
138
+ await solver.normal('captcha.png');
139
+ await solver.normal('https://example.com/captcha.jpg');
140
+ await solver.normal('data:image/png;base64,iVBORw0KGgo...');
141
+ // result.code holds the recognized text
142
+ ```
143
+
144
+ ### reCAPTCHA v2 / v3
145
+
146
+ ```js
147
+ // reCAPTCHA v2
148
+ const v2 = await solver.recaptcha('...', 'https://example.com');
149
+
150
+ // reCAPTCHA v3
151
+ const v3 = await solver.recaptcha('...', 'https://example.com', {
152
+ version: 'v3',
153
+ action: 'submit',
154
+ score: 0.7,
155
+ });
156
+ ```
157
+
158
+ ### Cloudflare Turnstile
159
+
160
+ ```js
161
+ const result = await solver.turnstile('0x4AAAAAAA...', 'https://example.com');
162
+ ```
163
+
164
+ ### With a proxy (reCAPTCHA & Turnstile only)
165
+
166
+ ```js
167
+ // Proxy is not supported for image captcha
168
+ await solver.recaptcha('...', 'https://example.com', {
169
+ proxy: { type: 'HTTPS', uri: 'user:pass@1.2.3.4:3128' },
170
+ });
171
+ await solver.turnstile('...', 'https://example.com', {
172
+ proxy: { type: 'HTTP', uri: '1.2.3.4:3128' },
173
+ });
174
+ ```
175
+
176
+ ### Parallel solving
177
+
178
+ ```js
179
+ const { CapSkip } = require('capskip');
180
+
181
+ async function main() {
182
+ const solver = new CapSkip();
183
+ const [r1, r2] = await Promise.all([
184
+ solver.recaptcha('...', 'https://a.com'),
185
+ solver.turnstile('...', 'https://b.com'),
186
+ ]);
187
+ console.log(r1.code, r2.code);
188
+ }
189
+
190
+ main();
191
+ ```
192
+
193
+ > `AsyncCapSkip` is exported as an alias of `CapSkip` — Node I/O is asynchronous by nature, so every method already returns a Promise.
194
+
195
+ More examples: [`examples/`](examples/)
196
+
197
+ ---
198
+
199
+ ## Return value
200
+
201
+ Every solve method resolves to:
202
+
203
+ ```js
204
+ {
205
+ captchaId: '12345', // internal ID from CapSkip
206
+ code: 'TOKEN_OR_TEXT', // solution — text for image, token for reCAPTCHA/Turnstile
207
+ userAgent: '...', // Turnstile only — use when submitting challenge-page tokens
208
+ }
209
+ ```
210
+
211
+ ---
212
+
213
+ ## Error handling
214
+
215
+ ```js
216
+ const {
217
+ CapSkip, ValidationException, NetworkException, ApiException, TimeoutException,
218
+ } = require('capskip');
219
+
220
+ try {
221
+ const result = await solver.recaptcha('...', '...');
222
+ } catch (err) {
223
+ if (err instanceof ValidationException) {
224
+ // invalid parameters
225
+ } else if (err instanceof NetworkException) {
226
+ // CapSkip not running, or captcha not ready (manual polling)
227
+ } else if (err instanceof ApiException) {
228
+ // API returned an error code
229
+ } else if (err instanceof TimeoutException) {
230
+ // polling timeout exceeded
231
+ }
232
+ }
233
+ ```
234
+
235
+ ---
236
+
237
+ ## TypeScript
238
+
239
+ Type definitions ship with the package — no `@types` install required.
240
+
241
+ ```ts
242
+ import { CapSkip, SolveResult } from 'capskip';
243
+
244
+ const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });
245
+ const result: SolveResult = await solver.recaptcha('...', 'https://example.com');
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Development
251
+
252
+ ```bash
253
+ git clone https://github.com/capskip/capskip-node.git
254
+ cd capskip-node
255
+ npm install
256
+ npm test
257
+ ```
258
+
259
+ The test suite uses Node's built-in test runner — no extra dependencies. See [CONTRIBUTING.md](CONTRIBUTING.md) for the full development workflow.
260
+
261
+ ---
262
+
263
+ ## Links
264
+
265
+ - [CapSkip website](https://capskip.com)
266
+ - [CapSkip API docs](https://capskip.com/api-docs/)
267
+ - [Report an issue](https://github.com/capskip/capskip-node/issues)
268
+ - [npm package](https://www.npmjs.com/package/capskip)
269
+
270
+ ---
271
+
272
+ ## License
273
+
274
+ MIT — see [LICENSE](LICENSE).
package/SECURITY.md ADDED
@@ -0,0 +1,35 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ | Version | Supported |
6
+ |---|---|
7
+ | 1.0.x | Yes |
8
+
9
+ ## Reporting a vulnerability
10
+
11
+ If you discover a security vulnerability in the CapSkip Node.js SDK, please report it responsibly.
12
+
13
+ **Do not** open a public GitHub issue for security vulnerabilities.
14
+
15
+ Instead, email **support@capskip.com** with:
16
+
17
+ - Description of the vulnerability
18
+ - Steps to reproduce
19
+ - Potential impact
20
+ - Suggested fix (if any)
21
+
22
+ We aim to acknowledge reports within 48 hours and provide a status update within 7 days.
23
+
24
+ ## Scope
25
+
26
+ This policy covers the `capskip` npm package in this repository.
27
+
28
+ The CapSkip desktop application itself is maintained separately — report app-level issues to CapSkip support.
29
+
30
+ ## Best practices for SDK users
31
+
32
+ - Do not commit API keys or captcha tokens to version control
33
+ - Use environment variables for `CAPSKIP_API_KEY`
34
+ - CapSkip runs locally — ensure your firewall rules match your security requirements
35
+ - When using proxies, avoid logging credentials in application logs
@@ -0,0 +1,292 @@
1
+ # CapSkip API Reference
2
+
3
+ Complete reference aligned with the [official CapSkip API](https://capskip.com/api-docs/).
4
+
5
+ CapSkip exposes a standard captcha-solver HTTP API on your local machine:
6
+
7
+ ```
8
+ POST http://<host>:<port>/in.php → submit captcha
9
+ GET http://<host>:<port>/res.php → poll result
10
+ ```
11
+
12
+ The SDK only supports the four captcha types documented by CapSkip. Every solve
13
+ method returns a `Promise`.
14
+
15
+ ---
16
+
17
+ ## Supported captcha types
18
+
19
+ | Type | SDK method | `method` (POST) |
20
+ |---|---|---|
21
+ | Image captcha | `normal()` | `post` or `base64` |
22
+ | reCAPTCHA v2 | `recaptcha(sitekey, url)` | `userrecaptcha` |
23
+ | reCAPTCHA v3 | `recaptcha(sitekey, url, { version: 'v3' })` | `userrecaptcha` + `version=v3` |
24
+ | Cloudflare Turnstile | `turnstile()` | `turnstile` |
25
+
26
+ **Proxy** is supported for reCAPTCHA and Turnstile only — not for image captcha.
27
+
28
+ ---
29
+
30
+ ## CapSkip
31
+
32
+ ```js
33
+ const { CapSkip } = require('capskip');
34
+
35
+ const solver = new CapSkip({
36
+ apiKey: 'capskip',
37
+ host: '127.0.0.1',
38
+ port: 8080,
39
+ defaultTimeout: 120,
40
+ recaptchaTimeout: 300,
41
+ pollingInterval: 5, // max seconds between polls; starts at 0.25s and backs off to this
42
+ });
43
+ ```
44
+
45
+ `AsyncCapSkip` is exported as an alias of `CapSkip`.
46
+
47
+ ---
48
+
49
+ ## 1. Image captcha — `normal(file, { json })`
50
+
51
+ ### POST `/in.php`
52
+
53
+ | Parameter | Type | Required | Description |
54
+ |---|---|---|---|
55
+ | `key` | string | Yes | CapSkip API key |
56
+ | `method` | string | Yes | `post` (multipart file) or `base64` |
57
+ | `file` | file | Yes* | Image file when `method=post` |
58
+ | `body` | string | Yes* | Base64 image when `method=base64` |
59
+ | `json` | int | No | `0` plain text (default), `1` JSON |
60
+
61
+ ### GET `/res.php`
62
+
63
+ | Parameter | Type | Required | Description |
64
+ |---|---|---|---|
65
+ | `key` | string | Yes | CapSkip API key |
66
+ | `action` | string | Yes | `get` |
67
+ | `id` | int | Yes | Captcha ID from `in.php` |
68
+ | `json` | int | No | `0` plain text (default), `1` JSON |
69
+
70
+ ### SDK usage
71
+
72
+ ```js
73
+ await solver.normal('captcha.png');
74
+ await solver.normal('https://example.com/captcha.jpg');
75
+ const result = await solver.normal('data:image/png;base64,iVBORw0KGgo...', { json: 1 });
76
+ console.log(result.code);
77
+ ```
78
+
79
+ Only `json` is accepted as an extra parameter. Proxy is **not** supported.
80
+
81
+ ---
82
+
83
+ ## 2. reCAPTCHA v2 — `recaptcha(sitekey, url, { ... })`
84
+
85
+ ### POST `/in.php`
86
+
87
+ | Parameter | Type | Required | Default | Description |
88
+ |---|---|---|---|---|
89
+ | `key` | string | Yes | — | CapSkip API key |
90
+ | `method` | string | Yes | — | `userrecaptcha` |
91
+ | `googlekey` | string | Yes | — | Site key (`data-sitekey` / `k`) |
92
+ | `pageurl` | string | Yes | — | Full page URL |
93
+ | `enterprise` | int | No | `0` | `1` = Enterprise v2 |
94
+ | `invisible` | int | No | `0` | `1` = Invisible reCAPTCHA |
95
+ | `data-s` | string | No | — | Google Search / services `data-s` value |
96
+ | `json` | int | No | `0` | `1` = JSON response |
97
+ | `proxy` | string | No | — | `IP:PORT` or `login:pass@IP:PORT` |
98
+ | `proxytype` | string | No | `HTTP` | `HTTP`, `HTTPS`, `SOCKS5`, `SOCKS5H` |
99
+
100
+ Do **not** send `version`, `action`, or `min_score` for v2.
101
+
102
+ ### GET `/res.php`
103
+
104
+ Same as image captcha poll parameters.
105
+
106
+ ### SDK usage
107
+
108
+ ```js
109
+ // Standard v2
110
+ await solver.recaptcha('...', 'https://example.com');
111
+
112
+ // Invisible v2
113
+ await solver.recaptcha('...', '...', { invisible: 1 });
114
+
115
+ // Enterprise v2
116
+ await solver.recaptcha('...', '...', { enterprise: 1 });
117
+
118
+ // Enterprise v2 with data-s (SDK alias: datas)
119
+ await solver.recaptcha('...', '...', { enterprise: 1, datas: '...' });
120
+
121
+ // With proxy
122
+ await solver.recaptcha('...', '...', {
123
+ proxy: { type: 'HTTPS', uri: 'user:pass@1.2.3.4:3128' },
124
+ });
125
+ ```
126
+
127
+ ---
128
+
129
+ ## 3. reCAPTCHA v3 — `recaptcha(sitekey, url, { version: 'v3', ... })`
130
+
131
+ ### POST `/in.php`
132
+
133
+ | Parameter | Type | Required | Default | Description |
134
+ |---|---|---|---|---|
135
+ | `key` | string | Yes | — | CapSkip API key |
136
+ | `method` | string | Yes | — | `userrecaptcha` |
137
+ | `version` | string | Yes | — | `v3` |
138
+ | `googlekey` | string | Yes | — | Site key |
139
+ | `pageurl` | string | Yes | — | Full page URL |
140
+ | `enterprise` | int | No | `0` | `1` = Enterprise v3 |
141
+ | `action` | string | No | `verify` | Action from `grecaptcha.execute()` |
142
+ | `min_score` | float | No | `0.4` | Minimum acceptable score |
143
+ | `json` | int | No | `0` | `1` = JSON response |
144
+ | `proxy` | string | No | — | Proxy address |
145
+ | `proxytype` | string | No | `HTTP` | Proxy type |
146
+
147
+ Do **not** send `invisible` for v3.
148
+
149
+ ### SDK usage
150
+
151
+ ```js
152
+ await solver.recaptcha('...', 'https://example.com', {
153
+ version: 'v3',
154
+ action: 'submit',
155
+ min_score: 0.7, // or SDK alias: score: 0.7
156
+ enterprise: 0,
157
+ });
158
+ ```
159
+
160
+ ---
161
+
162
+ ## 4. Cloudflare Turnstile — `turnstile(sitekey, url, { ... })`
163
+
164
+ ### POST `/in.php`
165
+
166
+ | Parameter | Type | Required | Description |
167
+ |---|---|---|---|
168
+ | `key` | string | Yes | CapSkip API key |
169
+ | `method` | string | Yes | `turnstile` |
170
+ | `sitekey` | string | Yes | Turnstile sitekey |
171
+ | `pageurl` | string | Yes | Full page URL |
172
+ | `action` | string | No | From `data-action` or `turnstile.render()` |
173
+ | `data` | string | No | `cData` / `data-cdata` |
174
+ | `pagedata` | string | No | `chlPageData` (challenge pages) |
175
+ | `json` | int | No | `0` plain text, `1` JSON |
176
+ | `proxy` | string | No | Proxy address |
177
+ | `proxytype` | string | No | Proxy type |
178
+
179
+ ### GET `/res.php`
180
+
181
+ | Parameter | Type | Required | Description |
182
+ |---|---|---|---|
183
+ | `key` | string | Yes | CapSkip API key |
184
+ | `action` | string | Yes | `get` |
185
+ | `id` | int | Yes | Captcha ID |
186
+ | `json` | int | **Yes** | Must be `1` to receive User-Agent |
187
+
188
+ The SDK **automatically** polls Turnstile results with `json=1` and includes `userAgent` in the result when CapSkip returns it.
189
+
190
+ ### SDK usage
191
+
192
+ ```js
193
+ // Standalone widget
194
+ const result = await solver.turnstile('0x4AAAAAAA...', 'https://example.com');
195
+ console.log(result.code);
196
+ console.log(result.userAgent); // present when CapSkip returns it
197
+
198
+ // Challenge page
199
+ const challenge = await solver.turnstile('0x4AAAAAAA...', 'https://example.com', {
200
+ action: 'managed',
201
+ data: 'cData_value',
202
+ pagedata: 'chlPageData_value',
203
+ });
204
+ // Use challenge.userAgent when submitting the token
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Return value
210
+
211
+ Every solve method resolves to:
212
+
213
+ ```js
214
+ {
215
+ captchaId: '12345',
216
+ code: 'TOKEN_OR_TEXT',
217
+ userAgent: '...', // Turnstile only, when json=1 poll includes it
218
+ }
219
+ ```
220
+
221
+ ---
222
+
223
+ ## SDK parameter aliases
224
+
225
+ Convenience aliases mapped before sending to CapSkip:
226
+
227
+ | SDK alias | CapSkip API param |
228
+ |---|---|
229
+ | `url` | `pageurl` |
230
+ | `score` | `min_score` |
231
+ | `minScore` | `min_score` |
232
+ | `datas` | `data-s` |
233
+ | `data_s` | `data-s` |
234
+ | `proxy` object | `proxy` + `proxytype` strings |
235
+
236
+ ```js
237
+ const proxy = { type: 'HTTPS', uri: 'login:password@1.2.3.4:3128' };
238
+ ```
239
+
240
+ Unsupported parameters (e.g. `numeric` on image captcha, `action` on v2) throw `ValidationException`.
241
+
242
+ ---
243
+
244
+ ## Manual workflow
245
+
246
+ ### `send(params)`
247
+
248
+ Submit without polling. Resolves to the captcha ID string.
249
+
250
+ ```js
251
+ const captchaId = await solver.send({
252
+ method: 'userrecaptcha',
253
+ googlekey: '...',
254
+ pageurl: 'https://example.com',
255
+ });
256
+ ```
257
+
258
+ ### `getResult(id, json = 0)`
259
+
260
+ Poll once. Rejects with `NetworkException` while `CAPCHA_NOT_READY`.
261
+
262
+ ```js
263
+ const { NetworkException } = require('capskip');
264
+
265
+ const code = await solver.getResult(captchaId); // plain text
266
+ const data = await solver.getResult(captchaId, 1); // object when json=1
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Low-level HTTP (ApiClient)
272
+
273
+ ```js
274
+ const { ApiClient } = require('capskip');
275
+
276
+ const client = new ApiClient({ host: '127.0.0.1', port: 8080 });
277
+ await client.in_({ method: 'turnstile', key: 'capskip', sitekey: '...', pageurl: '...' });
278
+ await client.res({ key: 'capskip', action: 'get', id: '12345', json: 1 });
279
+ ```
280
+
281
+ ---
282
+
283
+ ## Exceptions
284
+
285
+ | Exception | When |
286
+ |---|---|
287
+ | `ValidationException` | Invalid/unsupported parameters |
288
+ | `NetworkException` | Connection error, or captcha not ready |
289
+ | `ApiException` | CapSkip API error response |
290
+ | `TimeoutException` | Polling timeout exceeded |
291
+
292
+ All extend `CapSkipError`, so `catch (err) { if (err instanceof CapSkipError) ... }` catches them all.