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
package/dist/timing.d.ts CHANGED
@@ -1,48 +1,15 @@
1
- /**
2
- * Where one solve's wall-clock went, by phase.
3
- *
4
- * The TypeScript half of `python/src/captchakraken/timing.py`; the two are one
5
- * implementation in two languages and `test_timing_parity.py` pins that they
6
- * name the same phases. That parity is the point: without it the ports can only
7
- * be compared on their totals, and "the JS port is four seconds slower" is not
8
- * a bug report — "the JS port spends four seconds starting a Python
9
- * interpreter it did not need to start" is.
10
- *
11
- * Always on. It is a few map updates per phase against multi-second waits, and
12
- * a budget you have to opt into is one nobody has when the slow solve happens.
13
- * Only PRINTED under `CAPTCHA_TIMINGS=1`; always RETURNED on `SolveResult`.
14
- */
15
- /**
16
- * What a phase's time COUNTS AS, when asking how much of a solve was useful.
17
- *
18
- * Only two kinds of second are worth spending: one the model is thinking in,
19
- * and one the pointer is travelling in (which has to look human, so it cannot
20
- * be rushed). Everything else is the driver waiting on a clock, and every such
21
- * wait is a candidate for deletion or for overlapping with something real.
22
- */
23
- export declare const PRODUCTIVE: Set<string>;
1
+ import { Phase } from './kinds.js';
2
+ export declare const PRODUCTIVE: ReadonlySet<Phase>;
24
3
  export declare function timingsEnabled(): boolean;
4
+ /** Phases attribute, they do not partition: a nested phase counts under both names, and re-entering an open one counts once. */
25
5
  export declare class PhaseBudget {
26
- readonly totals: Map<string, number>;
27
- readonly counts: Map<string, number>;
6
+ readonly totals: Map<Phase, number>;
7
+ readonly counts: Map<Phase, number>;
28
8
  private readonly open;
29
9
  private readonly t0;
30
- /**
31
- * Attribute `fn`'s wall-clock to `name`.
32
- *
33
- * Phases may nest (a burst contains its screenshots). Only the OUTERMOST of a
34
- * given NAME accumulates, so re-entering one cannot double-count it — but a
35
- * phase nested inside a differently-named one counts under both, deliberately:
36
- * the cursor drifting over the widget WHILE the model generates is genuinely
37
- * both `mouse` and `inference`, and hiding either would misreport what the
38
- * solve was doing. The totals are therefore an attribution, not a partition,
39
- * and can exceed the elapsed time.
40
- */
41
- phase<T>(name: string, fn: () => Promise<T>): Promise<T>;
42
- /** Record a measured span directly, for blocks that would need re-indenting. */
43
- add(name: string, ms: number): void;
10
+ phase<T>(name: Phase, fn: () => Promise<T>): Promise<T>;
11
+ add(name: Phase, ms: number): void;
44
12
  elapsedMs(): number;
45
- /** Plain object for `SolveResult.phases`, with the solve total folded in. */
46
13
  toObject(): Record<string, number>;
47
14
  report(): string;
48
15
  }
package/dist/timing.js CHANGED
@@ -1,33 +1,14 @@
1
1
  "use strict";
2
- /**
3
- * Where one solve's wall-clock went, by phase.
4
- *
5
- * The TypeScript half of `python/src/captchakraken/timing.py`; the two are one
6
- * implementation in two languages and `test_timing_parity.py` pins that they
7
- * name the same phases. That parity is the point: without it the ports can only
8
- * be compared on their totals, and "the JS port is four seconds slower" is not
9
- * a bug report — "the JS port spends four seconds starting a Python
10
- * interpreter it did not need to start" is.
11
- *
12
- * Always on. It is a few map updates per phase against multi-second waits, and
13
- * a budget you have to opt into is one nobody has when the slow solve happens.
14
- * Only PRINTED under `CAPTCHA_TIMINGS=1`; always RETURNED on `SolveResult`.
15
- */
16
2
  Object.defineProperty(exports, "__esModule", { value: true });
17
3
  exports.PhaseBudget = exports.PRODUCTIVE = void 0;
18
4
  exports.timingsEnabled = timingsEnabled;
19
- /**
20
- * What a phase's time COUNTS AS, when asking how much of a solve was useful.
21
- *
22
- * Only two kinds of second are worth spending: one the model is thinking in,
23
- * and one the pointer is travelling in (which has to look human, so it cannot
24
- * be rushed). Everything else is the driver waiting on a clock, and every such
25
- * wait is a candidate for deletion or for overlapping with something real.
26
- */
27
- exports.PRODUCTIVE = new Set(['inference', 'mouse']);
5
+ const kinds_js_1 = require("./kinds.js");
6
+ // Always collected, printed only under CAPTCHA_TIMINGS=1: a budget you have to opt into is one nobody has when the slow solve happens.
7
+ exports.PRODUCTIVE = new Set([kinds_js_1.Phase.INFERENCE, kinds_js_1.Phase.MOUSE]);
28
8
  function timingsEnabled() {
29
9
  return process.env.CAPTCHA_TIMINGS === '1';
30
10
  }
11
+ /** Phases attribute, they do not partition: a nested phase counts under both names, and re-entering an open one counts once. */
31
12
  class PhaseBudget {
32
13
  constructor() {
33
14
  this.totals = new Map();
@@ -35,17 +16,6 @@ class PhaseBudget {
35
16
  this.open = [];
36
17
  this.t0 = Date.now();
37
18
  }
38
- /**
39
- * Attribute `fn`'s wall-clock to `name`.
40
- *
41
- * Phases may nest (a burst contains its screenshots). Only the OUTERMOST of a
42
- * given NAME accumulates, so re-entering one cannot double-count it — but a
43
- * phase nested inside a differently-named one counts under both, deliberately:
44
- * the cursor drifting over the widget WHILE the model generates is genuinely
45
- * both `mouse` and `inference`, and hiding either would misreport what the
46
- * solve was doing. The totals are therefore an attribution, not a partition,
47
- * and can exceed the elapsed time.
48
- */
49
19
  async phase(name, fn) {
50
20
  if (this.open.includes(name))
51
21
  return fn();
@@ -59,7 +29,6 @@ class PhaseBudget {
59
29
  this.add(name, Date.now() - t0);
60
30
  }
61
31
  }
62
- /** Record a measured span directly, for blocks that would need re-indenting. */
63
32
  add(name, ms) {
64
33
  this.totals.set(name, (this.totals.get(name) ?? 0) + ms);
65
34
  this.counts.set(name, (this.counts.get(name) ?? 0) + 1);
@@ -67,7 +36,6 @@ class PhaseBudget {
67
36
  elapsedMs() {
68
37
  return Date.now() - this.t0;
69
38
  }
70
- /** Plain object for `SolveResult.phases`, with the solve total folded in. */
71
39
  toObject() {
72
40
  const out = {};
73
41
  for (const [k, v] of this.totals)
@@ -1,11 +1,5 @@
1
1
  import { TokenUsage } from './types';
2
- export interface ModelPricing {
3
- inputPricePerM: number;
4
- outputPricePerM: number;
5
- cachedInputPricePerM: number;
6
- }
7
- export declare const PRICING: Record<string, ModelPricing>;
8
- export declare function estimateCost(usage: TokenUsage): number;
2
+ /** One summed row for a solve. The client has no price list, so `estimatedCost` is always 0. */
9
3
  export declare function aggregateTokenUsage(usages: TokenUsage[]): {
10
4
  modelName: string;
11
5
  inputTokens: number;
@@ -1,102 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PRICING = void 0;
4
- exports.estimateCost = estimateCost;
5
3
  exports.aggregateTokenUsage = aggregateTokenUsage;
6
- exports.PRICING = {
7
- 'gemini-3-pro-preview': {
8
- inputPricePerM: 2.00,
9
- outputPricePerM: 12.00,
10
- cachedInputPricePerM: 0.20,
11
- },
12
- 'gemini-3-flash-preview': {
13
- inputPricePerM: 0.50,
14
- outputPricePerM: 3.00,
15
- cachedInputPricePerM: 0.05,
16
- },
17
- 'gemini-2.5-pro': {
18
- inputPricePerM: 1.25,
19
- outputPricePerM: 10.00,
20
- cachedInputPricePerM: 0.125,
21
- },
22
- 'gemini-2.5-flash': {
23
- inputPricePerM: 0.30,
24
- outputPricePerM: 2.50,
25
- cachedInputPricePerM: 0.03,
26
- },
27
- 'gemini-2.5-flash-preview': {
28
- inputPricePerM: 0.30,
29
- outputPricePerM: 2.50,
30
- cachedInputPricePerM: 0.03,
31
- },
32
- 'gemini-2.5-flash-lite': {
33
- inputPricePerM: 0.10,
34
- outputPricePerM: 0.40,
35
- cachedInputPricePerM: 0.01,
36
- },
37
- 'gemini-2.5-flash-lite-preview': {
38
- inputPricePerM: 0.10,
39
- outputPricePerM: 0.40,
40
- cachedInputPricePerM: 0.01,
41
- },
42
- 'gemini-2.0-flash': {
43
- inputPricePerM: 0.10,
44
- outputPricePerM: 0.40,
45
- cachedInputPricePerM: 0.025,
46
- },
47
- 'gemini-2.5-computer-use-preview-10-2025': {
48
- inputPricePerM: 1.25,
49
- outputPricePerM: 10.00,
50
- cachedInputPricePerM: 0.025,
51
- }
52
- };
53
- // The CaptchaKraken CLI (vLLM/OpenAI shape) emits `prompt_tokens` /
54
- // `completion_tokens`, while older Gemini-path usages used `input_tokens` /
55
- // `output_tokens`. Normalize both so aggregation never produces NaN.
56
- function inTok(u) {
57
- return (u.input_tokens ?? u.prompt_tokens ?? 0);
58
- }
59
- function outTok(u) {
60
- return (u.output_tokens ?? u.completion_tokens ?? 0);
61
- }
62
- function cachedTok(u) {
63
- return (u.cached_input_tokens ?? u.prompt_tokens_details?.cached_tokens ?? 0);
64
- }
65
- function estimateCost(usage) {
66
- const model = usage.model;
67
- // Default to gemini-2.5-flash pricing if unknown
68
- const pricing = exports.PRICING[model] || exports.PRICING['gemini-2.5-flash'];
69
- const inputCost = (inTok(usage) / 1000000) * pricing.inputPricePerM;
70
- const outputCost = (outTok(usage) / 1000000) * pricing.outputPricePerM;
71
- const cachedCost = (cachedTok(usage) / 1000000) * pricing.cachedInputPricePerM;
72
- return inputCost + outputCost + cachedCost;
73
- }
4
+ // The CLI emits the vLLM/OpenAI shape (`prompt_tokens`); older rows used `input_tokens`.
5
+ const inTok = (u) => (u.input_tokens ?? u.prompt_tokens ?? 0);
6
+ const outTok = (u) => (u.output_tokens ?? u.completion_tokens ?? 0);
7
+ const cachedTok = (u) => (u.cached_input_tokens ?? u.prompt_tokens_details?.cached_tokens ?? 0);
8
+ /** One summed row for a solve. The client has no price list, so `estimatedCost` is always 0. */
74
9
  function aggregateTokenUsage(usages) {
75
- if (usages.length === 0) {
76
- return {
77
- modelName: 'none',
78
- inputTokens: 0,
79
- outputTokens: 0,
80
- cachedInputTokens: 0,
81
- estimatedCost: 0,
82
- };
83
- }
84
- const modelName = usages[0].model;
85
- let inputTokens = 0;
86
- let outputTokens = 0;
87
- let cachedInputTokens = 0;
88
- let estimatedCost = 0;
89
- for (const usage of usages) {
90
- inputTokens += inTok(usage);
91
- outputTokens += outTok(usage);
92
- cachedInputTokens += cachedTok(usage);
93
- estimatedCost += estimateCost(usage);
94
- }
95
10
  return {
96
- modelName,
97
- inputTokens,
98
- outputTokens,
99
- cachedInputTokens,
100
- estimatedCost,
11
+ modelName: usages[0]?.model ?? 'none',
12
+ inputTokens: usages.reduce((n, u) => n + inTok(u), 0),
13
+ outputTokens: usages.reduce((n, u) => n + outTok(u), 0),
14
+ cachedInputTokens: usages.reduce((n, u) => n + cachedTok(u), 0),
15
+ estimatedCost: 0,
101
16
  };
102
17
  }