captchakraken 3.2.1 → 4.0.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/AGENTS.md +5 -9
- package/LICENSE +165 -145
- package/NOTICE +4 -4
- package/README.md +10 -11
- package/dist/cli-invocation.d.ts +6 -3
- package/dist/cli-invocation.js +26 -10
- package/dist/humanize.d.ts +37 -1
- package/dist/humanize.js +143 -9
- package/dist/index.d.ts +13 -12
- package/dist/index.js +25 -33
- package/dist/kinds.d.ts +15 -2
- package/dist/kinds.js +12 -4
- package/dist/playwright-types.d.ts +3 -0
- package/dist/selectors.d.ts +93 -11
- package/dist/selectors.js +226 -116
- package/dist/solver.d.ts +92 -12
- package/dist/solver.js +562 -203
- package/dist/types.d.ts +53 -8
- package/dist/verdicts.d.ts +3 -0
- package/dist/verdicts.js +6 -0
- package/package.json +4 -4
- package/python/AGENTS.md +7 -11
- package/python/LICENSE +165 -145
- package/python/NOTICE +4 -4
- package/python/README.md +10 -9
- package/python/pyproject.toml +27 -13
- package/python/src/captchakraken/__init__.py +43 -46
- package/python/src/captchakraken/action_types.py +32 -1
- package/python/src/captchakraken/captchakraken.config.json +416 -0
- package/python/src/captchakraken/cli.py +66 -30
- package/python/src/captchakraken/clipvideo.py +258 -0
- package/python/src/captchakraken/humanize.py +141 -10
- package/python/src/captchakraken/image_processor.py +3 -7
- package/python/src/captchakraken/keyframes.py +96 -14
- package/python/src/captchakraken/kinds.py +14 -2
- package/python/src/captchakraken/models.json +28 -3
- package/python/src/captchakraken/page_solver.py +595 -251
- package/python/src/captchakraken/planner.py +298 -47
- package/python/src/captchakraken/pointer/__init__.py +20 -0
- package/python/src/captchakraken/pointer/detector.py +333 -0
- package/python/src/captchakraken/pointer/elements.py +273 -0
- package/python/src/captchakraken/pointer/heads.py +163 -0
- package/python/src/captchakraken/pointer/layout.py +319 -0
- package/python/src/captchakraken/pointer/marks.py +184 -0
- package/python/src/captchakraken/pointer/server.py +146 -0
- package/python/src/captchakraken/pointer/service.py +553 -0
- package/python/src/captchakraken/pointer/vllm_plugin.py +109 -0
- package/python/src/captchakraken/prompts.py +111 -34
- package/python/src/captchakraken/selectors.py +221 -135
- package/python/src/captchakraken/server_manager.py +18 -0
- package/python/src/captchakraken/solver.py +169 -88
- package/python/src/captchakraken/tool_calls/board_painted.py +11 -6
- package/python/src/captchakraken/tool_calls/fill_tracker.py +152 -0
- package/python/src/captchakraken/tool_calls/find_checkbox.py +14 -59
- package/python/src/captchakraken/tool_calls/find_grid.py +273 -1191
- package/python/src/captchakraken/tool_calls/slide_tracker.py +1102 -0
- package/python/src/captchakraken/verdicts.py +16 -0
- package/dist/slide-geometry.d.ts +0 -6
- package/dist/slide-geometry.js +0 -38
- package/python/src/captchakraken/tool_calls/track_piece.py +0 -120
package/python/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
The Python engine and CLI behind <a href="https://github.com/CaptchaKraken/CaptchaKraken">CaptchaKraken</a>.
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
CPU element detection plus a fine-tuned **Qwen3.5-9B** vision model. Give it a
|
|
13
13
|
screenshot of a captcha and it returns the plan to solve it — which tiles to
|
|
14
14
|
select, where to click, what to drag, how far to slide, or what text to type.
|
|
15
15
|
Ships the `captchakraken` command.
|
|
@@ -57,7 +57,7 @@ keyframes and answered with the frame the action belongs to.
|
|
|
57
57
|
## Install
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
pip install captchakraken # client:
|
|
60
|
+
pip install captchakraken # client: CPU element detection + vLLM HTTP planner
|
|
61
61
|
pip install "captchakraken[serve]" # + the serving stack (vLLM/torch) to self-host
|
|
62
62
|
```
|
|
63
63
|
|
|
@@ -107,7 +107,7 @@ solver = CaptchaSolver() # connects to / auto-starts a local vLLM
|
|
|
107
107
|
actions = solver.solve("captcha.png")
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
Element-detector tool subcommands (CPU only, no planner model): `find-grid`, `find-checkbox`,
|
|
111
111
|
`detect-selected`, `grid-cell-states`, and a persistent `serve` worker the browser driver polls.
|
|
112
112
|
|
|
113
113
|
## Configuration (model-agnostic)
|
|
@@ -123,6 +123,9 @@ Everything model-specific lives in `captchakraken.config` and is env-overridable
|
|
|
123
123
|
| `CAPTCHA_LORA_ADAPTER` | Captcha adapter (HF id or path) | `CaptchaKraken/CaptchaKraken-Lora-v1.2` |
|
|
124
124
|
| `CAPTCHA_LORA_NAME` | Served adapter name the client requests | `captcha-v12` |
|
|
125
125
|
| `CAPTCHA_KRAKEN_AUTOSTART` | `0` disables local auto-start | `1` |
|
|
126
|
+
| `CAPTCHA_KRAKEN_DETECTOR` | A local element-detector `.onnx` to run instead of downloading the released one | the released weights, fetched once |
|
|
127
|
+
| `CAPTCHA_KRAKEN_DETECTOR_MODEL` | Which released detector runs: `lite` (MobileNetV3, about 100 ms a board on one vCPU), `standard` (ResNet-18), or `auto`; overrides `detector` in `captchakraken.config.json` | the config's `detector`, shipped as `auto`: `lite` on two usable CPUs or fewer (a container's CPU quota included), else one board is timed and `standard` runs when it fits in 200 ms |
|
|
128
|
+
| `HF_TOKEN` | A Hugging Face token, sent only when downloading the detector weights from a private copy of their repo | unset: the download sends no credential |
|
|
126
129
|
| `CAPTCHA_HUMANIZATION` | How gestures are performed: `mouse`, `mobile` or `none` | `mouse` |
|
|
127
130
|
|
|
128
131
|
**How it moves** is a choice of input device, not a realism dial. `mobile`
|
|
@@ -149,9 +152,7 @@ PageSolver(config=PageSolverConfig(
|
|
|
149
152
|
|
|
150
153
|
## License
|
|
151
154
|
|
|
152
|
-
**
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
a stealth/antidetect browser you distribute** — using it with one is fine. Those
|
|
157
|
-
three are licensable, not categorically refused: open an issue to ask.
|
|
155
|
+
**LGPL-3.0-only** — see [LICENSE](./LICENSE). Use, modify and redistribute it,
|
|
156
|
+
including in commercial and hosted services. If you distribute a modified
|
|
157
|
+
version, keep the licence and copyright notices and make your changes available
|
|
158
|
+
under LGPLv3.
|
package/python/pyproject.toml
CHANGED
|
@@ -4,14 +4,10 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "captchakraken"
|
|
7
|
-
version = "
|
|
8
|
-
description = "Captcha-solving engine and CLI:
|
|
7
|
+
version = "4.0.0"
|
|
8
|
+
description = "Captcha-solving engine and CLI: a v2 pointer model and CNN element detector for image grids, click and drag puzzles, sliders, distorted text and animated challenges. Hosted API or self-hosted on vLLM."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
|
-
# Source-available, NOT GPL. The CaptchaKraken Source-Available License permits
|
|
12
|
-
# commercial use as an internal component but prohibits selling the solve itself,
|
|
13
|
-
# thin-wrapper resale, and bundling it into a stealth browser someone else ships
|
|
14
|
-
# — see LICENSE. (GPL-3.0 would wrongly permit all three.)
|
|
15
11
|
license = { file = "LICENSE" }
|
|
16
12
|
authors = [{ name = "Jake Writer" }]
|
|
17
13
|
keywords = [
|
|
@@ -31,11 +27,11 @@ classifiers = [
|
|
|
31
27
|
"Topic :: Scientific/Engineering :: Image Recognition",
|
|
32
28
|
"Topic :: Internet :: WWW/HTTP :: Browsers",
|
|
33
29
|
"Topic :: Software Development :: Testing",
|
|
34
|
-
"License ::
|
|
30
|
+
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
|
|
35
31
|
"Typing :: Typed",
|
|
36
32
|
]
|
|
37
33
|
|
|
38
|
-
# Core = the lightweight CLIENT:
|
|
34
|
+
# Core = the lightweight CLIENT: CPU element detection + the HTTP planner that
|
|
39
35
|
# talks to a vLLM server (local or remote). This is all you need to SOLVE
|
|
40
36
|
# captchas against an existing endpoint. The heavy serving stack (vllm/torch)
|
|
41
37
|
# is deliberately NOT here — see the `serve` extra.
|
|
@@ -53,19 +49,31 @@ classifiers = [
|
|
|
53
49
|
# against the floor before changing any of these, and see TRIBAL_KNOWLEDGE.md.
|
|
54
50
|
dependencies = [
|
|
55
51
|
"pydantic==2.13.5",
|
|
56
|
-
"pillow==
|
|
52
|
+
"pillow==13.0.0",
|
|
57
53
|
# 2.3.x rather than 2.2.6 because `cursory` requires `numpy~=2.3.3` and the
|
|
58
|
-
# old pin was ours
|
|
59
|
-
# `numpy>=2`. Moved deliberately as part of the major bump.
|
|
54
|
+
# old pin was ours. Moved deliberately as part of the major bump.
|
|
60
55
|
"numpy==2.3.5",
|
|
61
|
-
|
|
56
|
+
# Runs the element detector (find_grid, find_checkbox) on the CPU. Its
|
|
57
|
+
# wheels cover CPython 3.11-3.14 on Linux x86_64/aarch64, Windows and Apple
|
|
58
|
+
# silicon macOS; there is NO Intel-macOS wheel, so the client does not
|
|
59
|
+
# install there. Weights are fetched at first use, not shipped (detector.py).
|
|
60
|
+
"onnxruntime==1.31.1",
|
|
61
|
+
# The slide and hold trackers' image arithmetic: smoothing, morphology,
|
|
62
|
+
# labelling, distance transforms and FFT correlation. 1.17.1 is the newest
|
|
63
|
+
# release whose Requires-Python (>=3.11) covers the floor; 1.18 needs 3.12.
|
|
64
|
+
"scipy==1.17.1",
|
|
65
|
+
# Decodes the first frame of a video passed to `solve` (.mp4, .avi, .webm).
|
|
66
|
+
# 18.1.0 is the newest release whose wheels (cp311-abi3) install on the
|
|
67
|
+
# 3.11 floor; 19.x declares `Requires-Python >=3.12`. It has no runtime
|
|
68
|
+
# dependencies, so it does not constrain numpy.
|
|
69
|
+
"av==18.1.0",
|
|
62
70
|
"requests==2.34.2",
|
|
63
71
|
"python-dotenv==1.2.3",
|
|
64
72
|
# The mouse. LGPL-3.0-or-later, used as an ordinary installed dependency and
|
|
65
73
|
# never vendored or bundled, which is the arrangement the licence is written
|
|
66
74
|
# for — see NOTICE. Its TypeScript port `cursory-js` is the JS driver's
|
|
67
75
|
# dependency, and the two agree bit for bit on a seed.
|
|
68
|
-
"cursory==2.
|
|
76
|
+
"cursory==2.1.0",
|
|
69
77
|
]
|
|
70
78
|
|
|
71
79
|
[project.optional-dependencies]
|
|
@@ -90,6 +98,12 @@ dev = [
|
|
|
90
98
|
[project.scripts]
|
|
91
99
|
captchakraken = "captchakraken.cli:main"
|
|
92
100
|
|
|
101
|
+
# vLLM loads general plugins in every process it starts (the API server and each
|
|
102
|
+
# engine core), so the pointer model's architecture is known wherever a
|
|
103
|
+
# checkpoint naming it is loaded. Inert unless vLLM is installed (`serve` extra).
|
|
104
|
+
[project.entry-points."vllm.general_plugins"]
|
|
105
|
+
captchakraken_pointer = "captchakraken.pointer.vllm_plugin:register"
|
|
106
|
+
|
|
93
107
|
[project.urls]
|
|
94
108
|
Homepage = "https://captchakraken.com"
|
|
95
109
|
Documentation = "https://github.com/CaptchaKraken/CaptchaKraken#readme"
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""The published surface: what a caller constructs, configures, receives or implements. Everything else is internal
|
|
2
|
+
and can change in any release; add a name here only when a caller needs to write it."""
|
|
3
|
+
|
|
1
4
|
from pathlib import Path
|
|
2
5
|
|
|
3
6
|
try:
|
|
@@ -15,16 +18,18 @@ from .action_types import (
|
|
|
15
18
|
TypeAction,
|
|
16
19
|
WaitAction,
|
|
17
20
|
)
|
|
18
|
-
from .image_processor import ImageProcessor
|
|
19
|
-
from .overlay import add_overlays_to_image
|
|
20
|
-
|
|
21
21
|
from .errors import CaptchaKrakenAPIError
|
|
22
|
+
from .humanize import (
|
|
23
|
+
Humanizer,
|
|
24
|
+
MobileHumanizer,
|
|
25
|
+
MouseHumanizer,
|
|
26
|
+
NullHumanizer,
|
|
27
|
+
TouchBackend,
|
|
28
|
+
)
|
|
22
29
|
from .kinds import (
|
|
23
30
|
ActionKind,
|
|
24
|
-
Availability,
|
|
25
31
|
ErrorCode,
|
|
26
32
|
HumanizationMode,
|
|
27
|
-
KeyframeMode,
|
|
28
33
|
Outcome,
|
|
29
34
|
PauseKind,
|
|
30
35
|
PromptFamily,
|
|
@@ -34,73 +39,65 @@ from .kinds import (
|
|
|
34
39
|
)
|
|
35
40
|
from .verdicts import RoundVerdict
|
|
36
41
|
|
|
37
|
-
from .humanize import (
|
|
38
|
-
AppiumTouchBackend,
|
|
39
|
-
CdpTouchBackend,
|
|
40
|
-
Humanizer,
|
|
41
|
-
MobileHumanizer,
|
|
42
|
-
MouseHumanizer,
|
|
43
|
-
NullHumanizer,
|
|
44
|
-
TouchBackend,
|
|
45
|
-
TouchscreenTouchBackend,
|
|
46
|
-
)
|
|
47
|
-
|
|
48
42
|
try:
|
|
49
|
-
from .planner import ActionPlanner
|
|
50
43
|
from .solver import CaptchaSolver, solve_captcha
|
|
51
44
|
|
|
52
|
-
from .page_solver import PageSolver, SolveResult, solve_captcha_on_page
|
|
53
|
-
from .selectors import
|
|
45
|
+
from .page_solver import CaptchaSolveError, PageSolver, PageSolverConfig, SolveResult, solve_captcha_on_page
|
|
46
|
+
from .selectors import DEFAULT_CONFIG_PATH, SelectorConfigError, VendorSelectors, load_config
|
|
54
47
|
from .watcher import CaptchaWatcher
|
|
55
48
|
except ModuleNotFoundError:
|
|
56
|
-
ActionPlanner = None
|
|
57
49
|
CaptchaSolver = None
|
|
58
50
|
solve_captcha = None
|
|
51
|
+
CaptchaSolveError = None
|
|
59
52
|
PageSolver = None
|
|
60
|
-
|
|
61
|
-
VendorSelectors = None
|
|
62
|
-
CaptchaWatcher = None
|
|
53
|
+
PageSolverConfig = None
|
|
63
54
|
SolveResult = None
|
|
64
55
|
solve_captcha_on_page = None
|
|
56
|
+
VendorSelectors = None
|
|
57
|
+
DEFAULT_CONFIG_PATH = None
|
|
58
|
+
SelectorConfigError = None
|
|
59
|
+
load_config = None
|
|
60
|
+
CaptchaWatcher = None
|
|
65
61
|
|
|
66
62
|
__all__ = [
|
|
67
|
-
|
|
68
|
-
"solve_captcha",
|
|
63
|
+
# Solving a page
|
|
69
64
|
"PageSolver",
|
|
70
|
-
"
|
|
71
|
-
"VendorSelectors",
|
|
72
|
-
"CaptchaWatcher",
|
|
65
|
+
"PageSolverConfig",
|
|
73
66
|
"SolveResult",
|
|
67
|
+
"CaptchaSolveError",
|
|
74
68
|
"solve_captcha_on_page",
|
|
75
|
-
"
|
|
76
|
-
|
|
69
|
+
"CaptchaWatcher",
|
|
70
|
+
# Solving an image
|
|
71
|
+
"CaptchaSolver",
|
|
72
|
+
"solve_captcha",
|
|
77
73
|
"CaptchaAction",
|
|
78
74
|
"ClickAction",
|
|
79
75
|
"DragAction",
|
|
80
76
|
"TypeAction",
|
|
81
77
|
"WaitAction",
|
|
82
|
-
"
|
|
78
|
+
"ActionKind",
|
|
79
|
+
"RetryMode",
|
|
80
|
+
"PromptFamily",
|
|
81
|
+
# Configuration
|
|
82
|
+
"DEFAULT_CONFIG_PATH",
|
|
83
|
+
"SelectorConfigError",
|
|
84
|
+
"VendorSelectors",
|
|
85
|
+
"load_config",
|
|
86
|
+
"Vendor",
|
|
87
|
+
"HumanizationMode",
|
|
88
|
+
# Results and errors
|
|
89
|
+
"RoundVerdict",
|
|
90
|
+
"Outcome",
|
|
91
|
+
"Verdict",
|
|
92
|
+
"ErrorCode",
|
|
83
93
|
"CaptchaKrakenAPIError",
|
|
94
|
+
# Humanizers: the built-in three, and what one of your own implements
|
|
84
95
|
"Humanizer",
|
|
85
96
|
"MouseHumanizer",
|
|
86
97
|
"MobileHumanizer",
|
|
87
98
|
"NullHumanizer",
|
|
88
99
|
"TouchBackend",
|
|
89
|
-
"CdpTouchBackend",
|
|
90
|
-
"AppiumTouchBackend",
|
|
91
|
-
"TouchscreenTouchBackend",
|
|
92
|
-
"ActionKind",
|
|
93
|
-
"Availability",
|
|
94
|
-
"ErrorCode",
|
|
95
|
-
"HumanizationMode",
|
|
96
|
-
"KeyframeMode",
|
|
97
|
-
"Outcome",
|
|
98
100
|
"PauseKind",
|
|
99
|
-
"PromptFamily",
|
|
100
|
-
"RetryMode",
|
|
101
|
-
"RoundVerdict",
|
|
102
|
-
"Vendor",
|
|
103
|
-
"Verdict",
|
|
104
101
|
]
|
|
105
102
|
|
|
106
|
-
__version__ = "
|
|
103
|
+
__version__ = "4.0.0"
|
|
@@ -22,6 +22,8 @@ class Action(BaseModel):
|
|
|
22
22
|
action: ActionKind
|
|
23
23
|
await_keyframe: Optional[str] = None
|
|
24
24
|
frame: Optional[int] = None
|
|
25
|
+
# Seconds into the clip, on prompt generations that watch an animation as video; `frame` on earlier ones.
|
|
26
|
+
t: Optional[float] = None
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
class ClickAction(Action):
|
|
@@ -52,4 +54,33 @@ class DoneAction(Action):
|
|
|
52
54
|
action: Literal[ActionKind.DONE]
|
|
53
55
|
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
class HoldAction(Action):
|
|
58
|
+
"""Press on the box and keep pressing until the widget completes (prompt generation 3). When the model also named
|
|
59
|
+
where the fill starts and where to let go, the driver follows the fill along that line and releases at its end."""
|
|
60
|
+
|
|
61
|
+
action: Literal[ActionKind.HOLD]
|
|
62
|
+
target_bounding_box: BoundingBox
|
|
63
|
+
fill_from_bounding_box: Optional[BoundingBox] = None
|
|
64
|
+
fill_to_bounding_box: Optional[BoundingBox] = None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class RefreshAction(Action):
|
|
68
|
+
"""Load a new puzzle instead of solving this one (prompt generation 3). The box is the model's point on the
|
|
69
|
+
control, or None when it saw none; the driver presses the vendor's own control first either way."""
|
|
70
|
+
|
|
71
|
+
action: Literal[ActionKind.REFRESH]
|
|
72
|
+
target_bounding_box: Optional[BoundingBox] = None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class SlideAction(Action):
|
|
76
|
+
"""Press the handle and move it until the point on the part it moves reaches the target (prompt generation 3).
|
|
77
|
+
The driver tracks that part on screen and steers; nothing about the widget is read from its DOM."""
|
|
78
|
+
|
|
79
|
+
action: Literal[ActionKind.SLIDE]
|
|
80
|
+
handle_bounding_box: BoundingBox
|
|
81
|
+
source_bounding_box: BoundingBox
|
|
82
|
+
target_bounding_box: BoundingBox
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
CaptchaAction = Union[ClickAction, DragAction, TypeAction, WaitAction, DoneAction, HoldAction, RefreshAction,
|
|
86
|
+
SlideAction]
|