captchakraken 2.8.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/AGENTS.md +112 -0
  2. package/NOTICE +36 -0
  3. package/dist/cli-invocation.d.ts +9 -40
  4. package/dist/cli-invocation.js +16 -49
  5. package/dist/errors.d.ts +7 -27
  6. package/dist/errors.js +6 -25
  7. package/dist/humanize.d.ts +56 -240
  8. package/dist/humanize.js +159 -415
  9. package/dist/index.d.ts +5 -129
  10. package/dist/index.js +4 -98
  11. package/dist/kinds.d.ts +135 -0
  12. package/dist/kinds.js +70 -0
  13. package/dist/limits.d.ts +0 -48
  14. package/dist/limits.js +2 -48
  15. package/dist/model-name.d.ts +4 -19
  16. package/dist/model-name.js +32 -40
  17. package/dist/playwright-types.d.ts +25 -95
  18. package/dist/playwright-types.js +0 -23
  19. package/dist/puppeteer-adapter.d.ts +4 -43
  20. package/dist/puppeteer-adapter.js +22 -58
  21. package/dist/python-command.d.ts +0 -41
  22. package/dist/python-command.js +0 -37
  23. package/dist/selectors.d.ts +54 -0
  24. package/dist/selectors.js +117 -0
  25. package/dist/slide-geometry.d.ts +2 -29
  26. package/dist/slide-geometry.js +18 -44
  27. package/dist/solver.d.ts +112 -539
  28. package/dist/solver.js +1088 -2950
  29. package/dist/timing.d.ts +7 -40
  30. package/dist/timing.js +4 -36
  31. package/dist/token-usage.d.ts +1 -7
  32. package/dist/token-usage.js +10 -95
  33. package/dist/types.d.ts +77 -483
  34. package/dist/watcher.d.ts +5 -78
  35. package/dist/watcher.js +0 -56
  36. package/package.json +12 -4
  37. package/python/AGENTS.md +122 -0
  38. package/python/NOTICE +36 -0
  39. package/python/README.md +1 -2
  40. package/python/examples/README.md +10 -0
  41. package/python/examples/_harness.py +4 -73
  42. package/python/examples/demoHcaptcha.py +0 -13
  43. package/python/examples/demoRecaptcha.py +0 -10
  44. package/python/examples/watch_playwright.py +1 -26
  45. package/python/examples/with_local_gguf.py +28 -0
  46. package/python/examples/with_playwright.py +0 -19
  47. package/python/pyproject.toml +68 -19
  48. package/python/src/captchakraken/__init__.py +37 -45
  49. package/python/src/captchakraken/action_types.py +14 -36
  50. package/python/src/captchakraken/cli.py +262 -879
  51. package/python/src/captchakraken/config.py +60 -146
  52. package/python/src/captchakraken/errors.py +17 -95
  53. package/python/src/captchakraken/humanize.py +174 -597
  54. package/python/src/captchakraken/image_processor.py +16 -235
  55. package/python/src/captchakraken/keyframes.py +69 -434
  56. package/python/src/captchakraken/kinds.py +152 -0
  57. package/python/src/captchakraken/models.json +79 -21
  58. package/python/src/captchakraken/overlay.py +65 -498
  59. package/python/src/captchakraken/page_solver.py +1123 -2564
  60. package/python/src/captchakraken/planner.py +70 -428
  61. package/python/src/captchakraken/prompts.py +45 -261
  62. package/python/src/captchakraken/selectors.py +184 -0
  63. package/python/src/captchakraken/server_manager.py +7 -58
  64. package/python/src/captchakraken/solver.py +128 -522
  65. package/python/src/captchakraken/timing.py +20 -74
  66. package/python/src/captchakraken/tool_calls/board_painted.py +47 -0
  67. package/python/src/captchakraken/tool_calls/find_checkbox.py +20 -30
  68. package/python/src/captchakraken/tool_calls/find_grid.py +186 -1339
  69. package/python/src/captchakraken/tool_calls/track_piece.py +81 -57
  70. package/python/src/captchakraken/updater.py +7 -76
  71. package/python/src/captchakraken/watcher.py +12 -101
  72. package/scripts/copy-python.mjs +10 -10
  73. package/scripts/setup-python.js +1 -21
  74. package/dist/trajectory.d.ts +0 -58
  75. package/dist/trajectory.js +0 -266
  76. package/python/src/captchakraken/tool_calls/move_indicator.py +0 -431
  77. package/python/src/captchakraken/trajectory.py +0 -284
@@ -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 geetest_v4_slide 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
- }