captchakraken 2.9.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +112 -0
- package/NOTICE +36 -0
- package/dist/cli-invocation.d.ts +7 -36
- package/dist/cli-invocation.js +8 -49
- package/dist/errors.d.ts +7 -27
- package/dist/errors.js +6 -25
- package/dist/humanize.d.ts +56 -240
- package/dist/humanize.js +159 -415
- package/dist/index.d.ts +5 -129
- package/dist/index.js +4 -98
- package/dist/kinds.d.ts +135 -0
- package/dist/kinds.js +70 -0
- package/dist/limits.d.ts +0 -48
- package/dist/limits.js +2 -48
- package/dist/model-name.d.ts +4 -19
- package/dist/model-name.js +32 -40
- package/dist/playwright-types.d.ts +25 -95
- package/dist/playwright-types.js +0 -23
- package/dist/puppeteer-adapter.d.ts +4 -43
- package/dist/puppeteer-adapter.js +22 -58
- package/dist/python-command.d.ts +0 -41
- package/dist/python-command.js +0 -37
- package/dist/selectors.d.ts +54 -0
- package/dist/selectors.js +117 -0
- package/dist/slide-geometry.d.ts +2 -29
- package/dist/slide-geometry.js +18 -44
- package/dist/solver.d.ts +131 -708
- package/dist/solver.js +1319 -3382
- package/dist/timing.d.ts +7 -40
- package/dist/timing.js +4 -36
- package/dist/token-usage.d.ts +1 -7
- package/dist/token-usage.js +10 -95
- package/dist/types.d.ts +77 -493
- package/dist/watcher.d.ts +5 -78
- package/dist/watcher.js +0 -56
- package/package.json +11 -4
- package/python/AGENTS.md +122 -0
- package/python/NOTICE +36 -0
- package/python/README.md +1 -2
- package/python/examples/README.md +10 -0
- package/python/examples/_harness.py +4 -73
- package/python/examples/demoHcaptcha.py +0 -13
- package/python/examples/demoRecaptcha.py +0 -10
- package/python/examples/watch_playwright.py +1 -26
- package/python/examples/with_local_gguf.py +28 -0
- package/python/examples/with_playwright.py +0 -19
- package/python/pyproject.toml +68 -19
- package/python/src/captchakraken/__init__.py +37 -45
- package/python/src/captchakraken/action_types.py +14 -36
- package/python/src/captchakraken/cli.py +262 -883
- package/python/src/captchakraken/config.py +60 -146
- package/python/src/captchakraken/errors.py +17 -95
- package/python/src/captchakraken/humanize.py +174 -597
- package/python/src/captchakraken/image_processor.py +16 -235
- package/python/src/captchakraken/keyframes.py +46 -484
- package/python/src/captchakraken/kinds.py +152 -0
- package/python/src/captchakraken/models.json +36 -4
- package/python/src/captchakraken/overlay.py +65 -498
- package/python/src/captchakraken/page_solver.py +1282 -3101
- package/python/src/captchakraken/planner.py +65 -487
- package/python/src/captchakraken/prompts.py +45 -261
- package/python/src/captchakraken/selectors.py +184 -0
- package/python/src/captchakraken/server_manager.py +7 -58
- package/python/src/captchakraken/solver.py +128 -522
- package/python/src/captchakraken/timing.py +20 -74
- package/python/src/captchakraken/tool_calls/board_painted.py +47 -0
- package/python/src/captchakraken/tool_calls/find_checkbox.py +20 -30
- package/python/src/captchakraken/tool_calls/find_grid.py +186 -1339
- package/python/src/captchakraken/tool_calls/track_piece.py +81 -57
- package/python/src/captchakraken/updater.py +7 -76
- package/python/src/captchakraken/watcher.py +12 -101
- package/scripts/copy-python.mjs +10 -10
- package/scripts/setup-python.js +1 -21
- package/dist/trajectory.d.ts +0 -58
- package/dist/trajectory.js +0 -266
- package/python/src/captchakraken/tool_calls/move_indicator.py +0 -431
- package/python/src/captchakraken/trajectory.py +0 -284
package/dist/trajectory.js
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Human-like mouse trajectories for the page driver.
|
|
4
|
-
*
|
|
5
|
-
* The TypeScript side of `python/src/captchakraken/trajectory.py`; the two are
|
|
6
|
-
* one implementation in two languages, pinned by `test_trajectory_parity.py`.
|
|
7
|
-
*
|
|
8
|
-
* What makes the output human rather than a lerp:
|
|
9
|
-
* - a Bezier arc, not a straight line (hand motion bows)
|
|
10
|
-
* - Fitts's-law duration: distance and precision set the time, not a constant
|
|
11
|
-
* - an ease-in-out velocity profile, so the cursor accelerates then brakes
|
|
12
|
-
* - sub-pixel jitter that scales with speed (fast motion is sloppier)
|
|
13
|
-
* - a small overshoot-and-correct on longer moves, which is the single most
|
|
14
|
-
* recognisable human tell and the one a linear interpolation never produces
|
|
15
|
-
*
|
|
16
|
-
* `generate_swipe` is the SAME contract for a FINGER, and it is a different
|
|
17
|
-
* model rather than the mouse model with different constants — see its
|
|
18
|
-
* docstring.
|
|
19
|
-
*
|
|
20
|
-
* Camoufox's own `humanize` is a DIFFERENT, browser-level mechanism that
|
|
21
|
-
* re-humanises every `mouse.move()` it is handed. Running both composes them —
|
|
22
|
-
* measured 82.1s vs 13.4s on one a GeeTest v4 slider solve, because each of the 60
|
|
23
|
-
* points below became its own humanised sub-trajectory. Drive with humanize off.
|
|
24
|
-
*/
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.generate_trajectory = generate_trajectory;
|
|
27
|
-
exports.generate_swipe = generate_swipe;
|
|
28
|
-
// Fitts's law: MT = a + b * log2(distance / width + 1). The constants are the
|
|
29
|
-
// usual empirical range for a mouse; `WIDTH` stands in for target precision.
|
|
30
|
-
const FITTS_A_MS = 90.0;
|
|
31
|
-
const FITTS_B_MS = 105.0;
|
|
32
|
-
const WIDTH_PX = 28.0;
|
|
33
|
-
// Below this, a move is a nudge inside one element: no overshoot, no arc.
|
|
34
|
-
const SHORT_MOVE_PX = 24.0;
|
|
35
|
-
// Overshooting a 30px hop looks like a twitch, not a human.
|
|
36
|
-
const OVERSHOOT_MIN_DISTANCE_PX = 220.0;
|
|
37
|
-
/** Cubic ease. Real pointer motion is roughly bell-shaped in velocity. */
|
|
38
|
-
function easeInOut(t) {
|
|
39
|
-
if (t < 0.5)
|
|
40
|
-
return 4.0 * t * t * t;
|
|
41
|
-
return 1.0 - Math.pow(-2.0 * t + 2.0, 3) / 2.0;
|
|
42
|
-
}
|
|
43
|
-
function cubicBezier(p0, p1, p2, p3, t) {
|
|
44
|
-
const u = 1.0 - t;
|
|
45
|
-
const x = u * u * u * p0[0] + 3 * u * u * t * p1[0] + 3 * u * t * t * p2[0] + t * t * t * p3[0];
|
|
46
|
-
const y = u * u * u * p0[1] + 3 * u * u * t * p1[1] + 3 * u * t * t * p2[1] + t * t * t * p3[1];
|
|
47
|
-
return [x, y];
|
|
48
|
-
}
|
|
49
|
-
function uniform(lo, hi) {
|
|
50
|
-
return lo + Math.random() * (hi - lo);
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Box-Muller. `Math.random()` is uniform and the jitter model wants a normal
|
|
54
|
-
* deviate; a uniform one has hard edges that show up as a boxy distribution of
|
|
55
|
-
* off-path error, which is itself a fingerprint.
|
|
56
|
-
*/
|
|
57
|
-
function gauss(mu, sigma) {
|
|
58
|
-
let u = 0;
|
|
59
|
-
while (u === 0)
|
|
60
|
-
u = Math.random(); // log(0) is -Infinity
|
|
61
|
-
const v = Math.random();
|
|
62
|
-
return mu + sigma * Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Two control points offset PERPENDICULAR to the travel direction, so the path
|
|
66
|
-
* bows to one side. The sign is random per gesture: always bowing the same way
|
|
67
|
-
* would itself be a fingerprint.
|
|
68
|
-
*/
|
|
69
|
-
function controlPoints(start, end, distance) {
|
|
70
|
-
const dx = end[0] - start[0];
|
|
71
|
-
const dy = end[1] - start[1];
|
|
72
|
-
if (distance === 0)
|
|
73
|
-
return [start, end];
|
|
74
|
-
// Unit normal to the direction of travel.
|
|
75
|
-
const nx = -dy / distance;
|
|
76
|
-
const ny = dx / distance;
|
|
77
|
-
// Bow proportional to distance, but capped — long drags don't arc absurdly.
|
|
78
|
-
const bow = Math.min(distance * uniform(0.06, 0.16), 140.0) * (Math.random() < 0.5 ? -1.0 : 1.0);
|
|
79
|
-
// Control points at roughly 1/3 and 2/3 along, each pushed off-axis.
|
|
80
|
-
const c1 = [
|
|
81
|
-
start[0] + dx * uniform(0.2, 0.4) + nx * bow,
|
|
82
|
-
start[1] + dy * uniform(0.2, 0.4) + ny * bow,
|
|
83
|
-
];
|
|
84
|
-
const c2 = [
|
|
85
|
-
start[0] + dx * uniform(0.6, 0.8) + nx * bow * uniform(0.4, 0.9),
|
|
86
|
-
start[1] + dy * uniform(0.6, 0.8) + ny * bow * uniform(0.4, 0.9),
|
|
87
|
-
];
|
|
88
|
-
return [c1, c2];
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Returns `[points, timings]`. `timings` is CUMULATIVE milliseconds from the
|
|
92
|
-
* gesture start — not per-step deltas — because that is what `tracePath` sleeps
|
|
93
|
-
* against, and both drivers must pace identically.
|
|
94
|
-
*
|
|
95
|
-
* `frequency` is samples per second (the driver passes 60).
|
|
96
|
-
* `frequencyRandomizer` jitters each frame's timestamp so the sample interval
|
|
97
|
-
* is not a metronome; a perfectly periodic mousemove stream is trivially
|
|
98
|
-
* detectable.
|
|
99
|
-
*/
|
|
100
|
-
function generate_trajectory(target_start, target_end, frequency = 60, frequencyRandomizer = 0.12) {
|
|
101
|
-
const start = [Number(target_start[0]), Number(target_start[1])];
|
|
102
|
-
const end = [Number(target_end[0]), Number(target_end[1])];
|
|
103
|
-
const dx = end[0] - start[0];
|
|
104
|
-
const dy = end[1] - start[1];
|
|
105
|
-
const distance = Math.hypot(dx, dy);
|
|
106
|
-
if (distance < 1e-6)
|
|
107
|
-
return [[end], [0.0]];
|
|
108
|
-
let durationMs = FITTS_A_MS + FITTS_B_MS * Math.log2(distance / WIDTH_PX + 1.0);
|
|
109
|
-
durationMs *= uniform(0.85, 1.2);
|
|
110
|
-
const steps = Math.max(2, Math.round((durationMs / 1000.0) * Math.max(1, frequency)));
|
|
111
|
-
// A short hop is a straight ease with jitter — bowing a 15px move looks worse
|
|
112
|
-
// than not bowing it.
|
|
113
|
-
let c1;
|
|
114
|
-
let c2;
|
|
115
|
-
if (distance < SHORT_MOVE_PX) {
|
|
116
|
-
c1 = start;
|
|
117
|
-
c2 = end;
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
[c1, c2] = controlPoints(start, end, distance);
|
|
121
|
-
}
|
|
122
|
-
// Overshoot: on a long move the hand arrives past the target and corrects.
|
|
123
|
-
// Modelled as a longer primary gesture to a point beyond the target, then a
|
|
124
|
-
// short settle back — the settle is appended as extra samples below.
|
|
125
|
-
const overshoot = distance >= OVERSHOOT_MIN_DISTANCE_PX && Math.random() < 0.55;
|
|
126
|
-
let aim = end;
|
|
127
|
-
if (overshoot) {
|
|
128
|
-
const over = uniform(0.01, 0.035) * distance;
|
|
129
|
-
aim = [end[0] + (dx / distance) * over, end[1] + (dy / distance) * over];
|
|
130
|
-
}
|
|
131
|
-
const points = [];
|
|
132
|
-
const timings = [];
|
|
133
|
-
let elapsed = 0.0;
|
|
134
|
-
const perStep = durationMs / steps;
|
|
135
|
-
for (let i = 0; i <= steps; i++) {
|
|
136
|
-
const t = easeInOut(i / steps);
|
|
137
|
-
let [px, py] = cubicBezier(start, c1, c2, aim, t);
|
|
138
|
-
// Jitter scales with instantaneous speed: the faster the cursor is moving,
|
|
139
|
-
// the less precisely a human tracks the intended path.
|
|
140
|
-
const speed = Math.abs(easeInOut(Math.min(1.0, (i + 1) / steps)) - easeInOut(i / steps));
|
|
141
|
-
const jitter = Math.min(1.6, 0.25 + speed * steps * 0.5);
|
|
142
|
-
px += gauss(0.0, jitter * 0.5);
|
|
143
|
-
py += gauss(0.0, jitter * 0.5);
|
|
144
|
-
points.push([px, py]);
|
|
145
|
-
timings.push(elapsed);
|
|
146
|
-
elapsed += perStep * uniform(1.0 - frequencyRandomizer, 1.0 + frequencyRandomizer);
|
|
147
|
-
}
|
|
148
|
-
if (overshoot) {
|
|
149
|
-
// Correction: a couple of small samples back onto the true target, at the
|
|
150
|
-
// slower pace of a deliberate fine adjustment.
|
|
151
|
-
const settleSteps = Math.floor(uniform(2, 5)); // 2..4 inclusive
|
|
152
|
-
const origin = points[points.length - 1];
|
|
153
|
-
for (let i = 1; i <= settleSteps; i++) {
|
|
154
|
-
const t = i / settleSteps;
|
|
155
|
-
points.push([
|
|
156
|
-
origin[0] + (end[0] - origin[0]) * t + gauss(0.0, 0.35),
|
|
157
|
-
origin[1] + (end[1] - origin[1]) * t + gauss(0.0, 0.35),
|
|
158
|
-
]);
|
|
159
|
-
timings.push(elapsed);
|
|
160
|
-
elapsed += uniform(14.0, 30.0);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Land exactly on the requested pixel. Everything above is texture; the final
|
|
164
|
-
// sample must be the point the caller asked for or clicks drift off-target.
|
|
165
|
-
points[points.length - 1] = end;
|
|
166
|
-
return [points, timings];
|
|
167
|
-
}
|
|
168
|
-
// ── Touch ───────────────────────────────────────────────────────────────────
|
|
169
|
-
//
|
|
170
|
-
// Fitts's law holds for direct touch too, but with a slower intercept and a much
|
|
171
|
-
// wider effective target: a fingertip contact patch is ~9mm across, which is the
|
|
172
|
-
// 44pt / 48dp minimum both platform guidelines are built around. So the same
|
|
173
|
-
// distance takes longer AND is aimed less precisely than with a mouse.
|
|
174
|
-
const TOUCH_FITTS_A_MS = 160.0;
|
|
175
|
-
const TOUCH_FITTS_B_MS = 135.0;
|
|
176
|
-
const TOUCH_WIDTH_PX = 44.0;
|
|
177
|
-
// How flick-like the launch is. 0 would be the mouse's symmetric bell; 1 would
|
|
178
|
-
// be an instantaneous jump at t=0. See `easeTouch`.
|
|
179
|
-
const TOUCH_LAUNCH = 0.7;
|
|
180
|
-
// A finger drags across glass on a short wrist/thumb pivot, so it bows far less
|
|
181
|
-
// than a hand moving a mouse across a desk.
|
|
182
|
-
const TOUCH_BOW_CAP_PX = 40.0;
|
|
183
|
-
// The reported contact point wanders, because it is the CENTROID of a soft patch
|
|
184
|
-
// rolling under pressure rather than a rigid sensor. `TOUCH_WOBBLE_DECAY` is the
|
|
185
|
-
// AR(1) coefficient that makes that wander low-frequency: white per-sample noise
|
|
186
|
-
// would show up in a spectrum as nothing a finger produces.
|
|
187
|
-
const TOUCH_WOBBLE_PX = 0.55;
|
|
188
|
-
const TOUCH_WOBBLE_DECAY = 0.82;
|
|
189
|
-
/**
|
|
190
|
-
* Asymmetric ease: a finger leaves fast and brakes late.
|
|
191
|
-
*
|
|
192
|
-
* A mouse hand is symmetric (`easeInOut` — accelerate, decelerate, equally). A
|
|
193
|
-
* finger is not: the launch is a flick off the contact point and the arrival is
|
|
194
|
-
* a brake. Blending the two rather than using a pure ease-out is what keeps the
|
|
195
|
-
* launch from being an instantaneous jump at t=0, which no digitizer would ever
|
|
196
|
-
* report.
|
|
197
|
-
*/
|
|
198
|
-
function easeTouch(t) {
|
|
199
|
-
return (1.0 - TOUCH_LAUNCH) * easeInOut(t) + TOUCH_LAUNCH * (1.0 - Math.pow(1.0 - t, 2.4));
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* A FINGER travelling from `target_start` to `target_end`, same contract as
|
|
203
|
-
* `generate_trajectory`: `[points, timings]`, timings cumulative from 0, last
|
|
204
|
-
* point exactly `target_end`.
|
|
205
|
-
*
|
|
206
|
-
* A separate model rather than `generate_trajectory` with other constants,
|
|
207
|
-
* because the three things that make the mouse output human are each WRONG
|
|
208
|
-
* here:
|
|
209
|
-
*
|
|
210
|
-
* - **No overshoot-and-correct.** That tell is a hand arriving past a target
|
|
211
|
-
* it cannot see under the cursor. A finger occludes its own target and
|
|
212
|
-
* commits; the correction, when there is one, is a second gesture.
|
|
213
|
-
* - **A different velocity profile.** `easeTouch`, not `easeInOut` — above.
|
|
214
|
-
* - **Different jitter.** The mouse model adds white noise scaled by speed (a
|
|
215
|
-
* hand tracking a path imprecisely). A digitizer instead reports a wandering
|
|
216
|
-
* centroid, which is low-frequency and roughly speed INDEPENDENT, so this is
|
|
217
|
-
* a smoothed random walk.
|
|
218
|
-
*
|
|
219
|
-
* `frequency` defaults to 90: digitizers sample at 120Hz+, but touchmove is
|
|
220
|
-
* coalesced to the compositor, so more samples than this buys nothing and costs
|
|
221
|
-
* a dispatch round-trip each.
|
|
222
|
-
*/
|
|
223
|
-
function generate_swipe(target_start, target_end, frequency = 90, frequencyRandomizer = 0.1) {
|
|
224
|
-
const start = [Number(target_start[0]), Number(target_start[1])];
|
|
225
|
-
const end = [Number(target_end[0]), Number(target_end[1])];
|
|
226
|
-
const dx = end[0] - start[0];
|
|
227
|
-
const dy = end[1] - start[1];
|
|
228
|
-
const distance = Math.hypot(dx, dy);
|
|
229
|
-
if (distance < 1e-6)
|
|
230
|
-
return [[end], [0.0]];
|
|
231
|
-
let durationMs = TOUCH_FITTS_A_MS + TOUCH_FITTS_B_MS * Math.log2(distance / TOUCH_WIDTH_PX + 1.0);
|
|
232
|
-
durationMs *= uniform(0.85, 1.2);
|
|
233
|
-
const steps = Math.max(2, Math.round((durationMs / 1000.0) * Math.max(1, frequency)));
|
|
234
|
-
let c1;
|
|
235
|
-
let c2;
|
|
236
|
-
if (distance < SHORT_MOVE_PX) {
|
|
237
|
-
c1 = start;
|
|
238
|
-
c2 = end;
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
[c1, c2] = controlPoints(start, end, distance);
|
|
242
|
-
// Pull the mouse model's bow in: `controlPoints` is shared, and its arc is
|
|
243
|
-
// sized for a hand crossing a desk.
|
|
244
|
-
const shrink = Math.min(1.0, TOUCH_BOW_CAP_PX / Math.max(TOUCH_BOW_CAP_PX, distance * 0.16));
|
|
245
|
-
c1 = [start[0] + (c1[0] - start[0]) * shrink, start[1] + (c1[1] - start[1]) * shrink];
|
|
246
|
-
c2 = [start[0] + (c2[0] - start[0]) * shrink, start[1] + (c2[1] - start[1]) * shrink];
|
|
247
|
-
}
|
|
248
|
-
const points = [];
|
|
249
|
-
const timings = [];
|
|
250
|
-
let elapsed = 0.0;
|
|
251
|
-
const perStep = durationMs / steps;
|
|
252
|
-
let wobX = 0.0;
|
|
253
|
-
let wobY = 0.0;
|
|
254
|
-
for (let i = 0; i <= steps; i++) {
|
|
255
|
-
const t = easeTouch(i / steps);
|
|
256
|
-
const [px, py] = cubicBezier(start, c1, c2, end, t);
|
|
257
|
-
wobX = wobX * TOUCH_WOBBLE_DECAY + gauss(0.0, TOUCH_WOBBLE_PX);
|
|
258
|
-
wobY = wobY * TOUCH_WOBBLE_DECAY + gauss(0.0, TOUCH_WOBBLE_PX);
|
|
259
|
-
points.push([px + wobX, py + wobY]);
|
|
260
|
-
timings.push(elapsed);
|
|
261
|
-
elapsed += perStep * uniform(1.0 - frequencyRandomizer, 1.0 + frequencyRandomizer);
|
|
262
|
-
}
|
|
263
|
-
// Land exactly where the caller asked, same reason as the mouse model.
|
|
264
|
-
points[points.length - 1] = end;
|
|
265
|
-
return [points, timings];
|
|
266
|
-
}
|