captchakraken 2.4.0 → 2.6.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.
- package/LICENSE +47 -7
- package/README.md +24 -4
- package/dist/cli-invocation.d.ts +46 -0
- package/dist/cli-invocation.js +68 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +29 -1
- package/dist/limits.d.ts +51 -0
- package/dist/limits.js +54 -0
- package/dist/model-name.d.ts +23 -0
- package/dist/model-name.js +70 -0
- package/dist/playwright-types.d.ts +28 -0
- package/dist/puppeteer-adapter.d.ts +11 -0
- package/dist/puppeteer-adapter.js +23 -0
- package/dist/python-command.d.ts +50 -0
- package/dist/python-command.js +60 -0
- package/dist/slide-geometry.d.ts +33 -0
- package/dist/slide-geometry.js +64 -0
- package/dist/solver.d.ts +214 -12
- package/dist/solver.js +1285 -165
- package/dist/trajectory.d.ts +31 -0
- package/dist/trajectory.js +162 -0
- package/dist/types.d.ts +128 -11
- package/dist/watcher.d.ts +99 -0
- package/dist/watcher.js +129 -0
- package/package.json +2 -5
- package/python/LICENSE +47 -7
- package/python/README.md +25 -8
- package/python/examples/README.md +36 -0
- package/python/examples/_harness.py +182 -94
- package/python/examples/demoHcaptcha.py +2 -1
- package/python/examples/demoRecaptcha.py +2 -1
- package/python/examples/watch_playwright.py +56 -0
- package/python/examples/with_playwright.py +43 -0
- package/python/pyproject.toml +4 -3
- package/python/src/captchakraken/__init__.py +5 -3
- package/python/src/captchakraken/action_types.py +24 -3
- package/python/src/captchakraken/cli.py +212 -2
- package/python/src/captchakraken/config.py +39 -5
- package/python/src/captchakraken/keyframes.py +690 -0
- package/python/src/captchakraken/models.json +120 -0
- package/python/src/captchakraken/page_solver.py +1465 -102
- package/python/src/captchakraken/pinned_model.json +5 -7
- package/python/src/captchakraken/planner.py +253 -59
- package/python/src/captchakraken/prompts.py +485 -0
- package/python/src/captchakraken/solver.py +182 -11
- package/python/src/captchakraken/timing.py +70 -0
- package/python/src/captchakraken/tool_calls/find_grid.py +367 -52
- package/python/src/captchakraken/tool_calls/track_piece.py +96 -0
- package/python/src/captchakraken/trajectory.py +2 -7
- package/python/src/captchakraken/watcher.py +193 -0
- package/python/src/captchakraken/planner_types.py +0 -74
package/LICENSE
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
CaptchaKraken Source-Available License v1.
|
|
2
|
-
Copyright (c) 2026
|
|
1
|
+
CaptchaKraken Source-Available License v1.1
|
|
2
|
+
Copyright (c) 2026 CaptchaKraken LLC. All rights reserved.
|
|
3
3
|
|
|
4
4
|
This license governs use of the CaptchaKraken software in this repository and of
|
|
5
5
|
the associated machine-learning model weights and LoRA adapters published by the
|
|
@@ -26,12 +26,17 @@ You MAY use the Software for:
|
|
|
26
26
|
|
|
27
27
|
Illustrative (non-exhaustive) examples of PERMITTED commercial use:
|
|
28
28
|
- Web scrapers and data-collection pipelines.
|
|
29
|
-
-
|
|
30
|
-
that use the Software as one internal capability.
|
|
29
|
+
- Browser automation you build and operate for your own purposes.
|
|
31
30
|
- QA, testing, and accessibility tooling.
|
|
32
31
|
- Any application where the captcha solve is a means to an end the user
|
|
33
32
|
is actually paying for.
|
|
34
33
|
|
|
34
|
+
(c) RUNNING the Software alongside a stealth, anti-detection, or antidetect
|
|
35
|
+
browser — Camoufox, Puppeteer, Playwright, or any similar tool — to
|
|
36
|
+
automate your own work, commercially or otherwise. Section 3(d) restricts
|
|
37
|
+
SHIPPING the Software as a feature of such a browser to other people. It
|
|
38
|
+
does not restrict USING it with one.
|
|
39
|
+
|
|
35
40
|
================================================================================
|
|
36
41
|
3. PROHIBITED USES
|
|
37
42
|
================================================================================
|
|
@@ -51,7 +56,23 @@ holder:
|
|
|
51
56
|
models (e.g. bounding boxes, tile selections, click plans) through a paid
|
|
52
57
|
or public API as a captcha-solving service.
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
(d) Embed, bundle, preinstall, or otherwise ship the Software — or a client,
|
|
60
|
+
wrapper, plugin, or extension for it — as a captcha-solving capability of
|
|
61
|
+
any stealth browser, anti-detection or antidetect browser, browser
|
|
62
|
+
profile or identity manager, or browser-automation product or platform
|
|
63
|
+
that you distribute, host, or otherwise offer to third parties. This
|
|
64
|
+
applies whether the Software ships inside your distribution, is fetched
|
|
65
|
+
on demand, or is merely wired up and advertised as a supported
|
|
66
|
+
integration, and it applies whether or not you charge for it.
|
|
67
|
+
|
|
68
|
+
This restriction is about DISTRIBUTION, not use. Nothing in it limits
|
|
69
|
+
Section 2(c): a person who uses such a browser may install and run the
|
|
70
|
+
Software with it freely.
|
|
71
|
+
|
|
72
|
+
In short: you may BUILD WITH this Software, and you may USE it with whatever
|
|
73
|
+
browser you like — but you may not SELL THE SOLVE, and you may not SHIP THE
|
|
74
|
+
SOLVE as a feature of a stealth browser or automation product you hand to
|
|
75
|
+
other people.
|
|
55
76
|
|
|
56
77
|
================================================================================
|
|
57
78
|
4. MODEL OUTPUTS
|
|
@@ -65,6 +86,7 @@ outputs as a captcha-solving service is prohibited under Section 3(c).
|
|
|
65
86
|
================================================================================
|
|
66
87
|
If you redistribute the Software (modified or not), you must retain this license
|
|
67
88
|
and this notice, and you must not remove or alter the restrictions in Section 3.
|
|
89
|
+
The right to redistribute granted by Section 1 is subject to Section 3(d).
|
|
68
90
|
|
|
69
91
|
================================================================================
|
|
70
92
|
6. TRADEMARKS
|
|
@@ -95,11 +117,29 @@ not endorse using the Software to violate any third party's terms or rights.
|
|
|
95
117
|
9. COMMERCIAL LICENSING
|
|
96
118
|
================================================================================
|
|
97
119
|
For uses prohibited under Section 3 — including selling captcha-solving as a
|
|
98
|
-
service
|
|
120
|
+
service, and shipping the Software as a built-in capability of a stealth browser
|
|
121
|
+
or automation product — contact the copyright holder to discuss a separate
|
|
122
|
+
commercial license. These uses are licensable; they are not categorically
|
|
123
|
+
refused.
|
|
124
|
+
|
|
125
|
+
================================================================================
|
|
126
|
+
10. VERSION HISTORY
|
|
127
|
+
================================================================================
|
|
128
|
+
v1.1 — Section 3(d) added: shipping the Software as a captcha-solving feature of
|
|
129
|
+
a stealth/anti-detection browser or automation product distributed to
|
|
130
|
+
third parties now requires a commercial license. Section 2(c) added to
|
|
131
|
+
make clear that USING the Software with such a browser remains permitted.
|
|
132
|
+
The v1.0 text listed "stealth / anti-detection browsers" as an
|
|
133
|
+
illustrative PERMITTED commercial use; that example is removed.
|
|
134
|
+
v1.0 — Initial release.
|
|
135
|
+
|
|
136
|
+
Each version applies to the releases distributed under it. Copies obtained under
|
|
137
|
+
v1.0 remain governed by v1.0.
|
|
99
138
|
|
|
100
139
|
--------------------------------------------------------------------------------
|
|
101
140
|
This text is a license, not legal advice. If the boundary between a permitted
|
|
102
|
-
"value-add" product and a prohibited "thin wrapper"
|
|
141
|
+
"value-add" product and a prohibited "thin wrapper", or between using the
|
|
142
|
+
Software with a browser and shipping it as part of one, is unclear for your use
|
|
103
143
|
case, ask by opening an issue or messaging the maintainer on GitHub:
|
|
104
144
|
https://github.com/JWriter20/CaptchaKraken
|
|
105
145
|
--------------------------------------------------------------------------------
|
package/README.md
CHANGED
|
@@ -23,6 +23,20 @@ Playwright-compatible launcher.
|
|
|
23
23
|
- To **self-host** the model, run the repo's `setup.sh` (installs vLLM + weights).
|
|
24
24
|
Otherwise point `VLLM_BASE_URL` at a server you already run.
|
|
25
25
|
|
|
26
|
+
## No GPU? Use the hosted API
|
|
27
|
+
|
|
28
|
+
Point the client at `https://api.captchakraken.com/v1` and run no model at all.
|
|
29
|
+
Sign in at [captchakraken.com/signin](https://captchakraken.com/signin) for a
|
|
30
|
+
`ck_live_…` key, or let the MCP server write one for you:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
claude mcp add captchakraken -- npx -y captchakraken-mcp
|
|
34
|
+
# then call sign_in, then create_api_key
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`create_api_key` writes the key and the endpoint to `~/.captchakraken/credentials`,
|
|
38
|
+
which the client reads on its own — **no environment variables needed**.
|
|
39
|
+
|
|
26
40
|
## Usage
|
|
27
41
|
|
|
28
42
|
```typescript
|
|
@@ -46,11 +60,17 @@ Puppeteer users wrap the page once with `fromPuppeteer(page)`.
|
|
|
46
60
|
|
|
47
61
|
| Variable | Meaning |
|
|
48
62
|
|---|---|
|
|
49
|
-
| `VLLM_BASE_URL` | Inference endpoint (local vLLM,
|
|
63
|
+
| `VLLM_BASE_URL` | Inference endpoint (local vLLM, a server you run, or `https://api.captchakraken.com/v1`) |
|
|
50
64
|
| `CAPTCHA_KRAKEN_API_KEY` | Bearer token (`VLLM_API_KEY` also accepted) |
|
|
51
65
|
|
|
66
|
+
Both fall back to `~/.captchakraken/credentials` when unset, so the hosted API
|
|
67
|
+
needs no environment variables at all.
|
|
68
|
+
|
|
52
69
|
## License
|
|
53
70
|
|
|
54
|
-
**CaptchaKraken Source-Available License v1.
|
|
55
|
-
Build *with* it (scrapers,
|
|
56
|
-
|
|
71
|
+
**CaptchaKraken Source-Available License v1.1** — see [LICENSE](./LICENSE).
|
|
72
|
+
Build *with* it (scrapers, QA tooling) and run it against **any** browser you
|
|
73
|
+
like, stealth or not. You may **not sell the solve itself**, ship a thin wrapper
|
|
74
|
+
(browser extension, hosted solving API), or **bundle it as a built-in feature of
|
|
75
|
+
a stealth/antidetect browser you distribute** — using it with one is fine. Those
|
|
76
|
+
three are licensable, not categorically refused: open an issue to ask.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How the JS client hands a solve request to the bundled Python CLI.
|
|
3
|
+
*
|
|
4
|
+
* The API key used to travel as a POSITIONAL ARGUMENT, and the whole command
|
|
5
|
+
* line was then echoed:
|
|
6
|
+
*
|
|
7
|
+
* console.log(`Executing CaptchaKraken CLI: ${command}`);
|
|
8
|
+
* // Executing CaptchaKraken CLI: python -m captchakraken.cli "shot.png" \
|
|
9
|
+
* // captcha-v12 captchaKrakenApi b8978fa3392... --puzzle-source=hcaptcha
|
|
10
|
+
*
|
|
11
|
+
* Two leaks from one mistake. Argv is world-readable on Linux — any local user
|
|
12
|
+
* can read `/proc/<pid>/cmdline` while the solve runs, and `ps` shows it — and
|
|
13
|
+
* the key was additionally written to stdout, so it landed in CI logs, terminal
|
|
14
|
+
* scrollback, and anything scraping driver output.
|
|
15
|
+
*
|
|
16
|
+
* Secrets go through the environment instead, which is per-process and not
|
|
17
|
+
* world-readable, and the command is built as an ARGV ARRAY for `execFile`
|
|
18
|
+
* rather than a joined string for a shell, so a path with a space or a quote
|
|
19
|
+
* cannot reshape the command.
|
|
20
|
+
*/
|
|
21
|
+
/** Env var the CLI reads the bearer token from. */
|
|
22
|
+
export declare const API_KEY_ENV = "CAPTCHA_KRAKEN_API_KEY";
|
|
23
|
+
/** Historical positional placeholder. The CLI still accepts the slot. */
|
|
24
|
+
export declare const API_PROVIDER = "captchaKrakenApi";
|
|
25
|
+
export interface SolveInvocation {
|
|
26
|
+
imagePath: string;
|
|
27
|
+
model: string;
|
|
28
|
+
puzzleSource: string;
|
|
29
|
+
retryMode?: string | null;
|
|
30
|
+
textMode?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The argv for one solve. No shell, no quoting, and NO CREDENTIAL — the key is
|
|
34
|
+
* supplied by `solveEnv` instead.
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildSolveArgs(invocation: SolveInvocation): string[];
|
|
37
|
+
/** Base env plus the credential, when there is one. */
|
|
38
|
+
export declare function solveEnv(base: NodeJS.ProcessEnv, apiKey?: string): NodeJS.ProcessEnv;
|
|
39
|
+
/**
|
|
40
|
+
* A log line for a solve invocation, with any credential removed.
|
|
41
|
+
*
|
|
42
|
+
* Belt and braces: `buildSolveArgs` no longer carries the key, but this is the
|
|
43
|
+
* function that prints to stdout, so it redacts anything key-shaped regardless
|
|
44
|
+
* of how it got into the array.
|
|
45
|
+
*/
|
|
46
|
+
export declare function redactCommand(command: string, apiKey?: string): string;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* How the JS client hands a solve request to the bundled Python CLI.
|
|
4
|
+
*
|
|
5
|
+
* The API key used to travel as a POSITIONAL ARGUMENT, and the whole command
|
|
6
|
+
* line was then echoed:
|
|
7
|
+
*
|
|
8
|
+
* console.log(`Executing CaptchaKraken CLI: ${command}`);
|
|
9
|
+
* // Executing CaptchaKraken CLI: python -m captchakraken.cli "shot.png" \
|
|
10
|
+
* // captcha-v12 captchaKrakenApi b8978fa3392... --puzzle-source=hcaptcha
|
|
11
|
+
*
|
|
12
|
+
* Two leaks from one mistake. Argv is world-readable on Linux — any local user
|
|
13
|
+
* can read `/proc/<pid>/cmdline` while the solve runs, and `ps` shows it — and
|
|
14
|
+
* the key was additionally written to stdout, so it landed in CI logs, terminal
|
|
15
|
+
* scrollback, and anything scraping driver output.
|
|
16
|
+
*
|
|
17
|
+
* Secrets go through the environment instead, which is per-process and not
|
|
18
|
+
* world-readable, and the command is built as an ARGV ARRAY for `execFile`
|
|
19
|
+
* rather than a joined string for a shell, so a path with a space or a quote
|
|
20
|
+
* cannot reshape the command.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.API_PROVIDER = exports.API_KEY_ENV = void 0;
|
|
24
|
+
exports.buildSolveArgs = buildSolveArgs;
|
|
25
|
+
exports.solveEnv = solveEnv;
|
|
26
|
+
exports.redactCommand = redactCommand;
|
|
27
|
+
/** Env var the CLI reads the bearer token from. */
|
|
28
|
+
exports.API_KEY_ENV = 'CAPTCHA_KRAKEN_API_KEY';
|
|
29
|
+
/** Historical positional placeholder. The CLI still accepts the slot. */
|
|
30
|
+
exports.API_PROVIDER = 'captchaKrakenApi';
|
|
31
|
+
/**
|
|
32
|
+
* The argv for one solve. No shell, no quoting, and NO CREDENTIAL — the key is
|
|
33
|
+
* supplied by `solveEnv` instead.
|
|
34
|
+
*/
|
|
35
|
+
function buildSolveArgs(invocation) {
|
|
36
|
+
const { imagePath, model, puzzleSource, retryMode, textMode } = invocation;
|
|
37
|
+
const args = [
|
|
38
|
+
'-m',
|
|
39
|
+
'captchakraken.cli',
|
|
40
|
+
imagePath,
|
|
41
|
+
model,
|
|
42
|
+
exports.API_PROVIDER,
|
|
43
|
+
`--puzzle-source=${puzzleSource}`,
|
|
44
|
+
];
|
|
45
|
+
if (retryMode)
|
|
46
|
+
args.push(`--retry-mode=${retryMode}`);
|
|
47
|
+
if (textMode)
|
|
48
|
+
args.push('--text-mode');
|
|
49
|
+
return args;
|
|
50
|
+
}
|
|
51
|
+
/** Base env plus the credential, when there is one. */
|
|
52
|
+
function solveEnv(base, apiKey) {
|
|
53
|
+
if (!apiKey)
|
|
54
|
+
return base;
|
|
55
|
+
return { ...base, [exports.API_KEY_ENV]: apiKey };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A log line for a solve invocation, with any credential removed.
|
|
59
|
+
*
|
|
60
|
+
* Belt and braces: `buildSolveArgs` no longer carries the key, but this is the
|
|
61
|
+
* function that prints to stdout, so it redacts anything key-shaped regardless
|
|
62
|
+
* of how it got into the array.
|
|
63
|
+
*/
|
|
64
|
+
function redactCommand(command, apiKey) {
|
|
65
|
+
if (!apiKey)
|
|
66
|
+
return command;
|
|
67
|
+
return command.split(apiKey).join('***');
|
|
68
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export { CaptchaKrakenSolver } from './solver';
|
|
3
|
+
/**
|
|
4
|
+
* The served LoRA name this client will ask for when `model` is left unset —
|
|
5
|
+
* `CAPTCHA_LORA_NAME`, else `models.json`'s `latest`, else the shipped pin.
|
|
6
|
+
*
|
|
7
|
+
* Exported so anything that REPORTS which adapter was driven can ask the client
|
|
8
|
+
* instead of re-deriving the answer. That distinction is not cosmetic: the name
|
|
9
|
+
* selects the prompt generation, so a reporter that guesses it can disagree with
|
|
10
|
+
* the solver and describe a run that never happened.
|
|
11
|
+
*/
|
|
12
|
+
export { resolveLoraName } from './model-name';
|
|
3
13
|
/**
|
|
4
14
|
* Errors raised when the **hosted** CaptchaKraken API refuses a solve — out of
|
|
5
15
|
* credits, rate limited, key rejected, attempt abandoned.
|
|
@@ -77,3 +87,20 @@ export { fromPuppeteer } from './puppeteer-adapter';
|
|
|
77
87
|
* ```
|
|
78
88
|
*/
|
|
79
89
|
export type { Page, PlaywrightPage, PlaywrightFrame, PlaywrightElementHandle } from './playwright-types';
|
|
90
|
+
/**
|
|
91
|
+
* Auto-solve watcher: `solver.watch(page)` installs a background poller that
|
|
92
|
+
* solves captchas as they appear, and returns a handle with `stop()`.
|
|
93
|
+
*
|
|
94
|
+
* Injects nothing into the page, on any platform — see watcher.ts. On Camoufox
|
|
95
|
+
* its DOM reads run in the isolated Juggler world by default.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* const solver = new CaptchaKrakenSolver();
|
|
100
|
+
* const watcher = solver.watch(page, { onSolved: r => console.log(r.isSolved) });
|
|
101
|
+
* // ... your automation ...
|
|
102
|
+
* await watcher.stop();
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export { watchPage } from './watcher';
|
|
106
|
+
export type { CaptchaWatcher, WatchOptions, WatchableSolver } from './watcher';
|
package/dist/index.js
CHANGED
|
@@ -14,10 +14,21 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.fromPuppeteer = exports.CaptchaKrakenAPIError = exports.CaptchaKrakenSolver = void 0;
|
|
17
|
+
exports.watchPage = exports.fromPuppeteer = exports.CaptchaKrakenAPIError = exports.resolveLoraName = exports.CaptchaKrakenSolver = void 0;
|
|
18
18
|
__exportStar(require("./types"), exports);
|
|
19
19
|
var solver_1 = require("./solver");
|
|
20
20
|
Object.defineProperty(exports, "CaptchaKrakenSolver", { enumerable: true, get: function () { return solver_1.CaptchaKrakenSolver; } });
|
|
21
|
+
/**
|
|
22
|
+
* The served LoRA name this client will ask for when `model` is left unset —
|
|
23
|
+
* `CAPTCHA_LORA_NAME`, else `models.json`'s `latest`, else the shipped pin.
|
|
24
|
+
*
|
|
25
|
+
* Exported so anything that REPORTS which adapter was driven can ask the client
|
|
26
|
+
* instead of re-deriving the answer. That distinction is not cosmetic: the name
|
|
27
|
+
* selects the prompt generation, so a reporter that guesses it can disagree with
|
|
28
|
+
* the solver and describe a run that never happened.
|
|
29
|
+
*/
|
|
30
|
+
var model_name_1 = require("./model-name");
|
|
31
|
+
Object.defineProperty(exports, "resolveLoraName", { enumerable: true, get: function () { return model_name_1.resolveLoraName; } });
|
|
21
32
|
/**
|
|
22
33
|
* Errors raised when the **hosted** CaptchaKraken API refuses a solve — out of
|
|
23
34
|
* credits, rate limited, key rejected, attempt abandoned.
|
|
@@ -64,3 +75,20 @@ Object.defineProperty(exports, "CaptchaKrakenAPIError", { enumerable: true, get:
|
|
|
64
75
|
*/
|
|
65
76
|
var puppeteer_adapter_1 = require("./puppeteer-adapter");
|
|
66
77
|
Object.defineProperty(exports, "fromPuppeteer", { enumerable: true, get: function () { return puppeteer_adapter_1.fromPuppeteer; } });
|
|
78
|
+
/**
|
|
79
|
+
* Auto-solve watcher: `solver.watch(page)` installs a background poller that
|
|
80
|
+
* solves captchas as they appear, and returns a handle with `stop()`.
|
|
81
|
+
*
|
|
82
|
+
* Injects nothing into the page, on any platform — see watcher.ts. On Camoufox
|
|
83
|
+
* its DOM reads run in the isolated Juggler world by default.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const solver = new CaptchaKrakenSolver();
|
|
88
|
+
* const watcher = solver.watch(page, { onSolved: r => console.log(r.isSolved) });
|
|
89
|
+
* // ... your automation ...
|
|
90
|
+
* await watcher.stop();
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
var watcher_1 = require("./watcher");
|
|
94
|
+
Object.defineProperty(exports, "watchPage", { enumerable: true, get: function () { return watcher_1.watchPage; } });
|
package/dist/limits.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Numbers that must agree with the hosted gateway, stated once.
|
|
3
|
+
*
|
|
4
|
+
* These are not arbitrary defaults. They sit inside an ordering the server
|
|
5
|
+
* enforces, and the ordering — not any individual value — is the property worth
|
|
6
|
+
* protecting. `limits.test.ts` pins it.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The server bills at most this many responses per captcha attempt.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors `MAX_BILLABLE_ROUNDS_PER_SESSION` in captchakraken-gateway's
|
|
12
|
+
* `src/pricing.ts`. Declared here so the ordering below can be asserted; the
|
|
13
|
+
* gateway is what actually enforces it.
|
|
14
|
+
*/
|
|
15
|
+
export declare const SERVER_MAX_BILLABLE_ROUNDS = 5;
|
|
16
|
+
/**
|
|
17
|
+
* The server answers at most this many responses per attempt, then refuses the
|
|
18
|
+
* eleventh with 409 `solve_abandoned`.
|
|
19
|
+
*
|
|
20
|
+
* Mirrors `MAX_ROUNDS_PER_SESSION` in the gateway.
|
|
21
|
+
*/
|
|
22
|
+
export declare const SERVER_MAX_SERVED_ROUNDS = 10;
|
|
23
|
+
/**
|
|
24
|
+
* How many click → refresh → re-solve rounds the client will attempt on one
|
|
25
|
+
* reCAPTCHA 3x3 dynamic puzzle.
|
|
26
|
+
*
|
|
27
|
+
* ── WHY 8, AND WHY IT MUST STAY STRICTLY BETWEEN 5 AND 10 ───────────────────
|
|
28
|
+
*
|
|
29
|
+
* The three numbers describe one attempt in three stages:
|
|
30
|
+
*
|
|
31
|
+
* rounds 1–5 billed
|
|
32
|
+
* rounds 6–10 served, and comped
|
|
33
|
+
* round 11 refused — 409, the attempt is over
|
|
34
|
+
*
|
|
35
|
+
* 8 sits strictly inside that, with room on both sides, and both margins do a
|
|
36
|
+
* job:
|
|
37
|
+
*
|
|
38
|
+
* Above 5 — rounds 6, 7 and 8 are free. A puzzle that needs a couple of extra
|
|
39
|
+
* passes still gets them, and the customer is not charged for our model
|
|
40
|
+
* needing another look. Dropping to 5 would abandon winnable puzzles at
|
|
41
|
+
* exactly the point where continuing costs the customer nothing.
|
|
42
|
+
*
|
|
43
|
+
* Below 10 — a well-behaved client gives up BEFORE the server refuses it.
|
|
44
|
+
* Raising this to 10 or beyond means the normal end of a hard puzzle is a
|
|
45
|
+
* 409 rather than a clean local stop, which turns an ordinary outcome into an
|
|
46
|
+
* error the caller has to interpret.
|
|
47
|
+
*
|
|
48
|
+
* So: if the billable cap moves, re-derive this and move the test in the same
|
|
49
|
+
* commit. Changing one number alone silently breaks one of the two margins.
|
|
50
|
+
*/
|
|
51
|
+
export declare const DEFAULT_RECAPTCHA_MAX_DYNAMIC_ROUNDS = 8;
|
package/dist/limits.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Numbers that must agree with the hosted gateway, stated once.
|
|
4
|
+
*
|
|
5
|
+
* These are not arbitrary defaults. They sit inside an ordering the server
|
|
6
|
+
* enforces, and the ordering — not any individual value — is the property worth
|
|
7
|
+
* protecting. `limits.test.ts` pins it.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.DEFAULT_RECAPTCHA_MAX_DYNAMIC_ROUNDS = exports.SERVER_MAX_SERVED_ROUNDS = exports.SERVER_MAX_BILLABLE_ROUNDS = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* The server bills at most this many responses per captcha attempt.
|
|
13
|
+
*
|
|
14
|
+
* Mirrors `MAX_BILLABLE_ROUNDS_PER_SESSION` in captchakraken-gateway's
|
|
15
|
+
* `src/pricing.ts`. Declared here so the ordering below can be asserted; the
|
|
16
|
+
* gateway is what actually enforces it.
|
|
17
|
+
*/
|
|
18
|
+
exports.SERVER_MAX_BILLABLE_ROUNDS = 5;
|
|
19
|
+
/**
|
|
20
|
+
* The server answers at most this many responses per attempt, then refuses the
|
|
21
|
+
* eleventh with 409 `solve_abandoned`.
|
|
22
|
+
*
|
|
23
|
+
* Mirrors `MAX_ROUNDS_PER_SESSION` in the gateway.
|
|
24
|
+
*/
|
|
25
|
+
exports.SERVER_MAX_SERVED_ROUNDS = 10;
|
|
26
|
+
/**
|
|
27
|
+
* How many click → refresh → re-solve rounds the client will attempt on one
|
|
28
|
+
* reCAPTCHA 3x3 dynamic puzzle.
|
|
29
|
+
*
|
|
30
|
+
* ── WHY 8, AND WHY IT MUST STAY STRICTLY BETWEEN 5 AND 10 ───────────────────
|
|
31
|
+
*
|
|
32
|
+
* The three numbers describe one attempt in three stages:
|
|
33
|
+
*
|
|
34
|
+
* rounds 1–5 billed
|
|
35
|
+
* rounds 6–10 served, and comped
|
|
36
|
+
* round 11 refused — 409, the attempt is over
|
|
37
|
+
*
|
|
38
|
+
* 8 sits strictly inside that, with room on both sides, and both margins do a
|
|
39
|
+
* job:
|
|
40
|
+
*
|
|
41
|
+
* Above 5 — rounds 6, 7 and 8 are free. A puzzle that needs a couple of extra
|
|
42
|
+
* passes still gets them, and the customer is not charged for our model
|
|
43
|
+
* needing another look. Dropping to 5 would abandon winnable puzzles at
|
|
44
|
+
* exactly the point where continuing costs the customer nothing.
|
|
45
|
+
*
|
|
46
|
+
* Below 10 — a well-behaved client gives up BEFORE the server refuses it.
|
|
47
|
+
* Raising this to 10 or beyond means the normal end of a hard puzzle is a
|
|
48
|
+
* 409 rather than a clean local stop, which turns an ordinary outcome into an
|
|
49
|
+
* error the caller has to interpret.
|
|
50
|
+
*
|
|
51
|
+
* So: if the billable cap moves, re-derive this and move the test in the same
|
|
52
|
+
* commit. Changing one number alone silently breaks one of the two margins.
|
|
53
|
+
*/
|
|
54
|
+
exports.DEFAULT_RECAPTCHA_MAX_DYNAMIC_ROUNDS = 8;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the bundled Python engine lives — the copy of `models.json` this client
|
|
3
|
+
* actually answers from.
|
|
4
|
+
*
|
|
5
|
+
* Lives HERE rather than in solver.ts because the registry lookup below is its
|
|
6
|
+
* only remaining caller-visible use, and a second copy of this path logic is
|
|
7
|
+
* how the two ports drifted apart in the first place. solver.ts imports it.
|
|
8
|
+
*
|
|
9
|
+
* When installed from npm this file is in `<pkgRoot>/dist` (compiled) or
|
|
10
|
+
* `<pkgRoot>/src` (dev), and published packages bundle the engine at
|
|
11
|
+
* `<pkgRoot>/python` (copied in by scripts/copy-python.mjs at build time). In
|
|
12
|
+
* the source monorepo it instead lives at the sibling `../python`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getBundledCliRoot(): string;
|
|
15
|
+
/**
|
|
16
|
+
* `cliRoot` is optional so a caller that only wants to REPORT the name — a test
|
|
17
|
+
* harness recording which adapter it drove — can ask without reconstructing the
|
|
18
|
+
* engine's layout. Re-deriving it is the same mistake one level down.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveLoraName({ cliRoot, env }?: {
|
|
21
|
+
cliRoot?: string;
|
|
22
|
+
env?: NodeJS.ProcessEnv;
|
|
23
|
+
}): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getBundledCliRoot = getBundledCliRoot;
|
|
7
|
+
exports.resolveLoraName = resolveLoraName;
|
|
8
|
+
/**
|
|
9
|
+
* Which served LoRA name this client asks for — the SAME answer the Python
|
|
10
|
+
* client's `config.lora_name()` gives, and for the same reasons.
|
|
11
|
+
*
|
|
12
|
+
* This has to be shared, not re-decided per port. The name is not just routing:
|
|
13
|
+
* the Python CLI feeds it to `prompts.resolve()`, which maps it through
|
|
14
|
+
* models.json to a PROMPT GENERATION. A port that picks a different name sends
|
|
15
|
+
* a different generation of prompts to the same weights, and nothing errors for
|
|
16
|
+
* any family both generations have a prompt for — it just answers worse. That
|
|
17
|
+
* is the failure models.json exists to prevent, and the JS port shipped it:
|
|
18
|
+
* a hardcoded `'captcha'` (CaptchaKraken_v1.1, generation 1) against the
|
|
19
|
+
* generation-2 adapter `latest` has named since v1.2.
|
|
20
|
+
*
|
|
21
|
+
* Precedence mirrors config.py exactly:
|
|
22
|
+
* 1. CAPTCHA_LORA_NAME — an explicit pin always wins, and pinning is opt-in.
|
|
23
|
+
* 2. models.json's `latest` entry, so the default model and the default
|
|
24
|
+
* prompt generation move forward together and cannot get out of step.
|
|
25
|
+
* 3. pinned_model.json, so a hand-edited or older manifest still resolves.
|
|
26
|
+
*/
|
|
27
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
28
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
29
|
+
/**
|
|
30
|
+
* Where the bundled Python engine lives — the copy of `models.json` this client
|
|
31
|
+
* actually answers from.
|
|
32
|
+
*
|
|
33
|
+
* Lives HERE rather than in solver.ts because the registry lookup below is its
|
|
34
|
+
* only remaining caller-visible use, and a second copy of this path logic is
|
|
35
|
+
* how the two ports drifted apart in the first place. solver.ts imports it.
|
|
36
|
+
*
|
|
37
|
+
* When installed from npm this file is in `<pkgRoot>/dist` (compiled) or
|
|
38
|
+
* `<pkgRoot>/src` (dev), and published packages bundle the engine at
|
|
39
|
+
* `<pkgRoot>/python` (copied in by scripts/copy-python.mjs at build time). In
|
|
40
|
+
* the source monorepo it instead lives at the sibling `../python`.
|
|
41
|
+
*/
|
|
42
|
+
function getBundledCliRoot() {
|
|
43
|
+
const bundled = node_path_1.default.resolve(__dirname, '..', 'python');
|
|
44
|
+
if (node_fs_1.default.existsSync(bundled))
|
|
45
|
+
return bundled;
|
|
46
|
+
return node_path_1.default.resolve(__dirname, '..', '..', 'python');
|
|
47
|
+
}
|
|
48
|
+
function readJson(cliRoot, name) {
|
|
49
|
+
return JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(cliRoot, 'src', 'captchakraken', name), 'utf-8'));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `cliRoot` is optional so a caller that only wants to REPORT the name — a test
|
|
53
|
+
* harness recording which adapter it drove — can ask without reconstructing the
|
|
54
|
+
* engine's layout. Re-deriving it is the same mistake one level down.
|
|
55
|
+
*/
|
|
56
|
+
function resolveLoraName({ cliRoot = getBundledCliRoot(), env = process.env } = {}) {
|
|
57
|
+
if (env.CAPTCHA_LORA_NAME)
|
|
58
|
+
return env.CAPTCHA_LORA_NAME;
|
|
59
|
+
try {
|
|
60
|
+
const reg = readJson(cliRoot, 'models.json');
|
|
61
|
+
const name = reg?.models?.[reg?.latest]?.lora_name;
|
|
62
|
+
if (typeof name === 'string' && name)
|
|
63
|
+
return name;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// A missing or broken registry falls through to the pin, never throws —
|
|
67
|
+
// same contract as config.py's `_registry_default`.
|
|
68
|
+
}
|
|
69
|
+
return readJson(cliRoot, 'pinned_model.json').lora_name;
|
|
70
|
+
}
|
|
@@ -59,6 +59,13 @@ export interface PlaywrightElementHandle {
|
|
|
59
59
|
isVisible(): Promise<boolean>;
|
|
60
60
|
/** The element's text content, or null. */
|
|
61
61
|
textContent(): Promise<string | null>;
|
|
62
|
+
/**
|
|
63
|
+
* First matching element WITHIN this element's subtree, or null. The vendors
|
|
64
|
+
* that render into the host page rather than an iframe (GeeTest, Yidun,
|
|
65
|
+
* BotDetect, …) have no Frame to scope against, so the widget element is the
|
|
66
|
+
* boundary that keeps a generic selector off the rest of the document.
|
|
67
|
+
*/
|
|
68
|
+
$(selector: string): Promise<PlaywrightElementHandle | null>;
|
|
62
69
|
}
|
|
63
70
|
/**
|
|
64
71
|
* Structural subset of Playwright's `Frame` (an iframe's content document).
|
|
@@ -96,6 +103,19 @@ export interface PlaywrightPage {
|
|
|
96
103
|
clickCount?: number;
|
|
97
104
|
}): Promise<void>;
|
|
98
105
|
};
|
|
106
|
+
/**
|
|
107
|
+
* Key input. Used to type a distorted-text captcha's answer character by
|
|
108
|
+
* character — `fill()` would set the value with no keystrokes at all, and
|
|
109
|
+
* these are the vendors that score typing cadence.
|
|
110
|
+
*/
|
|
111
|
+
keyboard: {
|
|
112
|
+
type(text: string, options?: {
|
|
113
|
+
delay?: number;
|
|
114
|
+
}): Promise<void>;
|
|
115
|
+
press(key: string, options?: {
|
|
116
|
+
delay?: number;
|
|
117
|
+
}): Promise<void>;
|
|
118
|
+
};
|
|
99
119
|
/** Sleep `timeout` ms (Playwright's own timer). */
|
|
100
120
|
waitForTimeout(timeout: number): Promise<void>;
|
|
101
121
|
/** Wait for a selector to reach the given state; resolves to the handle (or null). */
|
|
@@ -109,6 +129,14 @@ export interface PlaywrightPage {
|
|
|
109
129
|
$(selector: string): Promise<PlaywrightElementHandle | null>;
|
|
110
130
|
/** All matching element handles. */
|
|
111
131
|
$$(selector: string): Promise<PlaywrightElementHandle[]>;
|
|
132
|
+
/**
|
|
133
|
+
* Whether the page has been closed. OPTIONAL because it is the one member
|
|
134
|
+
* here the solver itself never needs — the auto-solve watcher uses it to end
|
|
135
|
+
* its loop when the caller closes the browser out from under it, and falls
|
|
136
|
+
* back to treating the page as open when a launcher does not expose it.
|
|
137
|
+
* Playwright and Puppeteer both do.
|
|
138
|
+
*/
|
|
139
|
+
isClosed?(): boolean;
|
|
112
140
|
/** Run `pageFunction` against the first matching element, in the page context. */
|
|
113
141
|
$eval<R>(selector: string, pageFunction: (element: Element) => R, arg?: any): Promise<R>;
|
|
114
142
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* - `handle.getAttribute(name)` → `handle.evaluate((el,n)=>el.getAttribute(n), name)`
|
|
15
15
|
* - `handle.textContent()` → `handle.evaluate(el=>el.textContent)`
|
|
16
16
|
* - `handle.scrollIntoViewIfNeeded()`→ `handle.scrollIntoView()`
|
|
17
|
+
* - `page.isClosed()` → same, but must be forwarded explicitly
|
|
17
18
|
* Everything else (`$`, `$$`, `$eval`, `waitForFunction`, `mouse.*`,
|
|
18
19
|
* `screenshot({path})`, `boundingBox`, `contentFrame`, `isVisible`) is
|
|
19
20
|
* call-compatible and passed straight through.
|
|
@@ -42,6 +43,7 @@ interface PuppeteerElementHandle {
|
|
|
42
43
|
scrollIntoView(): Promise<void>;
|
|
43
44
|
isVisible(): Promise<boolean>;
|
|
44
45
|
evaluate(pageFunction: (el: any, ...args: any[]) => any, ...args: any[]): Promise<any>;
|
|
46
|
+
$(selector: string): Promise<PuppeteerElementHandle | null>;
|
|
45
47
|
}
|
|
46
48
|
interface PuppeteerFrame {
|
|
47
49
|
$(selector: string): Promise<PuppeteerElementHandle | null>;
|
|
@@ -56,11 +58,20 @@ interface PuppeteerPage {
|
|
|
56
58
|
down(options?: any): Promise<void>;
|
|
57
59
|
up(options?: any): Promise<void>;
|
|
58
60
|
};
|
|
61
|
+
keyboard: {
|
|
62
|
+
type(text: string, options?: {
|
|
63
|
+
delay?: number;
|
|
64
|
+
}): Promise<void>;
|
|
65
|
+
press(key: string, options?: any): Promise<void>;
|
|
66
|
+
down(key: string, options?: any): Promise<void>;
|
|
67
|
+
up(key: string): Promise<void>;
|
|
68
|
+
};
|
|
59
69
|
waitForSelector(selector: string, options?: any): Promise<PuppeteerElementHandle | null>;
|
|
60
70
|
viewport(): ViewportSize | null;
|
|
61
71
|
$(selector: string): Promise<PuppeteerElementHandle | null>;
|
|
62
72
|
$$(selector: string): Promise<PuppeteerElementHandle[]>;
|
|
63
73
|
$eval(selector: string, pageFunction: (element: Element) => any, ...args: any[]): Promise<any>;
|
|
74
|
+
isClosed(): boolean;
|
|
64
75
|
}
|
|
65
76
|
/**
|
|
66
77
|
* Wrap a Puppeteer `Page` so it satisfies the solver's structural Playwright
|