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.
Files changed (132) hide show
  1. {captchakraken-3.0.0 → captchakraken-3.2.0}/AGENTS.md +13 -8
  2. {captchakraken-3.0.0 → captchakraken-3.2.0}/LICENSE +1 -1
  3. {captchakraken-3.0.0 → captchakraken-3.2.0}/PKG-INFO +17 -20
  4. {captchakraken-3.0.0 → captchakraken-3.2.0}/README.md +10 -13
  5. {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/README.md +4 -6
  6. {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/_harness.py +5 -9
  7. {captchakraken-3.0.0 → captchakraken-3.2.0}/pyproject.toml +6 -6
  8. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/__init__.py +5 -1
  9. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/cli.py +7 -1
  10. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/kinds.py +9 -0
  11. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/page_solver.py +651 -137
  12. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/planner.py +68 -12
  13. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/prompts.py +24 -0
  14. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/selectors.py +10 -0
  15. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/tool_calls/find_checkbox.py +4 -0
  16. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/tool_calls/find_grid.py +22 -0
  17. captchakraken-3.2.0/src/captchakraken/verdicts.py +154 -0
  18. captchakraken-3.2.0/tests/fixtures/vendor_verdicts.json +117 -0
  19. captchakraken-3.2.0/tests/test_a_board_is_routed_and_watched_once_it_has_painted.py +128 -0
  20. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_board_that_never_repeats_is_animated.py +1 -1
  21. captchakraken-3.2.0/tests/test_a_board_that_will_not_film_may_already_be_solved.py +126 -0
  22. captchakraken-3.2.0/tests/test_a_capture_never_clips_the_page.py +142 -0
  23. captchakraken-3.2.0/tests/test_a_checkbox_is_clicked_not_watched.py +144 -0
  24. captchakraken-3.2.0/tests/test_a_new_board_ends_the_film.py +78 -0
  25. captchakraken-3.2.0/tests/test_a_repeated_answer_is_not_re_performed.py +53 -0
  26. captchakraken-3.2.0/tests/test_a_replaced_board_has_not_failed_yet.py +63 -0
  27. captchakraken-3.2.0/tests/test_a_round_that_did_nothing_waits_for_nothing.py +60 -0
  28. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_slow_camera_does_not_stretch_the_burst.py +31 -1
  29. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_slow_model_does_not_extend_the_burst.py +1 -1
  30. captchakraken-3.2.0/tests/test_a_solved_board_is_not_lost_to_a_typed_action.py +50 -0
  31. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_still_burst_stops_at_the_floor.py +2 -2
  32. captchakraken-3.2.0/tests/test_a_widget_is_waited_for.py +130 -0
  33. captchakraken-3.2.0/tests/test_an_unusable_answer_is_not_a_dead_page.py +72 -0
  34. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_animated_solve_budget.py +45 -0
  35. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_browser_compat.py +42 -0
  36. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_find_checkbox_rejects_what_is_not_a_checkbox.py +15 -0
  37. captchakraken-3.2.0/tests/test_grid_never_pairs_tiles.py +60 -0
  38. captchakraken-3.2.0/tests/test_one_ask_cannot_outlive_the_solve.py +82 -0
  39. captchakraken-3.2.0/tests/test_our_own_gesture_is_not_animation.py +103 -0
  40. captchakraken-3.2.0/tests/test_outcome_reporting.py +153 -0
  41. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_page_solver.py +24 -14
  42. captchakraken-3.2.0/tests/test_the_film_outlives_the_answer.py +205 -0
  43. captchakraken-3.2.0/tests/test_the_legacy_model_warning_is_one_text.py +57 -0
  44. captchakraken-3.2.0/tests/test_the_second_look_believes_its_recording.py +86 -0
  45. captchakraken-3.2.0/tests/test_the_vendor_says_whether_the_round_was_taken.py +209 -0
  46. captchakraken-3.0.0/examples/demoHcaptcha.py +0 -8
  47. captchakraken-3.0.0/tests/test_a_repeated_answer_is_not_re_performed.py +0 -27
  48. {captchakraken-3.0.0 → captchakraken-3.2.0}/.gitignore +0 -0
  49. {captchakraken-3.0.0 → captchakraken-3.2.0}/Dockerfile +0 -0
  50. {captchakraken-3.0.0 → captchakraken-3.2.0}/NOTICE +0 -0
  51. {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/demoRecaptcha.py +0 -0
  52. {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/watch_playwright.py +0 -0
  53. {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/with_local_gguf.py +0 -0
  54. {captchakraken-3.0.0 → captchakraken-3.2.0}/examples/with_playwright.py +0 -0
  55. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/action_types.py +0 -0
  56. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/config.py +0 -0
  57. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/errors.py +0 -0
  58. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/humanize.py +0 -0
  59. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/image_processor.py +0 -0
  60. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/keyframes.py +0 -0
  61. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/models.json +0 -0
  62. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/overlay.py +0 -0
  63. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/pinned_model.json +0 -0
  64. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/server_manager.py +0 -0
  65. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/solver.py +0 -0
  66. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/timing.py +0 -0
  67. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/tool_calls/board_painted.py +0 -0
  68. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/tool_calls/track_piece.py +0 -0
  69. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/updater.py +0 -0
  70. {captchakraken-3.0.0 → captchakraken-3.2.0}/src/captchakraken/watcher.py +0 -0
  71. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/fake_dom.py +0 -0
  72. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/fixtures/cursory_cross_port.json +0 -0
  73. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_blank_board_is_not_photographed.py +0 -0
  74. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_burst_cannot_trip_its_own_deadline.py +0 -0
  75. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_grid_is_a_regular_lattice.py +0 -0
  76. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_missing_prompt_is_not_a_reason_to_wait.py +0 -0
  77. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_pin_reads_its_own_entry.py +0 -0
  78. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_reask_is_a_different_sample.py +0 -0
  79. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_refusal_keeps_its_machine_readable_half.py +0 -0
  80. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_refused_clip_is_not_re_asked.py +0 -0
  81. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_a_still_board_is_not_filmed.py +0 -0
  82. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_api_error_is_exported.py +0 -0
  83. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_api_errors.py +0 -0
  84. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_cli_stdout_is_the_wire_between_the_ports.py +0 -0
  85. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_credentials_file.py +0 -0
  86. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_cycling_board_waits_for_its_screen.py +0 -0
  87. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_empty_answer_still_submits.py +0 -0
  88. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_endpoint_is_dialled_once.py +0 -0
  89. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_every_keyframe_write_names_its_stem.py +0 -0
  90. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_expert_reaches_the_wire.py +0 -0
  91. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_expert_routing.py +0 -0
  92. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_fetch_command.py +0 -0
  93. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_freshness_check.py +0 -0
  94. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_geetest_accept_is_a_signal.py +0 -0
  95. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_geetest_done_needs_no_bounding_box.py +0 -0
  96. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_geetest_submit_button.py +0 -0
  97. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_detection.py +0 -0
  98. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_detection_ci.py +0 -0
  99. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_dims_must_be_possible.py +0 -0
  100. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_flank_contrast.py +0 -0
  101. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_geometry_gates.py +0 -0
  102. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_noisy_gutter.py +0 -0
  103. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_off_lattice_clusters.py +0 -0
  104. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_overcount_falls_back.py +0 -0
  105. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_grid_round_pays_once.py +0 -0
  106. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_hcaptcha_badge_detection.py +0 -0
  107. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_humanizer.py +0 -0
  108. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_licensed_models.py +0 -0
  109. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_min_pixels_upscale.py +0 -0
  110. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_overlay_is_what_the_model_reads.py +0 -0
  111. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_pinned_model.py +0 -0
  112. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_pixel_parser.py +0 -0
  113. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_private_models.py +0 -0
  114. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_public_contract.py +0 -0
  115. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_recaptcha_chipped_board.py +0 -0
  116. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_recaptcha_dynamic_more_is_not_an_error.py +0 -0
  117. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_routing_headers.py +0 -0
  118. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_selected_fields.py +0 -0
  119. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_setup_sh_model_ids.py +0 -0
  120. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_slide_reads_the_piece_off_the_page.py +0 -0
  121. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_solver.py +0 -0
  122. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_stale_handle_after_submit.py +0 -0
  123. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_the_notice_travels_with_the_package.py +0 -0
  124. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_the_two_ports_share_one_mouse.py +0 -0
  125. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_the_wait_gate_metric.py +0 -0
  126. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_the_worker_protocol_survives_a_bad_request.py +0 -0
  127. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_thinking_is_off_on_every_runtime.py +0 -0
  128. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_vendor_gates_are_not_keyed_on_unknown.py +0 -0
  129. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_vendor_hint_decides_the_expert.py +0 -0
  130. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_version_matches_manifest.py +0 -0
  131. {captchakraken-3.0.0 → captchakraken-3.2.0}/tests/test_watcher.py +0 -0
  132. {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/JWriter20/CaptchaKraken>
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 — usually not a failure),
75
- `UnsupportedChallengeError`, `AnimatedChallengeError` (the challenge could not
76
- be *recorded*), `PageClosedError`, and `CaptchaSolveError`, which is the base
77
- class of the other four — catch it last or it swallows them. The TypeScript
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/JWriter20/CaptchaKraken/blob/main/docs/benchmarks.md).
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/JWriter20/CaptchaKraken/blob/main/docs/licensing.md).
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/JWriter20/CaptchaKraken/blob/main/CONTRIBUTING.md),
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/JWriter20/CaptchaKraken
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.0.0
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/JWriter20/CaptchaKraken#readme
7
- Project-URL: Source, https://github.com/JWriter20/CaptchaKraken
8
- Project-URL: Issues, https://github.com/JWriter20/CaptchaKraken/issues
9
- Project-URL: Changelog, https://github.com/JWriter20/CaptchaKraken/blob/main/CHANGELOG.md
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/JWriter20/CaptchaKraken
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,hcaptcha,ocr,playwright,qwen,recaptcha,scraping,tencent-captcha,turnstile,vllm,yidun
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/JWriter20/CaptchaKraken/main/docs/assets/logo-card.png" alt="CaptchaKraken" width="128" height="128">
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/JWriter20/CaptchaKraken">CaptchaKraken</a>.
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/JWriter20/CaptchaKraken)**.
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/JWriter20/CaptchaKraken/main/docs/assets/demo/hcaptcha_grid.webp" width="260"
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/JWriter20/CaptchaKraken/main/docs/assets/demo/geetest_slide.webp" width="260"
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
- hCaptcha image select 12/12 in 10.5s · 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**
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
- **Ten more puzzle types**, as video and with the full method, at
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/JWriter20/CaptchaKraken#watch-it-work).
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/JWriter20/CaptchaKraken) installs `[serve]`,
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 hcaptcha
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/JWriter20/CaptchaKraken/main/docs/assets/logo-card.png" alt="CaptchaKraken" width="128" height="128">
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/JWriter20/CaptchaKraken">CaptchaKraken</a>.
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/JWriter20/CaptchaKraken)**.
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/JWriter20/CaptchaKraken/main/docs/assets/demo/hcaptcha_grid.webp" width="260"
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/JWriter20/CaptchaKraken/main/docs/assets/demo/geetest_slide.webp" width="260"
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
- hCaptcha image select 12/12 in 10.5s · 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**
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
- **Ten more puzzle types**, as video and with the full method, at
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/JWriter20/CaptchaKraken#watch-it-work).
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/JWriter20/CaptchaKraken) installs `[serve]`,
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 hcaptcha
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
- Two runnable demos that drive a real stealth browser
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
- screenshot the challenge, run the **engine** on it, and print token speed /
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). These demos validate the
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, an unsupported hCaptcha puzzle (drag/video),
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
- choices=("hcaptcha", "recaptcha"),
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(vendor: str, solved: bool, err) -> str:
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(spec.vendor, ok, None)
127
+ reason = _explain(ok, None)
132
128
  except CaptchaSolveError as e:
133
129
  err = e
134
- reason = _explain(spec.vendor, False, e)
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(spec.vendor, False, e)
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.0.0"
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", "hcaptcha", "geetest", "turnstile",
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/JWriter20/CaptchaKraken#readme"
96
- Source = "https://github.com/JWriter20/CaptchaKraken"
97
- Issues = "https://github.com/JWriter20/CaptchaKraken/issues"
98
- Changelog = "https://github.com/JWriter20/CaptchaKraken/blob/main/CHANGELOG.md"
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.0.0"
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
- return {"reported": bool(ActionPlanner().report_outcome(args[0], Outcome(args[1]) == Outcome.SOLVED))}
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