captchakraken 2.3.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.
Files changed (54) hide show
  1. package/LICENSE +47 -7
  2. package/README.md +24 -4
  3. package/dist/cli-invocation.d.ts +46 -0
  4. package/dist/cli-invocation.js +68 -0
  5. package/dist/errors.d.ts +45 -0
  6. package/dist/errors.js +74 -0
  7. package/dist/index.d.ts +50 -0
  8. package/dist/index.js +52 -1
  9. package/dist/limits.d.ts +51 -0
  10. package/dist/limits.js +54 -0
  11. package/dist/model-name.d.ts +23 -0
  12. package/dist/model-name.js +70 -0
  13. package/dist/playwright-types.d.ts +28 -0
  14. package/dist/puppeteer-adapter.d.ts +11 -0
  15. package/dist/puppeteer-adapter.js +23 -0
  16. package/dist/python-command.d.ts +50 -0
  17. package/dist/python-command.js +60 -0
  18. package/dist/slide-geometry.d.ts +33 -0
  19. package/dist/slide-geometry.js +64 -0
  20. package/dist/solver.d.ts +214 -12
  21. package/dist/solver.js +1297 -165
  22. package/dist/trajectory.d.ts +31 -0
  23. package/dist/trajectory.js +162 -0
  24. package/dist/types.d.ts +128 -11
  25. package/dist/watcher.d.ts +99 -0
  26. package/dist/watcher.js +129 -0
  27. package/package.json +2 -5
  28. package/python/LICENSE +47 -7
  29. package/python/README.md +25 -8
  30. package/python/examples/README.md +36 -0
  31. package/python/examples/_harness.py +182 -94
  32. package/python/examples/demoHcaptcha.py +2 -1
  33. package/python/examples/demoRecaptcha.py +2 -1
  34. package/python/examples/watch_playwright.py +56 -0
  35. package/python/examples/with_playwright.py +43 -0
  36. package/python/pyproject.toml +4 -3
  37. package/python/src/captchakraken/__init__.py +5 -3
  38. package/python/src/captchakraken/action_types.py +24 -3
  39. package/python/src/captchakraken/cli.py +224 -2
  40. package/python/src/captchakraken/config.py +119 -17
  41. package/python/src/captchakraken/errors.py +195 -0
  42. package/python/src/captchakraken/keyframes.py +690 -0
  43. package/python/src/captchakraken/models.json +120 -0
  44. package/python/src/captchakraken/page_solver.py +1465 -102
  45. package/python/src/captchakraken/pinned_model.json +5 -7
  46. package/python/src/captchakraken/planner.py +328 -73
  47. package/python/src/captchakraken/prompts.py +485 -0
  48. package/python/src/captchakraken/solver.py +182 -11
  49. package/python/src/captchakraken/timing.py +70 -0
  50. package/python/src/captchakraken/tool_calls/find_grid.py +367 -52
  51. package/python/src/captchakraken/tool_calls/track_piece.py +96 -0
  52. package/python/src/captchakraken/trajectory.py +2 -7
  53. package/python/src/captchakraken/watcher.py +193 -0
  54. package/python/src/captchakraken/planner_types.py +0 -74
package/LICENSE CHANGED
@@ -1,5 +1,5 @@
1
- CaptchaKraken Source-Available License v1.0
2
- Copyright (c) 2026 JobHarvest / JWriter20. All rights reserved.
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
- - Stealth / anti-detection browsers and browser automation frameworks
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
- In short: you may BUILD WITH this Software, but you may not SELL THE SOLVE.
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 — contact the copyright holder to discuss a separate commercial license.
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" is unclear for your use
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, or a server you run) |
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.0** — see [LICENSE](./LICENSE).
55
- Build *with* it (scrapers, stealth browsers, QA tooling); you may **not sell the
56
- solve itself** or ship a thin wrapper (browser extension, hosted solving API).
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
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Hosted-API refusals, carried across the Python subprocess boundary.
3
+ *
4
+ * The TypeScript driver does not talk to the model; it shells out to the Python
5
+ * CLI, which does. So every hosted-API error arrives here as text on stderr and
6
+ * would, without this, be flattened into
7
+ * `Failed to execute captcha solver CLI: Command failed with exit code 3` —
8
+ * a sentence that tells a camoufox user nothing about the fact that their
9
+ * account is out of credits.
10
+ *
11
+ * The CLI exits 3 and prints `{"error": "...", "ck_error": {...}}`, so the
12
+ * wording is decided once, in Python (`captchakraken/errors.py`), and merely
13
+ * relayed here. Deliberately NOT re-worded on this side: two copies of the same
14
+ * message drift, and the Python one is also what a direct CLI user sees.
15
+ */
16
+ /** The machine-readable half. Mirrors the gateway's `GatewayErrorCode`. */
17
+ export type CaptchaKrakenErrorCode = 'missing_api_key' | 'invalid_api_key' | 'account_suspended' | 'insufficient_credits' | 'rate_limited' | 'solve_abandoned' | 'unrecognized_prompt' | 'invalid_request' | 'request_too_large' | 'upstream_unavailable' | (string & {});
18
+ export declare class CaptchaKrakenAPIError extends Error {
19
+ readonly status: number | undefined;
20
+ readonly code: CaptchaKrakenErrorCode | undefined;
21
+ /** Where to send the user to fix it, when the server named somewhere. */
22
+ readonly resolutionUrl: string | undefined;
23
+ /** Seconds to wait, on `rate_limited`. */
24
+ readonly retryAfterSeconds: number | undefined;
25
+ /** Lets `catch` blocks branch without `instanceof` across module copies. */
26
+ readonly isCaptchaKrakenAPIError = true;
27
+ constructor(message: string, fields?: {
28
+ status?: number;
29
+ code?: string;
30
+ resolutionUrl?: string;
31
+ retryAfterSeconds?: number;
32
+ });
33
+ }
34
+ /**
35
+ * Pull a hosted-API error out of the CLI's stderr, or return null.
36
+ *
37
+ * Scans line by line rather than parsing the whole buffer: stderr also carries
38
+ * timing records and any warning the Python side emitted, so the JSON payload
39
+ * is one line among several and `JSON.parse(stderr)` would simply fail.
40
+ *
41
+ * Returns null for anything that is not recognisably ours, which leaves the
42
+ * caller's existing generic handling in place — an unparseable stderr must not
43
+ * become a confident but wrong billing message.
44
+ */
45
+ export declare function parseApiError(stderr: string): CaptchaKrakenAPIError | null;
package/dist/errors.js ADDED
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /**
3
+ * Hosted-API refusals, carried across the Python subprocess boundary.
4
+ *
5
+ * The TypeScript driver does not talk to the model; it shells out to the Python
6
+ * CLI, which does. So every hosted-API error arrives here as text on stderr and
7
+ * would, without this, be flattened into
8
+ * `Failed to execute captcha solver CLI: Command failed with exit code 3` —
9
+ * a sentence that tells a camoufox user nothing about the fact that their
10
+ * account is out of credits.
11
+ *
12
+ * The CLI exits 3 and prints `{"error": "...", "ck_error": {...}}`, so the
13
+ * wording is decided once, in Python (`captchakraken/errors.py`), and merely
14
+ * relayed here. Deliberately NOT re-worded on this side: two copies of the same
15
+ * message drift, and the Python one is also what a direct CLI user sees.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.CaptchaKrakenAPIError = void 0;
19
+ exports.parseApiError = parseApiError;
20
+ class CaptchaKrakenAPIError extends Error {
21
+ constructor(message, fields = {}) {
22
+ super(message);
23
+ /** Lets `catch` blocks branch without `instanceof` across module copies. */
24
+ this.isCaptchaKrakenAPIError = true;
25
+ this.name = 'CaptchaKrakenAPIError';
26
+ this.status = fields.status;
27
+ this.code = fields.code;
28
+ this.resolutionUrl = fields.resolutionUrl;
29
+ this.retryAfterSeconds = fields.retryAfterSeconds;
30
+ }
31
+ }
32
+ exports.CaptchaKrakenAPIError = CaptchaKrakenAPIError;
33
+ /**
34
+ * Pull a hosted-API error out of the CLI's stderr, or return null.
35
+ *
36
+ * Scans line by line rather than parsing the whole buffer: stderr also carries
37
+ * timing records and any warning the Python side emitted, so the JSON payload
38
+ * is one line among several and `JSON.parse(stderr)` would simply fail.
39
+ *
40
+ * Returns null for anything that is not recognisably ours, which leaves the
41
+ * caller's existing generic handling in place — an unparseable stderr must not
42
+ * become a confident but wrong billing message.
43
+ */
44
+ function parseApiError(stderr) {
45
+ if (!stderr || !stderr.includes('ck_error'))
46
+ return null;
47
+ for (const line of stderr.split('\n')) {
48
+ const trimmed = line.trim();
49
+ if (!trimmed.startsWith('{') || !trimmed.includes('ck_error'))
50
+ continue;
51
+ let parsed;
52
+ try {
53
+ parsed = JSON.parse(trimmed);
54
+ }
55
+ catch {
56
+ continue;
57
+ }
58
+ const details = parsed?.ck_error;
59
+ if (!details || typeof details !== 'object')
60
+ continue;
61
+ const message = typeof parsed.error === 'string' && parsed.error.trim()
62
+ ? parsed.error
63
+ : 'CaptchaKraken refused this solve.';
64
+ return new CaptchaKrakenAPIError(message, {
65
+ status: typeof details.status === 'number' ? details.status : undefined,
66
+ code: typeof details.code === 'string' ? details.code : undefined,
67
+ resolutionUrl: typeof details.resolution_url === 'string' ? details.resolution_url : undefined,
68
+ retryAfterSeconds: typeof details.retry_after_seconds === 'number'
69
+ ? details.retry_after_seconds
70
+ : undefined,
71
+ });
72
+ }
73
+ return null;
74
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,38 @@
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';
13
+ /**
14
+ * Errors raised when the **hosted** CaptchaKraken API refuses a solve — out of
15
+ * credits, rate limited, key rejected, attempt abandoned.
16
+ *
17
+ * `message` is already a complete sentence naming the product and, where one
18
+ * exists, the URL that fixes it, so printing it is enough. `code` is the stable
19
+ * contract to branch on; the prose is not.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { CaptchaKrakenSolver, CaptchaKrakenAPIError } from 'captchakraken';
24
+ *
25
+ * try {
26
+ * await solver.solve(page);
27
+ * } catch (e) {
28
+ * if (e instanceof CaptchaKrakenAPIError && e.code === 'insufficient_credits') {
29
+ * console.error(`Top up: ${e.resolutionUrl}`);
30
+ * } else throw e;
31
+ * }
32
+ * ```
33
+ */
34
+ export { CaptchaKrakenAPIError } from './errors';
35
+ export type { CaptchaKrakenErrorCode } from './errors';
3
36
  /**
4
37
  * Adapter for driving the solver with **Puppeteer** instead of a Playwright
5
38
  * launcher. Puppeteer's `Page` is API-compatible with Playwright's except for a
@@ -54,3 +87,20 @@ export { fromPuppeteer } from './puppeteer-adapter';
54
87
  * ```
55
88
  */
56
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,44 @@ 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.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; } });
32
+ /**
33
+ * Errors raised when the **hosted** CaptchaKraken API refuses a solve — out of
34
+ * credits, rate limited, key rejected, attempt abandoned.
35
+ *
36
+ * `message` is already a complete sentence naming the product and, where one
37
+ * exists, the URL that fixes it, so printing it is enough. `code` is the stable
38
+ * contract to branch on; the prose is not.
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * import { CaptchaKrakenSolver, CaptchaKrakenAPIError } from 'captchakraken';
43
+ *
44
+ * try {
45
+ * await solver.solve(page);
46
+ * } catch (e) {
47
+ * if (e instanceof CaptchaKrakenAPIError && e.code === 'insufficient_credits') {
48
+ * console.error(`Top up: ${e.resolutionUrl}`);
49
+ * } else throw e;
50
+ * }
51
+ * ```
52
+ */
53
+ var errors_1 = require("./errors");
54
+ Object.defineProperty(exports, "CaptchaKrakenAPIError", { enumerable: true, get: function () { return errors_1.CaptchaKrakenAPIError; } });
21
55
  /**
22
56
  * Adapter for driving the solver with **Puppeteer** instead of a Playwright
23
57
  * launcher. Puppeteer's `Page` is API-compatible with Playwright's except for a
@@ -41,3 +75,20 @@ Object.defineProperty(exports, "CaptchaKrakenSolver", { enumerable: true, get: f
41
75
  */
42
76
  var puppeteer_adapter_1 = require("./puppeteer-adapter");
43
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; } });
@@ -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;