captchakraken 2.2.0__tar.gz → 2.3.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-2.2.0 → captchakraken-2.3.0}/.gitignore +4 -1
- captchakraken-2.3.0/LICENSE +105 -0
- captchakraken-2.3.0/PKG-INFO +210 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/README.md +3 -1
- {captchakraken-2.2.0 → captchakraken-2.3.0}/pyproject.toml +6 -2
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/__init__.py +13 -1
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/cli.py +63 -0
- captchakraken-2.3.0/src/captchakraken/config.py +160 -0
- captchakraken-2.3.0/src/captchakraken/page_solver.py +1579 -0
- captchakraken-2.3.0/src/captchakraken/pinned_model.json +28 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/planner.py +248 -17
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/server_manager.py +3 -2
- captchakraken-2.3.0/src/captchakraken/trajectory.py +172 -0
- captchakraken-2.3.0/src/captchakraken/updater.py +168 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/test_checkbox_captcha.py +8 -0
- captchakraken-2.3.0/tests/test_credentials_file.py +87 -0
- captchakraken-2.3.0/tests/test_fetch_command.py +91 -0
- captchakraken-2.3.0/tests/test_freshness_check.py +85 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/test_object_detection.py +8 -0
- captchakraken-2.3.0/tests/test_page_solver.py +563 -0
- captchakraken-2.3.0/tests/test_pinned_model.py +86 -0
- captchakraken-2.3.0/tests/test_pixel_parser.py +76 -0
- captchakraken-2.3.0/tests/test_routing_headers.py +104 -0
- captchakraken-2.3.0/tests/test_solver_contract.py +230 -0
- captchakraken-2.2.0/PKG-INFO +0 -102
- captchakraken-2.2.0/src/captchakraken/config.py +0 -78
- {captchakraken-2.2.0 → captchakraken-2.3.0}/Dockerfile +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/examples/README.md +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/examples/_harness.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/examples/demoHcaptcha.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/examples/demoRecaptcha.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/action_types.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/image_processor.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/overlay.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/planner_types.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/solver.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/timing.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/tool_calls/find_checkbox.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/tool_calls/find_grid.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/src/captchakraken/tool_calls/move_indicator.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/conftest.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/grid_diag.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/grid_fp.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/grid_regression.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/grid_trace_reject.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/test_find_grid_corpus.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/test_grid_detection.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/test_grid_detection_ci.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/test_move_indicator.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/test_selected_fields.py +0 -0
- {captchakraken-2.2.0 → captchakraken-2.3.0}/tests/test_solver.py +0 -0
|
@@ -41,4 +41,7 @@ record_demos*.log
|
|
|
41
41
|
run_full_record.out
|
|
42
42
|
run_full_record.sh
|
|
43
43
|
*_video_attempt_*.png
|
|
44
|
-
captcha_videos/
|
|
44
|
+
captcha_videos/
|
|
45
|
+
# Personal VSCode workspace files (machine-specific; belong at a project
|
|
46
|
+
# root, not in the tree). One strayed into python/tests/.
|
|
47
|
+
*.code-workspace
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
CaptchaKraken Source-Available License v1.0
|
|
2
|
+
Copyright (c) 2026 JobHarvest / JWriter20. All rights reserved.
|
|
3
|
+
|
|
4
|
+
This license governs use of the CaptchaKraken software in this repository and of
|
|
5
|
+
the associated machine-learning model weights and LoRA adapters published by the
|
|
6
|
+
copyright holder (collectively, the "Software"). By using the Software you agree
|
|
7
|
+
to these terms. If you do not agree, do not use the Software.
|
|
8
|
+
|
|
9
|
+
================================================================================
|
|
10
|
+
1. GRANT
|
|
11
|
+
================================================================================
|
|
12
|
+
Subject to the restrictions below, you are granted a worldwide, royalty-free,
|
|
13
|
+
non-exclusive license to use, copy, modify, and distribute the Software, and to
|
|
14
|
+
incorporate it into your own products and services.
|
|
15
|
+
|
|
16
|
+
================================================================================
|
|
17
|
+
2. PERMITTED USES
|
|
18
|
+
================================================================================
|
|
19
|
+
You MAY use the Software for:
|
|
20
|
+
|
|
21
|
+
(a) Personal, non-commercial use, research, and education.
|
|
22
|
+
|
|
23
|
+
(b) Commercial use INSIDE a larger product or service that delivers
|
|
24
|
+
substantial value BEYOND captcha solving itself — that is, where captcha
|
|
25
|
+
solving is an internal, enabling component rather than the product.
|
|
26
|
+
|
|
27
|
+
Illustrative (non-exhaustive) examples of PERMITTED commercial use:
|
|
28
|
+
- Web scrapers and data-collection pipelines.
|
|
29
|
+
- Stealth / anti-detection browsers and browser automation frameworks
|
|
30
|
+
that use the Software as one internal capability.
|
|
31
|
+
- QA, testing, and accessibility tooling.
|
|
32
|
+
- Any application where the captcha solve is a means to an end the user
|
|
33
|
+
is actually paying for.
|
|
34
|
+
|
|
35
|
+
================================================================================
|
|
36
|
+
3. PROHIBITED USES
|
|
37
|
+
================================================================================
|
|
38
|
+
You MAY NOT, without a separate written commercial agreement from the copyright
|
|
39
|
+
holder:
|
|
40
|
+
|
|
41
|
+
(a) Sell, resell, rent, or otherwise offer for a fee any captcha-SOLVING
|
|
42
|
+
service, product, or API whose primary value is solving captchas,
|
|
43
|
+
where that service is powered by the Software or its model outputs.
|
|
44
|
+
|
|
45
|
+
(b) Distribute "thin wrappers" around the Software whose primary purpose is
|
|
46
|
+
captcha solving — including but not limited to browser extensions,
|
|
47
|
+
hosted endpoints, SaaS products, or CLIs that simply expose the
|
|
48
|
+
Software's solving capability to end users.
|
|
49
|
+
|
|
50
|
+
(c) Expose, proxy, or relay the responses or outputs of the Software's LoRA
|
|
51
|
+
models (e.g. bounding boxes, tile selections, click plans) through a paid
|
|
52
|
+
or public API as a captcha-solving service.
|
|
53
|
+
|
|
54
|
+
In short: you may BUILD WITH this Software, but you may not SELL THE SOLVE.
|
|
55
|
+
|
|
56
|
+
================================================================================
|
|
57
|
+
4. MODEL OUTPUTS
|
|
58
|
+
================================================================================
|
|
59
|
+
Outputs produced by the model weights/adapters are subject to Sections 2 and 3
|
|
60
|
+
to the same extent as the Software itself. Reselling or API-relaying those
|
|
61
|
+
outputs as a captcha-solving service is prohibited under Section 3(c).
|
|
62
|
+
|
|
63
|
+
================================================================================
|
|
64
|
+
5. REDISTRIBUTION
|
|
65
|
+
================================================================================
|
|
66
|
+
If you redistribute the Software (modified or not), you must retain this license
|
|
67
|
+
and this notice, and you must not remove or alter the restrictions in Section 3.
|
|
68
|
+
|
|
69
|
+
================================================================================
|
|
70
|
+
6. TRADEMARKS
|
|
71
|
+
================================================================================
|
|
72
|
+
This license does not grant permission to use the trade names, trademarks, or
|
|
73
|
+
product names of the copyright holder, except as required to describe the origin
|
|
74
|
+
of the Software.
|
|
75
|
+
|
|
76
|
+
================================================================================
|
|
77
|
+
7. NO WARRANTY
|
|
78
|
+
================================================================================
|
|
79
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
80
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
81
|
+
FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
82
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER
|
|
83
|
+
IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN
|
|
84
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
85
|
+
|
|
86
|
+
================================================================================
|
|
87
|
+
8. RESPONSIBLE USE
|
|
88
|
+
================================================================================
|
|
89
|
+
You are responsible for ensuring your use of the Software complies with all
|
|
90
|
+
applicable laws, the terms of service of any site you interact with, and
|
|
91
|
+
applicable anti-fraud and computer-access statutes. The copyright holder does
|
|
92
|
+
not endorse using the Software to violate any third party's terms or rights.
|
|
93
|
+
|
|
94
|
+
================================================================================
|
|
95
|
+
9. COMMERCIAL LICENSING
|
|
96
|
+
================================================================================
|
|
97
|
+
For uses prohibited under Section 3 — including selling captcha-solving as a
|
|
98
|
+
service — contact the copyright holder to discuss a separate commercial license.
|
|
99
|
+
|
|
100
|
+
--------------------------------------------------------------------------------
|
|
101
|
+
This text is a license, not legal advice. If the boundary between a permitted
|
|
102
|
+
"value-add" product and a prohibited "thin wrapper" is unclear for your use
|
|
103
|
+
case, ask by opening an issue or messaging the maintainer on GitHub:
|
|
104
|
+
https://github.com/JWriter20/CaptchaKraken
|
|
105
|
+
--------------------------------------------------------------------------------
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: captchakraken
|
|
3
|
+
Version: 2.3.0
|
|
4
|
+
Summary: Self-hosted captcha solver: OpenCV grid detection + a fine-tuned Qwen3.5-9B vision LoRA served on vLLM.
|
|
5
|
+
Project-URL: Homepage, https://github.com/JWriter20/CaptchaKraken
|
|
6
|
+
Project-URL: Issues, https://github.com/JWriter20/CaptchaKraken/issues
|
|
7
|
+
Author: Jake Writer
|
|
8
|
+
License: CaptchaKraken Source-Available License v1.0
|
|
9
|
+
Copyright (c) 2026 JobHarvest / JWriter20. All rights reserved.
|
|
10
|
+
|
|
11
|
+
This license governs use of the CaptchaKraken software in this repository and of
|
|
12
|
+
the associated machine-learning model weights and LoRA adapters published by the
|
|
13
|
+
copyright holder (collectively, the "Software"). By using the Software you agree
|
|
14
|
+
to these terms. If you do not agree, do not use the Software.
|
|
15
|
+
|
|
16
|
+
================================================================================
|
|
17
|
+
1. GRANT
|
|
18
|
+
================================================================================
|
|
19
|
+
Subject to the restrictions below, you are granted a worldwide, royalty-free,
|
|
20
|
+
non-exclusive license to use, copy, modify, and distribute the Software, and to
|
|
21
|
+
incorporate it into your own products and services.
|
|
22
|
+
|
|
23
|
+
================================================================================
|
|
24
|
+
2. PERMITTED USES
|
|
25
|
+
================================================================================
|
|
26
|
+
You MAY use the Software for:
|
|
27
|
+
|
|
28
|
+
(a) Personal, non-commercial use, research, and education.
|
|
29
|
+
|
|
30
|
+
(b) Commercial use INSIDE a larger product or service that delivers
|
|
31
|
+
substantial value BEYOND captcha solving itself — that is, where captcha
|
|
32
|
+
solving is an internal, enabling component rather than the product.
|
|
33
|
+
|
|
34
|
+
Illustrative (non-exhaustive) examples of PERMITTED commercial use:
|
|
35
|
+
- Web scrapers and data-collection pipelines.
|
|
36
|
+
- Stealth / anti-detection browsers and browser automation frameworks
|
|
37
|
+
that use the Software as one internal capability.
|
|
38
|
+
- QA, testing, and accessibility tooling.
|
|
39
|
+
- Any application where the captcha solve is a means to an end the user
|
|
40
|
+
is actually paying for.
|
|
41
|
+
|
|
42
|
+
================================================================================
|
|
43
|
+
3. PROHIBITED USES
|
|
44
|
+
================================================================================
|
|
45
|
+
You MAY NOT, without a separate written commercial agreement from the copyright
|
|
46
|
+
holder:
|
|
47
|
+
|
|
48
|
+
(a) Sell, resell, rent, or otherwise offer for a fee any captcha-SOLVING
|
|
49
|
+
service, product, or API whose primary value is solving captchas,
|
|
50
|
+
where that service is powered by the Software or its model outputs.
|
|
51
|
+
|
|
52
|
+
(b) Distribute "thin wrappers" around the Software whose primary purpose is
|
|
53
|
+
captcha solving — including but not limited to browser extensions,
|
|
54
|
+
hosted endpoints, SaaS products, or CLIs that simply expose the
|
|
55
|
+
Software's solving capability to end users.
|
|
56
|
+
|
|
57
|
+
(c) Expose, proxy, or relay the responses or outputs of the Software's LoRA
|
|
58
|
+
models (e.g. bounding boxes, tile selections, click plans) through a paid
|
|
59
|
+
or public API as a captcha-solving service.
|
|
60
|
+
|
|
61
|
+
In short: you may BUILD WITH this Software, but you may not SELL THE SOLVE.
|
|
62
|
+
|
|
63
|
+
================================================================================
|
|
64
|
+
4. MODEL OUTPUTS
|
|
65
|
+
================================================================================
|
|
66
|
+
Outputs produced by the model weights/adapters are subject to Sections 2 and 3
|
|
67
|
+
to the same extent as the Software itself. Reselling or API-relaying those
|
|
68
|
+
outputs as a captcha-solving service is prohibited under Section 3(c).
|
|
69
|
+
|
|
70
|
+
================================================================================
|
|
71
|
+
5. REDISTRIBUTION
|
|
72
|
+
================================================================================
|
|
73
|
+
If you redistribute the Software (modified or not), you must retain this license
|
|
74
|
+
and this notice, and you must not remove or alter the restrictions in Section 3.
|
|
75
|
+
|
|
76
|
+
================================================================================
|
|
77
|
+
6. TRADEMARKS
|
|
78
|
+
================================================================================
|
|
79
|
+
This license does not grant permission to use the trade names, trademarks, or
|
|
80
|
+
product names of the copyright holder, except as required to describe the origin
|
|
81
|
+
of the Software.
|
|
82
|
+
|
|
83
|
+
================================================================================
|
|
84
|
+
7. NO WARRANTY
|
|
85
|
+
================================================================================
|
|
86
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
87
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
88
|
+
FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
89
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER
|
|
90
|
+
IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN
|
|
91
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
92
|
+
|
|
93
|
+
================================================================================
|
|
94
|
+
8. RESPONSIBLE USE
|
|
95
|
+
================================================================================
|
|
96
|
+
You are responsible for ensuring your use of the Software complies with all
|
|
97
|
+
applicable laws, the terms of service of any site you interact with, and
|
|
98
|
+
applicable anti-fraud and computer-access statutes. The copyright holder does
|
|
99
|
+
not endorse using the Software to violate any third party's terms or rights.
|
|
100
|
+
|
|
101
|
+
================================================================================
|
|
102
|
+
9. COMMERCIAL LICENSING
|
|
103
|
+
================================================================================
|
|
104
|
+
For uses prohibited under Section 3 — including selling captcha-solving as a
|
|
105
|
+
service — contact the copyright holder to discuss a separate commercial license.
|
|
106
|
+
|
|
107
|
+
--------------------------------------------------------------------------------
|
|
108
|
+
This text is a license, not legal advice. If the boundary between a permitted
|
|
109
|
+
"value-add" product and a prohibited "thin wrapper" is unclear for your use
|
|
110
|
+
case, ask by opening an issue or messaging the maintainer on GitHub:
|
|
111
|
+
https://github.com/JWriter20/CaptchaKraken
|
|
112
|
+
--------------------------------------------------------------------------------
|
|
113
|
+
License-File: LICENSE
|
|
114
|
+
Keywords: automation,captcha,computer-vision,hcaptcha,qwen,recaptcha,vllm
|
|
115
|
+
Classifier: License :: Other/Proprietary License
|
|
116
|
+
Classifier: Operating System :: OS Independent
|
|
117
|
+
Classifier: Programming Language :: Python :: 3
|
|
118
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
119
|
+
Requires-Python: >=3.10
|
|
120
|
+
Requires-Dist: numpy>=1.24.0
|
|
121
|
+
Requires-Dist: opencv-python-headless>=4.10.0
|
|
122
|
+
Requires-Dist: pillow>=10.0.0
|
|
123
|
+
Requires-Dist: pydantic>=2.0.0
|
|
124
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
125
|
+
Requires-Dist: requests>=2.31.0
|
|
126
|
+
Provides-Extra: dev
|
|
127
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
128
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
129
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
130
|
+
Provides-Extra: serve
|
|
131
|
+
Requires-Dist: accelerate>=0.27.0; extra == 'serve'
|
|
132
|
+
Requires-Dist: huggingface-hub>=0.23.0; extra == 'serve'
|
|
133
|
+
Requires-Dist: torch>=2.0.0; extra == 'serve'
|
|
134
|
+
Requires-Dist: transformers>=4.40.0; extra == 'serve'
|
|
135
|
+
Requires-Dist: vllm>=0.6.3; extra == 'serve'
|
|
136
|
+
Description-Content-Type: text/markdown
|
|
137
|
+
|
|
138
|
+
# captchakraken
|
|
139
|
+
|
|
140
|
+
The Python engine + CLI behind [CaptchaKraken](https://github.com/JWriter20/CaptchaKraken):
|
|
141
|
+
OpenCV grid detection + a fine-tuned **Qwen3.5-9B** vision LoRA served on
|
|
142
|
+
**vLLM**. Given a screenshot of a captcha grid, it locates the tiles and returns
|
|
143
|
+
the click plan. Ships the `captchakraken` command.
|
|
144
|
+
|
|
145
|
+
> For demo videos, accuracy numbers, the browser driver, and the full
|
|
146
|
+
> self-hosting guide, see the main repo
|
|
147
|
+
> **[CaptchaKraken](https://github.com/JWriter20/CaptchaKraken)**.
|
|
148
|
+
|
|
149
|
+
## Install
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
pip install captchakraken # client: OpenCV detection + vLLM HTTP planner
|
|
153
|
+
pip install "captchakraken[serve]" # + the serving stack (vLLM/torch) to self-host
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The base install is lightweight — everything you need to solve captchas against
|
|
157
|
+
a vLLM server (local or remote). The `[serve]` extra pulls the heavy stack only
|
|
158
|
+
if you want to run the model yourself. The one-command
|
|
159
|
+
[`setup.sh`](https://github.com/JWriter20/CaptchaKraken) installs `[serve]`,
|
|
160
|
+
downloads the weights, and writes an env file for you.
|
|
161
|
+
|
|
162
|
+
## Hands-off server
|
|
163
|
+
|
|
164
|
+
The vLLM server is managed for you. On your first solve, if the configured
|
|
165
|
+
endpoint is **local** and nothing is listening, a server is started
|
|
166
|
+
automatically and reused. Point `VLLM_BASE_URL` at a server you already run to
|
|
167
|
+
skip local management entirely.
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
captchakraken server start | stop | status | run
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Usage
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Solve an image/video: classify → find_grid → plan. Prints the click actions.
|
|
177
|
+
captchakraken path/to/captcha.png
|
|
178
|
+
captchakraken path/to/captcha.png --puzzle-source hcaptcha
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from captchakraken import CaptchaSolver
|
|
183
|
+
|
|
184
|
+
solver = CaptchaSolver() # connects to / auto-starts a local vLLM
|
|
185
|
+
actions = solver.solve("captcha.png")
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Pure-OpenCV tool subcommands (no model): `find-grid`, `find-checkbox`,
|
|
189
|
+
`detect-selected`, `grid-cell-states`, `find-move`, `find-movable`, and a
|
|
190
|
+
persistent `serve` worker the browser driver polls.
|
|
191
|
+
|
|
192
|
+
## Configuration (model-agnostic)
|
|
193
|
+
|
|
194
|
+
Everything model-specific lives in `captchakraken.config` and is env-overridable
|
|
195
|
+
— the solver never hard-codes a model.
|
|
196
|
+
|
|
197
|
+
| Variable | Meaning | Default |
|
|
198
|
+
|---|---|---|
|
|
199
|
+
| `VLLM_BASE_URL` | Inference endpoint | `http://localhost:8000/v1` |
|
|
200
|
+
| `CAPTCHA_KRAKEN_API_KEY` | Bearer token (`VLLM_API_KEY` also accepted) | `EMPTY` |
|
|
201
|
+
| `CAPTCHA_BASE_MODEL` | Base weights vLLM loads | `Qwen/Qwen3.5-9B` |
|
|
202
|
+
| `CAPTCHA_LORA_ADAPTER` | Captcha adapter (HF id or path) | `CaptchaKraken/CaptchaKraken_v1` |
|
|
203
|
+
| `CAPTCHA_LORA_NAME` | Served adapter name the client requests | `captcha` |
|
|
204
|
+
| `CAPTCHA_KRAKEN_AUTOSTART` | `0` disables local auto-start | `1` |
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
**CaptchaKraken Source-Available License v1.0** — see [LICENSE](./LICENSE).
|
|
209
|
+
Build *with* it (scrapers, stealth browsers, QA tooling); you may **not sell the
|
|
210
|
+
solve itself** or ship a thin wrapper (browser extension, hosted solving API).
|
|
@@ -68,4 +68,6 @@ Everything model-specific lives in `captchakraken.config` and is env-overridable
|
|
|
68
68
|
|
|
69
69
|
## License
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
**CaptchaKraken Source-Available License v1.0** — see [LICENSE](./LICENSE).
|
|
72
|
+
Build *with* it (scrapers, stealth browsers, QA tooling); you may **not sell the
|
|
73
|
+
solve itself** or ship a thin wrapper (browser extension, hosted solving API).
|
|
@@ -4,17 +4,21 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "captchakraken"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.3.0"
|
|
8
8
|
description = "Self-hosted captcha solver: OpenCV grid detection + a fine-tuned Qwen3.5-9B vision LoRA served on vLLM."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
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
|
+
# or thin-wrapper resale — see LICENSE. (GPL-3.0 would wrongly permit reselling.)
|
|
14
|
+
license = { file = "LICENSE" }
|
|
12
15
|
authors = [{ name = "Jake Writer" }]
|
|
13
16
|
keywords = ["captcha", "recaptcha", "hcaptcha", "vllm", "qwen", "computer-vision", "automation"]
|
|
14
17
|
classifiers = [
|
|
15
18
|
"Programming Language :: Python :: 3",
|
|
16
19
|
"Operating System :: OS Independent",
|
|
17
20
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
21
|
+
"License :: Other/Proprietary License",
|
|
18
22
|
]
|
|
19
23
|
|
|
20
24
|
# Core = the lightweight CLIENT: OpenCV grid detection + the HTTP planner that
|
|
@@ -40,14 +40,26 @@ from .overlay import add_overlays_to_image
|
|
|
40
40
|
try: # pragma: no cover - exercised only when the serving stack is installed
|
|
41
41
|
from .planner import ActionPlanner
|
|
42
42
|
from .solver import CaptchaSolver, solve_captcha
|
|
43
|
+
|
|
44
|
+
# The page driver sits behind the same guard: it imports `solver`, so it has
|
|
45
|
+
# the same dependency floor. It needs NO browser package of its own — the
|
|
46
|
+
# caller supplies the Playwright-compatible page (see page_solver's module
|
|
47
|
+
# docstring on why we duck-type rather than import one).
|
|
48
|
+
from .page_solver import PageSolver, SolveResult, solve_captcha_on_page
|
|
43
49
|
except ModuleNotFoundError:
|
|
44
50
|
ActionPlanner = None # type: ignore[assignment,misc]
|
|
45
51
|
CaptchaSolver = None # type: ignore[assignment,misc]
|
|
46
52
|
solve_captcha = None # type: ignore[assignment]
|
|
53
|
+
PageSolver = None # type: ignore[assignment,misc]
|
|
54
|
+
SolveResult = None # type: ignore[assignment,misc]
|
|
55
|
+
solve_captcha_on_page = None # type: ignore[assignment]
|
|
47
56
|
|
|
48
57
|
__all__ = [
|
|
49
58
|
"CaptchaSolver",
|
|
50
59
|
"solve_captcha",
|
|
60
|
+
"PageSolver",
|
|
61
|
+
"SolveResult",
|
|
62
|
+
"solve_captcha_on_page",
|
|
51
63
|
"ActionPlanner",
|
|
52
64
|
"ImageProcessor",
|
|
53
65
|
"CaptchaAction",
|
|
@@ -58,4 +70,4 @@ __all__ = [
|
|
|
58
70
|
"add_overlays_to_image",
|
|
59
71
|
]
|
|
60
72
|
|
|
61
|
-
__version__ = "2.
|
|
73
|
+
__version__ = "2.3.0"
|
|
@@ -42,11 +42,18 @@ Modes:
|
|
|
42
42
|
these — a local server auto-starts on the first solve (disable with
|
|
43
43
|
CAPTCHA_KRAKEN_AUTOSTART=0). Point VLLM_BASE_URL at your own server to
|
|
44
44
|
skip local management entirely.
|
|
45
|
+
|
|
46
|
+
captchakraken fetch [--weights-only|--engine-only] [--no-restart] [--dry-run]
|
|
47
|
+
Update in one step: pull the latest CaptchaKraken model from the HF org
|
|
48
|
+
(https://huggingface.co/CaptchaKraken) AND upgrade the vLLM serving
|
|
49
|
+
stack, then restart a running local server so it takes effect. Use this
|
|
50
|
+
to get new model revisions + engine fixes without re-running setup.sh.
|
|
45
51
|
"""
|
|
46
52
|
|
|
47
53
|
import argparse
|
|
48
54
|
import json
|
|
49
55
|
import os
|
|
56
|
+
import subprocess
|
|
50
57
|
import sys
|
|
51
58
|
|
|
52
59
|
from .solver import CaptchaSolver, UnsupportedCaptchaError
|
|
@@ -372,6 +379,7 @@ def _handle_serve() -> bool:
|
|
|
372
379
|
find-grid {image} -> grid_boxes | null
|
|
373
380
|
grid-cell-states {a, b} -> states | {grid: null}
|
|
374
381
|
grid-cell-states-fixed {a, b, grid_boxes} -> states
|
|
382
|
+
check-movement {a, b, threshold?} -> {has_movement: bool}
|
|
375
383
|
|
|
376
384
|
Unknown cmd / malformed line -> an {ok:false} response (the process keeps
|
|
377
385
|
running). EOF on stdin ends the loop cleanly. All heavy detection delegates
|
|
@@ -396,6 +404,15 @@ def _handle_serve() -> bool:
|
|
|
396
404
|
if not grid_boxes:
|
|
397
405
|
raise ValueError("empty grid_boxes")
|
|
398
406
|
return _compute_grid_cell_states(req["a"], req["b"], grid_boxes)
|
|
407
|
+
if cmd == "check-movement":
|
|
408
|
+
# Freshness check for the JS solver: did the captcha frame change
|
|
409
|
+
# (tiles faded in / refreshed) between the screenshot we sent the
|
|
410
|
+
# model and now? Same primitive the one-shot `check-movement` uses.
|
|
411
|
+
from .image_processor import ImageProcessor
|
|
412
|
+
|
|
413
|
+
threshold = float(req.get("threshold", 0.005))
|
|
414
|
+
moved = ImageProcessor.detect_movement(req["a"], req["b"], threshold)
|
|
415
|
+
return {"has_movement": bool(moved)}
|
|
399
416
|
raise ValueError(f"unknown cmd: {cmd!r}")
|
|
400
417
|
|
|
401
418
|
# Signal readiness so the JS side knows imports are done before it polls.
|
|
@@ -560,9 +577,55 @@ def _handle_server_commands() -> bool:
|
|
|
560
577
|
return True
|
|
561
578
|
|
|
562
579
|
|
|
580
|
+
def _handle_fetch() -> bool:
|
|
581
|
+
"""`captchakraken fetch` — pull the latest published weights from the HF org
|
|
582
|
+
and refresh the vLLM serving stack, restarting a running local server so the
|
|
583
|
+
update takes effect. See updater.fetch for the full behavior."""
|
|
584
|
+
if len(sys.argv) <= 1 or sys.argv[1] not in {"fetch", "update"}:
|
|
585
|
+
return False
|
|
586
|
+
|
|
587
|
+
flags = set(sys.argv[2:])
|
|
588
|
+
unknown = flags - {"--weights-only", "--engine-only", "--no-restart", "--dry-run"}
|
|
589
|
+
if unknown:
|
|
590
|
+
print(
|
|
591
|
+
json.dumps({"error": f"unknown fetch flag(s): {sorted(unknown)} "
|
|
592
|
+
"(use --weights-only|--engine-only|--no-restart|--dry-run)"}),
|
|
593
|
+
file=sys.stderr,
|
|
594
|
+
)
|
|
595
|
+
sys.exit(2)
|
|
596
|
+
if "--weights-only" in flags and "--engine-only" in flags:
|
|
597
|
+
print(json.dumps({"error": "--weights-only and --engine-only are mutually exclusive"}),
|
|
598
|
+
file=sys.stderr)
|
|
599
|
+
sys.exit(2)
|
|
600
|
+
|
|
601
|
+
from . import updater
|
|
602
|
+
|
|
603
|
+
weights = "--engine-only" not in flags
|
|
604
|
+
engine = "--weights-only" not in flags
|
|
605
|
+
try:
|
|
606
|
+
result = updater.fetch(
|
|
607
|
+
weights=weights,
|
|
608
|
+
engine=engine,
|
|
609
|
+
restart="--no-restart" not in flags,
|
|
610
|
+
dry_run="--dry-run" in flags,
|
|
611
|
+
)
|
|
612
|
+
print(json.dumps(result))
|
|
613
|
+
return True
|
|
614
|
+
except subprocess.CalledProcessError as e:
|
|
615
|
+
print(json.dumps({"error": f"fetch step failed (exit {e.returncode}): "
|
|
616
|
+
f"{' '.join(e.cmd) if isinstance(e.cmd, list) else e.cmd}"}),
|
|
617
|
+
file=sys.stderr)
|
|
618
|
+
sys.exit(1)
|
|
619
|
+
except Exception as e:
|
|
620
|
+
print(json.dumps({"error": str(e)}), file=sys.stderr)
|
|
621
|
+
sys.exit(1)
|
|
622
|
+
|
|
623
|
+
|
|
563
624
|
def main():
|
|
564
625
|
if _handle_server_commands():
|
|
565
626
|
return
|
|
627
|
+
if _handle_fetch():
|
|
628
|
+
return
|
|
566
629
|
if _handle_movement_commands():
|
|
567
630
|
return
|
|
568
631
|
if _handle_move_commands():
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Central, model-agnostic configuration for CaptchaKraken.
|
|
3
|
+
|
|
4
|
+
Every model-specific value lives here and is overridable via environment
|
|
5
|
+
variables, so swapping the underlying model never requires touching the solver,
|
|
6
|
+
planner, or CLI. The defaults point at the published unified captcha LoRA, but
|
|
7
|
+
nothing in the codebase hard-codes a model name outside this module.
|
|
8
|
+
|
|
9
|
+
The two variables MOST users ever set:
|
|
10
|
+
VLLM_BASE_URL where inference requests go (local or a remote server)
|
|
11
|
+
CAPTCHA_KRAKEN_API_KEY bearer token for that server
|
|
12
|
+
|
|
13
|
+
Advanced / self-hosting overrides (only needed to change WHICH model is served):
|
|
14
|
+
CAPTCHA_BASE_MODEL base weights vLLM loads (HF id or local path)
|
|
15
|
+
CAPTCHA_LORA_ADAPTER LoRA applied on top (HF id or local path)
|
|
16
|
+
CAPTCHA_LORA_REVISION adapter git revision to serve
|
|
17
|
+
CAPTCHA_LORA_NAME served adapter name the client asks for as `model`
|
|
18
|
+
|
|
19
|
+
Defaults for the model-identity values come from `pinned_model.json` (see
|
|
20
|
+
`pinned()`), which records the model + prompt pair this release was validated
|
|
21
|
+
against.
|
|
22
|
+
VLLM_PORT, VLLM_GPU_MEMORY_UTILIZATION, VLLM_MAX_MODEL_LEN, VLLM_MAX_LORA_RANK
|
|
23
|
+
CAPTCHA_KRAKEN_AUTOSTART=0 disable auto-starting a local server on first use
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import os
|
|
28
|
+
from functools import lru_cache
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
from typing import Any, Dict
|
|
31
|
+
|
|
32
|
+
_PINNED_MODEL_PATH = Path(__file__).with_name("pinned_model.json")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@lru_cache(maxsize=1)
|
|
36
|
+
def pinned() -> Dict[str, Any]:
|
|
37
|
+
"""The pinned model manifest shipped with this release.
|
|
38
|
+
|
|
39
|
+
Defines the (base model, adapter, serving prompts) triple this version was
|
|
40
|
+
validated against. Env vars still override every value below — the manifest
|
|
41
|
+
supplies the DEFAULT and gives CI something to assert against, so an adapter
|
|
42
|
+
or prompt change is a reviewable diff instead of a silent drift.
|
|
43
|
+
"""
|
|
44
|
+
with _PINNED_MODEL_PATH.open(encoding="utf-8") as f:
|
|
45
|
+
return json.load(f)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ── Inference endpoint ──────────────────────────────────────────────────────
|
|
49
|
+
def base_url() -> str:
|
|
50
|
+
"""OpenAI-compatible endpoint, e.g. http://localhost:8000/v1."""
|
|
51
|
+
return os.getenv("VLLM_BASE_URL", f"http://localhost:{port()}/v1")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def state_dir() -> Path:
|
|
55
|
+
"""Shared state dir for the pidfile, lockfile, server log, and credentials."""
|
|
56
|
+
return Path(os.getenv("CAPTCHA_KRAKEN_STATE_DIR", str(Path.home() / ".captchakraken")))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def credentials_path() -> Path:
|
|
60
|
+
return state_dir() / "credentials"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _key_from_credentials_file() -> str:
|
|
64
|
+
"""Key written by the MCP server's signup flow (or a future `login` command).
|
|
65
|
+
|
|
66
|
+
Hosted-API onboarding writes the key to a 0600 file rather than returning it
|
|
67
|
+
through the agent transcript, so it never lands in an LLM context window.
|
|
68
|
+
Reading it here is what lets a solve authenticate with NO env vars set.
|
|
69
|
+
|
|
70
|
+
Deliberately tolerant of a bare token, an env-file line, or surrounding
|
|
71
|
+
quotes: the file is written by a *separate* tool, and a format mismatch here
|
|
72
|
+
would surface as a baffling 401 rather than an obvious parse error.
|
|
73
|
+
Returns "" whenever the file is missing, unreadable, or has no usable line.
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
text = credentials_path().read_text(encoding="utf-8")
|
|
77
|
+
except OSError:
|
|
78
|
+
return ""
|
|
79
|
+
|
|
80
|
+
for line in text.splitlines():
|
|
81
|
+
line = line.strip()
|
|
82
|
+
if not line or line.startswith("#"):
|
|
83
|
+
continue
|
|
84
|
+
if "=" in line:
|
|
85
|
+
name, _, value = line.partition("=")
|
|
86
|
+
# An env-file with unrelated keys must not yield a bogus token.
|
|
87
|
+
if name.strip() not in {"CAPTCHA_KRAKEN_API_KEY", "VLLM_API_KEY"}:
|
|
88
|
+
continue
|
|
89
|
+
line = value.strip()
|
|
90
|
+
return line.strip("'\"")
|
|
91
|
+
|
|
92
|
+
return ""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def api_key() -> str:
|
|
96
|
+
"""Bearer token, in precedence order.
|
|
97
|
+
|
|
98
|
+
Env first so an explicit override always wins, then the credentials file so
|
|
99
|
+
the MCP/hosted path needs no env plumbing at all, then "EMPTY" (which leaves
|
|
100
|
+
a self-hosted local vLLM open, as before).
|
|
101
|
+
"""
|
|
102
|
+
return (
|
|
103
|
+
os.getenv("CAPTCHA_KRAKEN_API_KEY")
|
|
104
|
+
or os.getenv("VLLM_API_KEY")
|
|
105
|
+
or _key_from_credentials_file()
|
|
106
|
+
or "EMPTY"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ── Model identity (all overridable — the CLI itself is model-agnostic) ──────
|
|
111
|
+
def base_model() -> str:
|
|
112
|
+
return os.getenv("CAPTCHA_BASE_MODEL", pinned()["base_model"])
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def lora_adapter() -> str:
|
|
116
|
+
"""HF repo id or local path of the captcha adapter served on top of the base."""
|
|
117
|
+
return os.getenv("CAPTCHA_LORA_ADAPTER", pinned()["lora_adapter"])
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def lora_revision() -> str:
|
|
121
|
+
"""Git revision of the adapter repo to serve — pinning this is what makes
|
|
122
|
+
'the adapter this release was tested against' a reproducible statement."""
|
|
123
|
+
return os.getenv("CAPTCHA_LORA_REVISION", pinned()["lora_revision"])
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def lora_name() -> str:
|
|
127
|
+
"""The served LoRA name the client sends as the `model` field."""
|
|
128
|
+
return os.getenv("CAPTCHA_LORA_NAME", pinned()["lora_name"])
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# ── Local vLLM server knobs ─────────────────────────────────────────────────
|
|
132
|
+
def port() -> int:
|
|
133
|
+
return int(os.getenv("VLLM_PORT", "8000"))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def gpu_memory_utilization() -> float:
|
|
137
|
+
return float(os.getenv("VLLM_GPU_MEMORY_UTILIZATION", "0.80"))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def max_model_len() -> int:
|
|
141
|
+
return int(os.getenv("VLLM_MAX_MODEL_LEN", "65536"))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def max_lora_rank() -> int:
|
|
145
|
+
return int(os.getenv("VLLM_MAX_LORA_RANK", "64"))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def autostart_enabled() -> bool:
|
|
149
|
+
"""Whether to auto-launch a local server on first use (default on)."""
|
|
150
|
+
return os.getenv("CAPTCHA_KRAKEN_AUTOSTART", "1") != "0"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def extra_serve_args() -> list:
|
|
154
|
+
"""Extra flags appended verbatim to `vllm serve` (space-separated).
|
|
155
|
+
|
|
156
|
+
Escape hatch for serving tweaks the config doesn't model directly — e.g.
|
|
157
|
+
`--enforce-eager` or a smaller `--max-num-seqs` to squeeze onto a tight GPU.
|
|
158
|
+
"""
|
|
159
|
+
raw = os.getenv("VLLM_EXTRA_ARGS", "").strip()
|
|
160
|
+
return raw.split() if raw else []
|