captchakraken 3.0.0__tar.gz → 3.2.0__tar.gz
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.
- {captchakraken-3.0.0 → captchakraken-3.2.0}/AGENTS.md +13 -8
- {captchakraken-3.0.0 → captchakraken-3.2.0}/LICENSE +1 -1
- {captchakraken-3.0.0 → captchakraken-3.2.0}/PKG-INFO +17 -20
- {captchakraken-3.0.0 → captchakraken-3.2.0}/README.md +10 -13
- {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/README.md +4 -6
- {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/_harness.py +5 -9
- {captchakraken-3.0.0 → captchakraken-3.2.0}/pyproject.toml +6 -6
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/__init__.py +5 -1
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/cli.py +7 -1
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/kinds.py +9 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/page_solver.py +651 -137
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/planner.py +68 -12
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/prompts.py +24 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/selectors.py +10 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/tool_calls/find_checkbox.py +4 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/tool_calls/find_grid.py +22 -0
- captchakraken-3.2.0/src/captchakraken/verdicts.py +154 -0
- captchakraken-3.2.0/tests/fixtures/vendor_verdicts.json +117 -0
- captchakraken-3.2.0/tests/test_a_board_is_routed_and_watched_once_it_has_painted.py +128 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_board_that_never_repeats_is_animated.py +1 -1
- captchakraken-3.2.0/tests/test_a_board_that_will_not_film_may_already_be_solved.py +126 -0
- captchakraken-3.2.0/tests/test_a_capture_never_clips_the_page.py +142 -0
- captchakraken-3.2.0/tests/test_a_checkbox_is_clicked_not_watched.py +144 -0
- captchakraken-3.2.0/tests/test_a_new_board_ends_the_film.py +78 -0
- captchakraken-3.2.0/tests/test_a_repeated_answer_is_not_re_performed.py +53 -0
- captchakraken-3.2.0/tests/test_a_replaced_board_has_not_failed_yet.py +63 -0
- captchakraken-3.2.0/tests/test_a_round_that_did_nothing_waits_for_nothing.py +60 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_slow_camera_does_not_stretch_the_burst.py +31 -1
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_slow_model_does_not_extend_the_burst.py +1 -1
- captchakraken-3.2.0/tests/test_a_solved_board_is_not_lost_to_a_typed_action.py +50 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_still_burst_stops_at_the_floor.py +2 -2
- captchakraken-3.2.0/tests/test_a_widget_is_waited_for.py +130 -0
- captchakraken-3.2.0/tests/test_an_unusable_answer_is_not_a_dead_page.py +72 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_animated_solve_budget.py +45 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_browser_compat.py +42 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_find_checkbox_rejects_what_is_not_a_checkbox.py +15 -0
- captchakraken-3.2.0/tests/test_grid_never_pairs_tiles.py +60 -0
- captchakraken-3.2.0/tests/test_one_ask_cannot_outlive_the_solve.py +82 -0
- captchakraken-3.2.0/tests/test_our_own_gesture_is_not_animation.py +103 -0
- captchakraken-3.2.0/tests/test_outcome_reporting.py +153 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_page_solver.py +24 -14
- captchakraken-3.2.0/tests/test_the_film_outlives_the_answer.py +205 -0
- captchakraken-3.2.0/tests/test_the_legacy_model_warning_is_one_text.py +57 -0
- captchakraken-3.2.0/tests/test_the_second_look_believes_its_recording.py +86 -0
- captchakraken-3.2.0/tests/test_the_vendor_says_whether_the_round_was_taken.py +209 -0
- captchakraken-3.0.0/examples/demoHcaptcha.py +0 -8
- captchakraken-3.0.0/tests/test_a_repeated_answer_is_not_re_performed.py +0 -27
- {captchakraken-3.0.0 → captchakraken-3.2.0}/.gitignore +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/Dockerfile +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/NOTICE +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/demoRecaptcha.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/watch_playwright.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/with_local_gguf.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/with_playwright.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/action_types.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/config.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/errors.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/humanize.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/image_processor.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/keyframes.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/models.json +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/overlay.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/pinned_model.json +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/server_manager.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/solver.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/timing.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/tool_calls/board_painted.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/tool_calls/track_piece.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/updater.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/watcher.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/fake_dom.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/fixtures/cursory_cross_port.json +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_blank_board_is_not_photographed.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_burst_cannot_trip_its_own_deadline.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_grid_is_a_regular_lattice.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_missing_prompt_is_not_a_reason_to_wait.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_pin_reads_its_own_entry.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_reask_is_a_different_sample.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_refusal_keeps_its_machine_readable_half.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_refused_clip_is_not_re_asked.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_still_board_is_not_filmed.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_api_error_is_exported.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_api_errors.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_cli_stdout_is_the_wire_between_the_ports.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_credentials_file.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_cycling_board_waits_for_its_screen.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_empty_answer_still_submits.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_endpoint_is_dialled_once.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_every_keyframe_write_names_its_stem.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_expert_reaches_the_wire.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_expert_routing.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_fetch_command.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_freshness_check.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_geetest_accept_is_a_signal.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_geetest_done_needs_no_bounding_box.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_geetest_submit_button.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_detection.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_detection_ci.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_dims_must_be_possible.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_flank_contrast.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_geometry_gates.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_noisy_gutter.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_off_lattice_clusters.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_overcount_falls_back.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_round_pays_once.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_hcaptcha_badge_detection.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_humanizer.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_licensed_models.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_min_pixels_upscale.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_overlay_is_what_the_model_reads.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_pinned_model.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_pixel_parser.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_private_models.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_public_contract.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_recaptcha_chipped_board.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_recaptcha_dynamic_more_is_not_an_error.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_routing_headers.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_selected_fields.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_setup_sh_model_ids.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_slide_reads_the_piece_off_the_page.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_solver.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_stale_handle_after_submit.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_the_notice_travels_with_the_package.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_the_two_ports_share_one_mouse.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_the_wait_gate_metric.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_the_worker_protocol_survives_a_bad_request.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_thinking_is_off_on_every_runtime.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_vendor_gates_are_not_keyed_on_unknown.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_vendor_hint_decides_the_expert.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_version_matches_manifest.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_watcher.py +0 -0
- {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/virtual_clock.py +0 -0
|
@@ -9,7 +9,7 @@ browser — bring your own — and every inference goes to a model endpoint you
|
|
|
9
9
|
point it at. The `serve` extra is what turns this box into that endpoint.
|
|
10
10
|
|
|
11
11
|
Repo, guides and the full agent guide:
|
|
12
|
-
<https://github.com/
|
|
12
|
+
<https://github.com/CaptchaKraken/CaptchaKraken>
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -71,10 +71,15 @@ module docstring of `captchakraken/cli.py` lists every mode with its arguments.
|
|
|
71
71
|
## 4. Errors
|
|
72
72
|
|
|
73
73
|
`PageSolver.solve()` raises from `captchakraken.page_solver`:
|
|
74
|
-
`NoCaptchaFoundError` (no interactive widget
|
|
75
|
-
`
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
`NoCaptchaFoundError` (no interactive widget appeared within
|
|
75
|
+
`detection_timeout_ms` — usually not a failure), `VendorBlockedError` (the
|
|
76
|
+
vendor refused to serve this client at all), `UnsupportedChallengeError`,
|
|
77
|
+
`AnimatedChallengeError` (the challenge could not be *recorded*),
|
|
78
|
+
`PageClosedError`, and `CaptchaSolveError`, which is the base class of the
|
|
79
|
+
other five — catch it last or it swallows them. Apart from a vendor refusal, a
|
|
80
|
+
closed page and the time budget, a solve only gives up once every one of
|
|
81
|
+
`max_solve_loops` has been spent. `result.verdicts` holds the vendor's own
|
|
82
|
+
answer to each round where its answer-check response is readable. The TypeScript
|
|
78
83
|
port raises none of these; it exports one error class and throws plain `Error`
|
|
79
84
|
otherwise.
|
|
80
85
|
|
|
@@ -100,7 +105,7 @@ generation and the weights drift apart.
|
|
|
100
105
|
|
|
101
106
|
- **Never invent a number.** Every accuracy, latency and price figure we publish
|
|
102
107
|
is on one page —
|
|
103
|
-
[docs/benchmarks.md](https://github.com/
|
|
108
|
+
[docs/benchmarks.md](https://github.com/CaptchaKraken/CaptchaKraken/blob/main/docs/benchmarks.md).
|
|
104
109
|
Quote it as written; do not derive, round, or convert anything, and do not
|
|
105
110
|
read a static-image rate as a promise about a browser run.
|
|
106
111
|
- **Never print an API key** into a transcript, a log, or a commit. Use
|
|
@@ -116,7 +121,7 @@ generation and the weights drift apart.
|
|
|
116
121
|
- **Respect the licence.** Building automation with this is fine; selling the
|
|
117
122
|
solve, or shipping it inside a browser or automation product, is not. See
|
|
118
123
|
[LICENSE](./LICENSE) and
|
|
119
|
-
[docs/licensing.md](https://github.com/
|
|
124
|
+
[docs/licensing.md](https://github.com/CaptchaKraken/CaptchaKraken/blob/main/docs/licensing.md).
|
|
120
125
|
- **Changing this package's own code?** Its rules are in
|
|
121
|
-
[CONTRIBUTING.md](https://github.com/
|
|
126
|
+
[CONTRIBUTING.md](https://github.com/CaptchaKraken/CaptchaKraken/blob/main/CONTRIBUTING.md),
|
|
122
127
|
not here.
|
|
@@ -141,5 +141,5 @@ This text is a license, not legal advice. If the boundary between a permitted
|
|
|
141
141
|
"value-add" product and a prohibited "thin wrapper", or between using the
|
|
142
142
|
Software with a browser and shipping it as part of one, is unclear for your use
|
|
143
143
|
case, ask by opening an issue or messaging the maintainer on GitHub:
|
|
144
|
-
https://github.com/
|
|
144
|
+
https://github.com/CaptchaKraken/CaptchaKraken
|
|
145
145
|
--------------------------------------------------------------------------------
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: captchakraken
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: 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.
|
|
5
5
|
Project-URL: Homepage, https://captchakraken.com
|
|
6
|
-
Project-URL: Documentation, https://github.com/
|
|
7
|
-
Project-URL: Source, https://github.com/
|
|
8
|
-
Project-URL: Issues, https://github.com/
|
|
9
|
-
Project-URL: Changelog, https://github.com/
|
|
6
|
+
Project-URL: Documentation, https://github.com/CaptchaKraken/CaptchaKraken#readme
|
|
7
|
+
Project-URL: Source, https://github.com/CaptchaKraken/CaptchaKraken
|
|
8
|
+
Project-URL: Issues, https://github.com/CaptchaKraken/CaptchaKraken/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/CaptchaKraken/CaptchaKraken/blob/main/CHANGELOG.md
|
|
10
10
|
Author: Jake Writer
|
|
11
11
|
License: CaptchaKraken Source-Available License v1.1
|
|
12
12
|
Copyright (c) 2026 CaptchaKraken LLC. All rights reserved.
|
|
@@ -151,11 +151,11 @@ License: CaptchaKraken Source-Available License v1.1
|
|
|
151
151
|
"value-add" product and a prohibited "thin wrapper", or between using the
|
|
152
152
|
Software with a browser and shipping it as part of one, is unclear for your use
|
|
153
153
|
case, ask by opening an issue or messaging the maintainer on GitHub:
|
|
154
|
-
https://github.com/
|
|
154
|
+
https://github.com/CaptchaKraken/CaptchaKraken
|
|
155
155
|
--------------------------------------------------------------------------------
|
|
156
156
|
License-File: LICENSE
|
|
157
157
|
License-File: NOTICE
|
|
158
|
-
Keywords: anti-bot,browser-automation,captcha,captcha-solver,computer-vision,geetest,
|
|
158
|
+
Keywords: anti-bot,browser-automation,captcha,captcha-solver,computer-vision,geetest,ocr,playwright,qwen,recaptcha,scraping,tencent-captcha,turnstile,vllm,yidun
|
|
159
159
|
Classifier: Development Status :: 5 - Production/Stable
|
|
160
160
|
Classifier: Intended Audience :: Developers
|
|
161
161
|
Classifier: License :: Other/Proprietary License
|
|
@@ -191,14 +191,14 @@ Requires-Dist: vllm==0.29.0; extra == 'serve'
|
|
|
191
191
|
Description-Content-Type: text/markdown
|
|
192
192
|
|
|
193
193
|
<p align="center">
|
|
194
|
-
<img src="https://raw.githubusercontent.com/
|
|
194
|
+
<img src="https://raw.githubusercontent.com/CaptchaKraken/CaptchaKraken/main/docs/assets/logo-card.png" alt="CaptchaKraken" width="128" height="128">
|
|
195
195
|
</p>
|
|
196
196
|
|
|
197
197
|
<h1 align="center">captchakraken</h1>
|
|
198
198
|
|
|
199
199
|
<p align="center">
|
|
200
200
|
<b>A captcha solver for browser automation.</b><br>
|
|
201
|
-
The Python engine and CLI behind <a href="https://github.com/
|
|
201
|
+
The Python engine and CLI behind <a href="https://github.com/CaptchaKraken/CaptchaKraken">CaptchaKraken</a>.
|
|
202
202
|
</p>
|
|
203
203
|
|
|
204
204
|
OpenCV tile detection plus a fine-tuned **Qwen3.5-9B** vision model. Give it a
|
|
@@ -211,34 +211,31 @@ nothing at all.
|
|
|
211
211
|
|
|
212
212
|
> For demo videos, accuracy numbers, the browser driver, and the full
|
|
213
213
|
> self-hosting guide, see the main repo
|
|
214
|
-
> **[CaptchaKraken](https://github.com/
|
|
214
|
+
> **[CaptchaKraken](https://github.com/CaptchaKraken/CaptchaKraken)**.
|
|
215
215
|
|
|
216
216
|
## Watch it work
|
|
217
217
|
|
|
218
218
|
<p align="center">
|
|
219
|
-
<img src="https://raw.githubusercontent.com/
|
|
220
|
-
alt="A live hCaptcha image select challenge being solved end to end">
|
|
221
|
-
<img src="https://raw.githubusercontent.com/JWriter20/CaptchaKraken/main/docs/assets/demo/recaptcha_4x4.webp" width="260"
|
|
219
|
+
<img src="https://raw.githubusercontent.com/CaptchaKraken/CaptchaKraken/main/docs/assets/demo/recaptcha_4x4.webp" width="260"
|
|
222
220
|
alt="A live reCAPTCHA 4×4 tile grid challenge being solved end to end">
|
|
223
|
-
<img src="https://raw.githubusercontent.com/
|
|
221
|
+
<img src="https://raw.githubusercontent.com/CaptchaKraken/CaptchaKraken/main/docs/assets/demo/geetest_slide.webp" width="260"
|
|
224
222
|
alt="A live GeeTest slide jigsaw challenge being solved end to end">
|
|
225
223
|
</p>
|
|
226
224
|
|
|
227
|
-
|
|
225
|
+
reCAPTCHA 4×4 tile grid 9/10 in 8.7s · GeeTest slide jigsaw 10/10 in 7.6s — median of the solved attempts, measured 2026-08-19 on **captcha-v12**
|
|
228
226
|
against each vendor's own public demo page. Counts rather than percentages
|
|
229
227
|
because ten attempts is not a percentage. Idle time is cut from the clips, so
|
|
230
228
|
they run shorter than the solves they show.
|
|
231
229
|
|
|
232
|
-
**
|
|
230
|
+
**More puzzle types**, as video and with the full method, at
|
|
233
231
|
[captchakraken.com](https://captchakraken.com) and in the
|
|
234
|
-
[main repo](https://github.com/
|
|
232
|
+
[main repo](https://github.com/CaptchaKraken/CaptchaKraken#watch-it-work).
|
|
235
233
|
|
|
236
234
|
## What it solves
|
|
237
235
|
|
|
238
236
|
| Vendor | Puzzles |
|
|
239
237
|
|---|---|
|
|
240
238
|
| **reCAPTCHA** | 3×3 and 4×4 image grids, including the dynamic re-deal |
|
|
241
|
-
| **hCaptcha** | Image grids, click, drag, connect-the-path, tetris-fit, animated |
|
|
242
239
|
| **GeeTest** v3 + v4 | Slide, icon, nine, svg, gobang, iconcrush |
|
|
243
240
|
| **NetEase Yidun** | Jigsaw, picture-click, icon-click |
|
|
244
241
|
| **Tencent, Lemin, Prosopo** | Slide, cropped-image and grid flows |
|
|
@@ -259,7 +256,7 @@ pip install "captchakraken[serve]" # + the serving stack (vLLM/torch) to self-
|
|
|
259
256
|
The base install is lightweight — everything you need to solve captchas against
|
|
260
257
|
a vLLM server (local or remote). The `[serve]` extra pulls the heavy stack only
|
|
261
258
|
if you want to run the model yourself. The one-command
|
|
262
|
-
[`setup.sh`](https://github.com/
|
|
259
|
+
[`setup.sh`](https://github.com/CaptchaKraken/CaptchaKraken) installs `[serve]`,
|
|
263
260
|
downloads the weights, and writes an env file for you.
|
|
264
261
|
|
|
265
262
|
## No GPU? Use the hosted API
|
|
@@ -292,7 +289,7 @@ captchakraken server start | stop | status | run
|
|
|
292
289
|
```bash
|
|
293
290
|
# Solve an image/video: classify → find_grid → plan. Prints the click actions.
|
|
294
291
|
captchakraken path/to/captcha.png
|
|
295
|
-
captchakraken path/to/captcha.png --puzzle-source
|
|
292
|
+
captchakraken path/to/captcha.png --puzzle-source recaptcha
|
|
296
293
|
```
|
|
297
294
|
|
|
298
295
|
```python
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://raw.githubusercontent.com/
|
|
2
|
+
<img src="https://raw.githubusercontent.com/CaptchaKraken/CaptchaKraken/main/docs/assets/logo-card.png" alt="CaptchaKraken" width="128" height="128">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">captchakraken</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<b>A captcha solver for browser automation.</b><br>
|
|
9
|
-
The Python engine and CLI behind <a href="https://github.com/
|
|
9
|
+
The Python engine and CLI behind <a href="https://github.com/CaptchaKraken/CaptchaKraken">CaptchaKraken</a>.
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
OpenCV tile detection plus a fine-tuned **Qwen3.5-9B** vision model. Give it a
|
|
@@ -19,34 +19,31 @@ nothing at all.
|
|
|
19
19
|
|
|
20
20
|
> For demo videos, accuracy numbers, the browser driver, and the full
|
|
21
21
|
> self-hosting guide, see the main repo
|
|
22
|
-
> **[CaptchaKraken](https://github.com/
|
|
22
|
+
> **[CaptchaKraken](https://github.com/CaptchaKraken/CaptchaKraken)**.
|
|
23
23
|
|
|
24
24
|
## Watch it work
|
|
25
25
|
|
|
26
26
|
<p align="center">
|
|
27
|
-
<img src="https://raw.githubusercontent.com/
|
|
28
|
-
alt="A live hCaptcha image select challenge being solved end to end">
|
|
29
|
-
<img src="https://raw.githubusercontent.com/JWriter20/CaptchaKraken/main/docs/assets/demo/recaptcha_4x4.webp" width="260"
|
|
27
|
+
<img src="https://raw.githubusercontent.com/CaptchaKraken/CaptchaKraken/main/docs/assets/demo/recaptcha_4x4.webp" width="260"
|
|
30
28
|
alt="A live reCAPTCHA 4×4 tile grid challenge being solved end to end">
|
|
31
|
-
<img src="https://raw.githubusercontent.com/
|
|
29
|
+
<img src="https://raw.githubusercontent.com/CaptchaKraken/CaptchaKraken/main/docs/assets/demo/geetest_slide.webp" width="260"
|
|
32
30
|
alt="A live GeeTest slide jigsaw challenge being solved end to end">
|
|
33
31
|
</p>
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
reCAPTCHA 4×4 tile grid 9/10 in 8.7s · GeeTest slide jigsaw 10/10 in 7.6s — median of the solved attempts, measured 2026-08-19 on **captcha-v12**
|
|
36
34
|
against each vendor's own public demo page. Counts rather than percentages
|
|
37
35
|
because ten attempts is not a percentage. Idle time is cut from the clips, so
|
|
38
36
|
they run shorter than the solves they show.
|
|
39
37
|
|
|
40
|
-
**
|
|
38
|
+
**More puzzle types**, as video and with the full method, at
|
|
41
39
|
[captchakraken.com](https://captchakraken.com) and in the
|
|
42
|
-
[main repo](https://github.com/
|
|
40
|
+
[main repo](https://github.com/CaptchaKraken/CaptchaKraken#watch-it-work).
|
|
43
41
|
|
|
44
42
|
## What it solves
|
|
45
43
|
|
|
46
44
|
| Vendor | Puzzles |
|
|
47
45
|
|---|---|
|
|
48
46
|
| **reCAPTCHA** | 3×3 and 4×4 image grids, including the dynamic re-deal |
|
|
49
|
-
| **hCaptcha** | Image grids, click, drag, connect-the-path, tetris-fit, animated |
|
|
50
47
|
| **GeeTest** v3 + v4 | Slide, icon, nine, svg, gobang, iconcrush |
|
|
51
48
|
| **NetEase Yidun** | Jigsaw, picture-click, icon-click |
|
|
52
49
|
| **Tencent, Lemin, Prosopo** | Slide, cropped-image and grid flows |
|
|
@@ -67,7 +64,7 @@ pip install "captchakraken[serve]" # + the serving stack (vLLM/torch) to self-
|
|
|
67
64
|
The base install is lightweight — everything you need to solve captchas against
|
|
68
65
|
a vLLM server (local or remote). The `[serve]` extra pulls the heavy stack only
|
|
69
66
|
if you want to run the model yourself. The one-command
|
|
70
|
-
[`setup.sh`](https://github.com/
|
|
67
|
+
[`setup.sh`](https://github.com/CaptchaKraken/CaptchaKraken) installs `[serve]`,
|
|
71
68
|
downloads the weights, and writes an env file for you.
|
|
72
69
|
|
|
73
70
|
## No GPU? Use the hosted API
|
|
@@ -100,7 +97,7 @@ captchakraken server start | stop | status | run
|
|
|
100
97
|
```bash
|
|
101
98
|
# Solve an image/video: classify → find_grid → plan. Prints the click actions.
|
|
102
99
|
captchakraken path/to/captcha.png
|
|
103
|
-
captchakraken path/to/captcha.png --puzzle-source
|
|
100
|
+
captchakraken path/to/captcha.png --puzzle-source recaptcha
|
|
104
101
|
```
|
|
105
102
|
|
|
106
103
|
```python
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
# Examples (Python)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A runnable demo that drives a real stealth browser
|
|
4
4
|
([camoufox](https://github.com/JWriter20/camoufox)) to a live captcha demo site,
|
|
5
|
-
|
|
5
|
+
screenshots the challenge, runs the **engine** on it, and prints token speed /
|
|
6
6
|
total time / outcome:
|
|
7
7
|
|
|
8
8
|
| File | Site |
|
|
9
9
|
|---|---|
|
|
10
10
|
| `demoRecaptcha.py` | Google reCAPTCHA v2 demo |
|
|
11
|
-
| `demoHcaptcha.py` | hCaptcha demo |
|
|
12
11
|
|
|
13
|
-
> The Python port is the engine (detection + planner).
|
|
12
|
+
> The Python port is the engine (detection + planner). This demo validates the
|
|
14
13
|
> engine + model + server on a real challenge frame. Full click-replay and
|
|
15
14
|
> multi-round verification in a live page are what the TypeScript port
|
|
16
15
|
> (`captchakraken`) does end-to-end.
|
|
@@ -58,7 +57,6 @@ python examples/with_local_gguf.py path/to/text-captcha.png --text
|
|
|
58
57
|
|
|
59
58
|
```bash
|
|
60
59
|
python examples/demoRecaptcha.py
|
|
61
|
-
python examples/demoHcaptcha.py
|
|
62
60
|
HEADLESS=0 python examples/demoRecaptcha.py # watch the browser
|
|
63
61
|
```
|
|
64
62
|
|
|
@@ -74,7 +72,7 @@ HEADLESS=0 python examples/demoRecaptcha.py # watch the browser
|
|
|
74
72
|
```
|
|
75
73
|
|
|
76
74
|
`gen speed` = model output tokens ÷ solve seconds. Failure reasons the harness
|
|
77
|
-
reports: unreachable vLLM server,
|
|
75
|
+
reports: unreachable vLLM server, a challenge type the solver does not handle,
|
|
78
76
|
the challenge iframe never appearing, or the model returning no matching tiles.
|
|
79
77
|
|
|
80
78
|
## Playwright, and the auto-solver
|
|
@@ -24,8 +24,7 @@ def spec_from_argv(default: DemoSpec, argv=None) -> DemoSpec:
|
|
|
24
24
|
ap.add_argument("url", nargs="?", default=default.url,
|
|
25
25
|
help=f"page to solve (default: {default.url})")
|
|
26
26
|
ap.add_argument("--vendor", default=default.vendor,
|
|
27
|
-
|
|
28
|
-
help="only affects failure wording; the solver auto-detects")
|
|
27
|
+
help="label recorded in the report; the solver auto-detects")
|
|
29
28
|
ap.add_argument("--name", default=None, help="label for the report")
|
|
30
29
|
ap.add_argument("--headed", action="store_true",
|
|
31
30
|
help="show the browser (same as HEADLESS=0)")
|
|
@@ -74,12 +73,9 @@ def _tokens(usage) -> tuple[int, int]:
|
|
|
74
73
|
return inp, out
|
|
75
74
|
|
|
76
75
|
|
|
77
|
-
def _explain(
|
|
76
|
+
def _explain(solved: bool, err) -> str:
|
|
78
77
|
msg = str(err or "").lower()
|
|
79
78
|
if "unsupported" in msg or "cannot solve" in msg:
|
|
80
|
-
if vendor == "hcaptcha":
|
|
81
|
-
return ("hCaptcha served a challenge type the solver does not handle "
|
|
82
|
-
"yet (drag / video / choose-the-card) — re-run for a grid.")
|
|
83
79
|
return "Not a supported challenge type — re-run to try again."
|
|
84
80
|
if "no captcha" in msg or "nocaptchafound" in msg:
|
|
85
81
|
return ("No captcha widget was found on that page. Check the URL, or "
|
|
@@ -128,15 +124,15 @@ def run_demo(spec: DemoSpec) -> None:
|
|
|
128
124
|
ok = bool(result.is_solved)
|
|
129
125
|
inp, out = _tokens(result.token_usage)
|
|
130
126
|
if not ok:
|
|
131
|
-
reason = _explain(
|
|
127
|
+
reason = _explain(ok, None)
|
|
132
128
|
except CaptchaSolveError as e:
|
|
133
129
|
err = e
|
|
134
|
-
reason = _explain(
|
|
130
|
+
reason = _explain(False, e)
|
|
135
131
|
finally:
|
|
136
132
|
video_path = _finish_video(page)
|
|
137
133
|
except Exception as e:
|
|
138
134
|
err = e
|
|
139
|
-
reason = _explain(
|
|
135
|
+
reason = _explain(False, e)
|
|
140
136
|
|
|
141
137
|
tps = out / solve_s if out and solve_s > 0 else 0.0
|
|
142
138
|
_report(spec, ok=ok, total_s=time.time() - t0, solve_s=solve_s,
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "captchakraken"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.2.0"
|
|
8
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."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -15,7 +15,7 @@ requires-python = ">=3.11"
|
|
|
15
15
|
license = { file = "LICENSE" }
|
|
16
16
|
authors = [{ name = "Jake Writer" }]
|
|
17
17
|
keywords = [
|
|
18
|
-
"captcha", "captcha-solver", "recaptcha", "
|
|
18
|
+
"captcha", "captcha-solver", "recaptcha", "geetest", "turnstile",
|
|
19
19
|
"yidun", "tencent-captcha", "playwright", "browser-automation", "scraping",
|
|
20
20
|
"anti-bot", "vllm", "qwen", "computer-vision", "ocr",
|
|
21
21
|
]
|
|
@@ -92,10 +92,10 @@ captchakraken = "captchakraken.cli:main"
|
|
|
92
92
|
|
|
93
93
|
[project.urls]
|
|
94
94
|
Homepage = "https://captchakraken.com"
|
|
95
|
-
Documentation = "https://github.com/
|
|
96
|
-
Source = "https://github.com/
|
|
97
|
-
Issues = "https://github.com/
|
|
98
|
-
Changelog = "https://github.com/
|
|
95
|
+
Documentation = "https://github.com/CaptchaKraken/CaptchaKraken#readme"
|
|
96
|
+
Source = "https://github.com/CaptchaKraken/CaptchaKraken"
|
|
97
|
+
Issues = "https://github.com/CaptchaKraken/CaptchaKraken/issues"
|
|
98
|
+
Changelog = "https://github.com/CaptchaKraken/CaptchaKraken/blob/main/CHANGELOG.md"
|
|
99
99
|
|
|
100
100
|
[tool.hatch.build.targets.wheel]
|
|
101
101
|
packages = ["src/captchakraken"]
|
|
@@ -30,7 +30,9 @@ from .kinds import (
|
|
|
30
30
|
PromptFamily,
|
|
31
31
|
RetryMode,
|
|
32
32
|
Vendor,
|
|
33
|
+
Verdict,
|
|
33
34
|
)
|
|
35
|
+
from .verdicts import RoundVerdict
|
|
34
36
|
|
|
35
37
|
from .humanize import (
|
|
36
38
|
AppiumTouchBackend,
|
|
@@ -96,7 +98,9 @@ __all__ = [
|
|
|
96
98
|
"PauseKind",
|
|
97
99
|
"PromptFamily",
|
|
98
100
|
"RetryMode",
|
|
101
|
+
"RoundVerdict",
|
|
99
102
|
"Vendor",
|
|
103
|
+
"Verdict",
|
|
100
104
|
]
|
|
101
105
|
|
|
102
|
-
__version__ = "3.
|
|
106
|
+
__version__ = "3.2.0"
|
|
@@ -177,7 +177,10 @@ def _report_outcome(args):
|
|
|
177
177
|
try:
|
|
178
178
|
from .planner import ActionPlanner
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
planner = ActionPlanner()
|
|
181
|
+
reported = planner.report_outcome(args[0], Outcome(args[1]) == Outcome.SOLVED)
|
|
182
|
+
# `supported` false is the self-hosted 404, which tells the driver to stop spawning this.
|
|
183
|
+
return {"reported": bool(reported), "supported": planner.outcome_supported}
|
|
181
184
|
except Exception as exc:
|
|
182
185
|
return {"reported": False, "error": str(exc)}
|
|
183
186
|
|
|
@@ -185,6 +188,7 @@ def _report_outcome(args):
|
|
|
185
188
|
def _serve(args):
|
|
186
189
|
"""Persistent CV worker: one JSON request per stdin line, one JSON reply per stdout line."""
|
|
187
190
|
from .image_processor import ImageProcessor
|
|
191
|
+
from .tool_calls.find_checkbox import find_checkbox
|
|
188
192
|
from .tool_calls.find_grid import find_grid
|
|
189
193
|
|
|
190
194
|
def handle(req):
|
|
@@ -206,6 +210,8 @@ def _serve(args):
|
|
|
206
210
|
return match_region(req["ref"], req["live"], float(req["cx"]), float(req["cy"]), req.get("tolerance"))
|
|
207
211
|
if cmd == "board-painted":
|
|
208
212
|
return board_painted(req["image"], req.get("floor"), req.get("box"))
|
|
213
|
+
if cmd == "find-checkbox":
|
|
214
|
+
return find_checkbox(req["image"])
|
|
209
215
|
if cmd == "track-piece":
|
|
210
216
|
return track_piece(req["before"], req["after"], req.get("exclude"), req.get("travel") or 0.0)
|
|
211
217
|
raise ValueError(f"unknown cmd: {cmd!r}")
|
|
@@ -104,6 +104,15 @@ class Outcome(StrEnum):
|
|
|
104
104
|
FAILED = "failed"
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
class Verdict(StrEnum):
|
|
108
|
+
"""What the vendor's own server answered a round: BLOCKED is a refusal to serve at all, not a wrong answer."""
|
|
109
|
+
|
|
110
|
+
ACCEPTED = "accepted"
|
|
111
|
+
REJECTED = "rejected"
|
|
112
|
+
NEW_CHALLENGE = "new-challenge"
|
|
113
|
+
BLOCKED = "blocked"
|
|
114
|
+
|
|
115
|
+
|
|
107
116
|
class Phase(StrEnum):
|
|
108
117
|
"""Timing phases. Only INFERENCE and MOUSE are productive; everything else is waiting."""
|
|
109
118
|
|