captchakraken 3.2.0 → 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.
Files changed (60) hide show
  1. package/AGENTS.md +5 -9
  2. package/LICENSE +165 -145
  3. package/NOTICE +4 -4
  4. package/README.md +10 -11
  5. package/dist/cli-invocation.d.ts +6 -3
  6. package/dist/cli-invocation.js +26 -10
  7. package/dist/humanize.d.ts +37 -1
  8. package/dist/humanize.js +143 -9
  9. package/dist/index.d.ts +13 -12
  10. package/dist/index.js +25 -33
  11. package/dist/kinds.d.ts +15 -2
  12. package/dist/kinds.js +12 -4
  13. package/dist/playwright-types.d.ts +3 -0
  14. package/dist/selectors.d.ts +96 -11
  15. package/dist/selectors.js +226 -112
  16. package/dist/solver.d.ts +93 -13
  17. package/dist/solver.js +579 -208
  18. package/dist/types.d.ts +53 -8
  19. package/dist/verdicts.d.ts +3 -0
  20. package/dist/verdicts.js +6 -0
  21. package/package.json +4 -4
  22. package/python/AGENTS.md +7 -11
  23. package/python/LICENSE +165 -145
  24. package/python/NOTICE +4 -4
  25. package/python/README.md +10 -9
  26. package/python/pyproject.toml +27 -13
  27. package/python/src/captchakraken/__init__.py +43 -46
  28. package/python/src/captchakraken/action_types.py +32 -1
  29. package/python/src/captchakraken/captchakraken.config.json +416 -0
  30. package/python/src/captchakraken/cli.py +66 -30
  31. package/python/src/captchakraken/clipvideo.py +258 -0
  32. package/python/src/captchakraken/humanize.py +141 -10
  33. package/python/src/captchakraken/image_processor.py +3 -7
  34. package/python/src/captchakraken/keyframes.py +96 -14
  35. package/python/src/captchakraken/kinds.py +14 -2
  36. package/python/src/captchakraken/models.json +28 -3
  37. package/python/src/captchakraken/page_solver.py +621 -259
  38. package/python/src/captchakraken/planner.py +298 -47
  39. package/python/src/captchakraken/pointer/__init__.py +20 -0
  40. package/python/src/captchakraken/pointer/detector.py +333 -0
  41. package/python/src/captchakraken/pointer/elements.py +273 -0
  42. package/python/src/captchakraken/pointer/heads.py +163 -0
  43. package/python/src/captchakraken/pointer/layout.py +319 -0
  44. package/python/src/captchakraken/pointer/marks.py +184 -0
  45. package/python/src/captchakraken/pointer/server.py +146 -0
  46. package/python/src/captchakraken/pointer/service.py +553 -0
  47. package/python/src/captchakraken/pointer/vllm_plugin.py +109 -0
  48. package/python/src/captchakraken/prompts.py +111 -34
  49. package/python/src/captchakraken/selectors.py +224 -131
  50. package/python/src/captchakraken/server_manager.py +18 -0
  51. package/python/src/captchakraken/solver.py +169 -88
  52. package/python/src/captchakraken/tool_calls/board_painted.py +11 -6
  53. package/python/src/captchakraken/tool_calls/fill_tracker.py +152 -0
  54. package/python/src/captchakraken/tool_calls/find_checkbox.py +14 -59
  55. package/python/src/captchakraken/tool_calls/find_grid.py +273 -1191
  56. package/python/src/captchakraken/tool_calls/slide_tracker.py +1102 -0
  57. package/python/src/captchakraken/verdicts.py +16 -0
  58. package/dist/slide-geometry.d.ts +0 -6
  59. package/dist/slide-geometry.js +0 -38
  60. 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
- OpenCV tile detection plus a fine-tuned **Qwen3.5-9B** vision model. Give it a
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: OpenCV detection + vLLM HTTP planner
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
- Pure-OpenCV tool subcommands (no model): `find-grid`, `find-checkbox`,
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
- **CaptchaKraken Source-Available License v1.1** — see [LICENSE](./LICENSE).
153
- Build *with* it (scrapers, QA tooling) and run it against **any** browser you
154
- like, stealth or not. You may **not sell the solve itself**, ship a thin wrapper
155
- (browser extension, hosted solving API), or **bundle it as a built-in feature of
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.
@@ -4,14 +4,10 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "captchakraken"
7
- version = "3.2.0"
8
- description = "Captcha-solving engine and CLI: OpenCV grid detection plus a fine-tuned Qwen3.5-9B vision model, for image grids, click and drag puzzles, sliders, distorted text and animated challenges. Hosted API or self-hosted on vLLM."
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 :: Other/Proprietary 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: OpenCV grid detection + the HTTP planner that
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==12.3.0",
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, not opencv's — opencv-python-headless asks only for
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
- "opencv-python-headless==5.0.0.93",
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.0.0",
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 SELECTORS, VendorSelectors
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
- SELECTORS = None
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
- "CaptchaSolver",
68
- "solve_captcha",
63
+ # Solving a page
69
64
  "PageSolver",
70
- "SELECTORS",
71
- "VendorSelectors",
72
- "CaptchaWatcher",
65
+ "PageSolverConfig",
73
66
  "SolveResult",
67
+ "CaptchaSolveError",
74
68
  "solve_captcha_on_page",
75
- "ActionPlanner",
76
- "ImageProcessor",
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
- "add_overlays_to_image",
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__ = "3.2.0"
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
- CaptchaAction = Union[ClickAction, DragAction, TypeAction, WaitAction, DoneAction]
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]