@weavetab/mcp 2.5.0 → 2.5.1

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 (168) hide show
  1. package/AGENTS.md +49 -49
  2. package/ARCHITECTURE.md +590 -590
  3. package/LICENSE +216 -216
  4. package/README.md +269 -269
  5. package/SECURITY.md +207 -207
  6. package/TROUBLESHOOTING.md +373 -365
  7. package/dist/anticaptcha/detector.js +395 -395
  8. package/dist/anticaptcha/solver.js +130 -130
  9. package/dist/cdp/bridge.js +73 -73
  10. package/dist/cdp/connector.js +46 -22
  11. package/dist/cdp/extension.js +34 -34
  12. package/dist/cdp/helpers.js +174 -174
  13. package/dist/cdp/pruner.js +195 -195
  14. package/dist/cdp/ref_cache.js +66 -66
  15. package/dist/cdp/security.js +10 -10
  16. package/dist/cdp/verify.js +57 -57
  17. package/dist/cdp/walker.js +563 -525
  18. package/dist/cli/plugin.js +26 -3
  19. package/dist/config/loader.d.ts +0 -3
  20. package/dist/config/loader.js +0 -9
  21. package/dist/index.js +0 -4
  22. package/dist/intelligence/intent.js +81 -81
  23. package/dist/intelligence/sessionMemory.js +2 -2
  24. package/dist/overlay/ghost.js +22 -22
  25. package/dist/overlay/injector.js +89 -89
  26. package/dist/plugin/extension_bridge.js +54 -54
  27. package/dist/plugin/interface.d.ts +7 -0
  28. package/dist/plugin/loader.js +3 -4
  29. package/dist/plugin/schema.d.ts +11 -0
  30. package/dist/plugin/schema.js +59 -0
  31. package/dist/security/guard.js +18 -18
  32. package/dist/sensors/actionability.js +167 -167
  33. package/dist/sensors/guard.js +29 -29
  34. package/dist/sensors/network.js +15 -17
  35. package/dist/server.d.ts +1 -1
  36. package/dist/server.js +173 -144
  37. package/dist/state/delta.js +6 -7
  38. package/dist/tools/automation.js +85 -85
  39. package/dist/tools/canvas.js +48 -25
  40. package/dist/tools/checkpoint.js +94 -35
  41. package/dist/tools/click.js +52 -41
  42. package/dist/tools/clipboard.js +80 -74
  43. package/dist/tools/design_extract.js +441 -441
  44. package/dist/tools/dialog.js +13 -13
  45. package/dist/tools/emulation.d.ts +2 -0
  46. package/dist/tools/emulation.js +7 -0
  47. package/dist/tools/fill.js +344 -347
  48. package/dist/tools/find.js +78 -52
  49. package/dist/tools/highlight.js +67 -67
  50. package/dist/tools/inspect.js +54 -25
  51. package/dist/tools/key.js +42 -35
  52. package/dist/tools/navigate.d.ts +1 -1
  53. package/dist/tools/navigate.js +35 -35
  54. package/dist/tools/office/parse.js +62 -18
  55. package/dist/tools/pdf.d.ts +9 -0
  56. package/dist/tools/pdf.js +7 -4
  57. package/dist/tools/performance.d.ts +11 -4
  58. package/dist/tools/performance.js +94 -42
  59. package/dist/tools/performance_test.d.ts +2 -2
  60. package/dist/tools/performance_test.js +69 -69
  61. package/dist/tools/pointer.js +116 -116
  62. package/dist/tools/read.js +99 -67
  63. package/dist/tools/recording.js +9 -0
  64. package/dist/tools/scrape.js +146 -146
  65. package/dist/tools/screenshot.d.ts +2 -0
  66. package/dist/tools/screenshot.js +46 -4
  67. package/dist/tools/scroll.d.ts +3 -1
  68. package/dist/tools/scroll.js +13 -15
  69. package/dist/tools/select.d.ts +3 -0
  70. package/dist/tools/select.js +131 -124
  71. package/dist/tools/snapshot.js +8 -6
  72. package/dist/tools/storage.d.ts +5 -1
  73. package/dist/tools/storage.js +16 -0
  74. package/dist/tools/tabs.d.ts +7 -5
  75. package/dist/tools/tabs.js +79 -26
  76. package/dist/tools/thought.js +8 -8
  77. package/dist/tools/type.js +274 -274
  78. package/dist/tools/type_secret.js +23 -23
  79. package/dist/tools/upload.js +3 -3
  80. package/dist/tools/video_compiler.js +9 -0
  81. package/dist/tools/wait.js +35 -35
  82. package/dist/version.d.ts +1 -1
  83. package/dist/version.js +1 -1
  84. package/docs/DOCUMENTATION.md +583 -585
  85. package/docs/PLUGINS.md +139 -121
  86. package/docs/TOOLS.md +3 -4
  87. package/docs/architecture/blind-injection.md +83 -83
  88. package/docs/architecture/overview.md +49 -49
  89. package/docs/architecture/security.md +36 -36
  90. package/docs/architecture/transport.md +37 -37
  91. package/docs/cli/cli-commands.md +50 -50
  92. package/docs/cli/cli-flags.md +28 -28
  93. package/docs/cli/cli-scripting.md +35 -35
  94. package/docs/configuration/config-locations.md +23 -23
  95. package/docs/configuration/configuration.md +39 -27
  96. package/docs/configuration/env-variables.md +19 -19
  97. package/docs/configuration/folders.md +62 -55
  98. package/docs/desktop-automation/electron-apps.md +225 -214
  99. package/docs/get-started/installation.md +1 -1
  100. package/docs/get-started/introduction.md +60 -60
  101. package/docs/get-started/quickstart.md +2 -2
  102. package/docs/get-started/setup-wizard.md +27 -27
  103. package/docs/intelligence/site-profiles.md +151 -151
  104. package/docs/tools/tools.md +582 -631
  105. package/docs/troubleshooting/trouble-browser.md +27 -27
  106. package/docs/troubleshooting/trouble-loop.md +31 -31
  107. package/ext-dist/background.js +415 -415
  108. package/ext-dist/content.js +2527 -2527
  109. package/ext-dist/dashboard.html +583 -583
  110. package/ext-dist/hud/hud.js +3 -3
  111. package/ext-dist/hud/index.html +20 -20
  112. package/ext-dist/manifest.json +73 -73
  113. package/ext-dist/styles.css +223 -223
  114. package/mcp.json +796 -805
  115. package/package.json +118 -118
  116. package/server.json +23 -23
  117. package/dist/cdp/walker.test.d.ts +0 -2
  118. package/dist/cdp/walker.test.js +0 -172
  119. package/dist/intelligence/sessionMemory.test.d.ts +0 -2
  120. package/dist/intelligence/sessionMemory.test.js +0 -62
  121. package/dist/intelligence/trail.test.d.ts +0 -2
  122. package/dist/intelligence/trail.test.js +0 -110
  123. package/dist/state/cache.test.d.ts +0 -2
  124. package/dist/state/cache.test.js +0 -76
  125. package/dist/state/delta.test.d.ts +0 -2
  126. package/dist/state/delta.test.js +0 -279
  127. package/dist/state/groups.d.ts +0 -15
  128. package/dist/state/groups.js +0 -80
  129. package/dist/tools/accessibility.d.ts +0 -14
  130. package/dist/tools/accessibility.js +0 -32
  131. package/dist/tools/ask.d.ts +0 -14
  132. package/dist/tools/ask.js +0 -49
  133. package/dist/tools/click_and_wait.d.ts +0 -33
  134. package/dist/tools/click_and_wait.js +0 -149
  135. package/dist/tools/drag.d.ts +0 -15
  136. package/dist/tools/drag.js +0 -29
  137. package/dist/tools/geo.d.ts +0 -19
  138. package/dist/tools/geo.js +0 -29
  139. package/dist/tools/github/analyze.d.ts +0 -4
  140. package/dist/tools/github/analyze.js +0 -113
  141. package/dist/tools/github/cache.d.ts +0 -21
  142. package/dist/tools/github/cache.js +0 -28
  143. package/dist/tools/github/index.d.ts +0 -19
  144. package/dist/tools/github/index.js +0 -40
  145. package/dist/tools/github/issues.d.ts +0 -9
  146. package/dist/tools/github/issues.js +0 -88
  147. package/dist/tools/github/pr.d.ts +0 -5
  148. package/dist/tools/github/pr.js +0 -88
  149. package/dist/tools/github/rateLimiter.d.ts +0 -4
  150. package/dist/tools/github/rateLimiter.js +0 -46
  151. package/dist/tools/github/read.d.ts +0 -34
  152. package/dist/tools/github/read.js +0 -74
  153. package/dist/tools/github/repoContext.d.ts +0 -20
  154. package/dist/tools/github/repoContext.js +0 -129
  155. package/dist/tools/hover.d.ts +0 -19
  156. package/dist/tools/hover.js +0 -57
  157. package/dist/tools/integration.test.d.ts +0 -2
  158. package/dist/tools/integration.test.js +0 -96
  159. package/dist/tools/macro_compiler.d.ts +0 -7
  160. package/dist/tools/macro_compiler.js +0 -82
  161. package/dist/tools/mouse.d.ts +0 -21
  162. package/dist/tools/mouse.js +0 -62
  163. package/dist/tools/response.d.ts +0 -129
  164. package/dist/tools/response.js +0 -5
  165. package/dist/tools/wait_for.d.ts +0 -23
  166. package/dist/tools/wait_for.js +0 -30
  167. package/dist/tools/windows.d.ts +0 -22
  168. package/dist/tools/windows.js +0 -57
@@ -1,382 +1,382 @@
1
1
  import { logDebug } from "../audit/logger.js";
2
- const DETECTION_SCRIPT = `
3
- (function() {
4
- 'use strict';
5
- const results = [];
6
-
7
- // ─── Helpers ────────────────────────────────────────────────────────────────
8
-
9
- function extractSiteKey(url) {
10
- const m = url.match(/[?&]k=([^&]+)/) ||
11
- url.match(/[?&]sitekey=([^&]+)/) ||
12
- url.match(/[?&]site_key=([^&]+)/);
13
- return m ? decodeURIComponent(m[1]) : null;
14
- }
15
-
16
- function isVisible(el) {
17
- if (!el) return false;
18
- const r = el.getBoundingClientRect();
19
- if (r.width === 0 && r.height === 0) return false;
20
- const st = window.getComputedStyle(el);
21
- return st.display !== 'none' && st.visibility !== 'hidden' && st.opacity !== '0';
22
- }
23
-
24
- function hasCaptchaKeyword(text) {
25
- return /captcha|challenge|verify|robot|human|puzzle|slider|drag/i.test(text);
26
- }
27
-
28
- function scoreResult(r) {
29
- // Higher = more confidence
30
- const typeScore = {
31
- recaptcha_v2: 0.95, recaptcha_v3: 0.9, hcaptcha: 0.95,
32
- turnstile: 0.9, geetest: 0.95, geetest_v4: 0.92,
33
- funcaptcha: 0.88, slider: 0.75, press_and_hold: 0.8,
34
- datadome: 0.9, perimeter_x: 0.88, custom: 0.6, unknown: 0.3,
35
- };
36
- return (typeScore[r.type] || 0.5) * r.confidence;
37
- }
38
-
39
- // ─── 1. reCAPTCHA v2 ────────────────────────────────────────────────────────
40
- document.querySelectorAll('iframe[src*="recaptcha"]').forEach(function(frame) {
41
- const src = frame.src || '';
42
- const isV2 = src.includes('/api2/') || src.includes('/enterprise/') || src.includes('recaptcha/api2');
43
- if (isV2) {
44
- const hasChallenge = src.includes('bframe') || src.includes('frame=bframe');
45
- results.push({
46
- type: 'recaptcha_v2',
47
- interactionType: hasChallenge ? 'image_select' : 'checkbox',
48
- difficulty: hasChallenge ? 'hard' : 'medium',
49
- selector: 'iframe[src*="recaptcha"]',
50
- iframe: src,
51
- confidence: 0.95,
52
- metadata: {
53
- siteKey: extractSiteKey(src),
54
- hasAudio: false,
55
- hasChallenge: hasChallenge,
56
- requiresUserInteraction: true,
57
- }
58
- });
59
- }
60
- });
61
-
62
- // ─── 2. reCAPTCHA v3 (invisible) ────────────────────────────────────────────
63
- var rcScripts = Array.from(document.scripts).filter(function(s) {
64
- return s.src && s.src.includes('recaptcha/api.js') && !s.src.includes('enterprise');
65
- });
66
- if (rcScripts.length > 0 && !results.some(function(r) { return r.type === 'recaptcha_v2'; })) {
67
- results.push({
68
- type: 'recaptcha_v3',
69
- interactionType: 'none',
70
- difficulty: 'easy',
71
- selector: 'script[src*="recaptcha/api.js"]',
72
- confidence: 0.85,
73
- metadata: {
74
- siteKey: extractSiteKey(rcScripts[0].src),
75
- requiresUserInteraction: false,
76
- }
77
- });
78
- }
79
-
80
- // ─── 3. hCaptcha ────────────────────────────────────────────────────────────
81
- document.querySelectorAll('iframe[src*="hcaptcha"]').forEach(function(frame) {
82
- const src = frame.src || '';
83
- const isChallenge = src.includes('/challenge');
84
- results.push({
85
- type: 'hcaptcha',
86
- interactionType: isChallenge ? 'image_select' : 'checkbox',
87
- difficulty: isChallenge ? 'hard' : 'medium',
88
- selector: 'iframe[src*="hcaptcha"]',
89
- iframe: src,
90
- confidence: 0.95,
91
- metadata: {
92
- siteKey: extractSiteKey(src),
93
- requiresUserInteraction: true,
94
- }
95
- });
96
- });
97
- // hCaptcha widget div
98
- if (!results.some(function(r) { return r.type === 'hcaptcha'; })) {
99
- var hDiv = document.querySelector('[data-hcaptcha-widget-id], h-captcha, [data-sitekey][class*="h-captcha"]');
100
- if (hDiv && isVisible(hDiv)) {
101
- results.push({
102
- type: 'hcaptcha',
103
- interactionType: 'checkbox',
104
- difficulty: 'medium',
105
- selector: '[data-hcaptcha-widget-id]',
106
- confidence: 0.88,
107
- metadata: {
108
- siteKey: hDiv.getAttribute('data-sitekey'),
109
- requiresUserInteraction: true,
110
- }
111
- });
112
- }
113
- }
114
-
115
- // ─── 4. Cloudflare Turnstile ────────────────────────────────────────────────
116
- document.querySelectorAll('iframe[src*="challenges.cloudflare"]').forEach(function(frame) {
117
- const src = frame.src || '';
118
- results.push({
119
- type: 'turnstile',
120
- interactionType: 'none',
121
- difficulty: 'easy',
122
- selector: 'iframe[src*="challenges.cloudflare"]',
123
- iframe: src,
124
- confidence: 0.92,
125
- metadata: {
126
- siteKey: extractSiteKey(src),
127
- requiresUserInteraction: false,
128
- }
129
- });
130
- });
131
- // Turnstile widget
132
- if (!results.some(function(r) { return r.type === 'turnstile'; })) {
133
- var cfEl = document.querySelector('[data-turnstile-sitekey], .cf-turnstile, cf-turnstile');
134
- if (cfEl && isVisible(cfEl)) {
135
- results.push({
136
- type: 'turnstile',
137
- interactionType: 'none',
138
- difficulty: 'easy',
139
- selector: '.cf-turnstile',
140
- confidence: 0.88,
141
- metadata: {
142
- siteKey: cfEl.getAttribute('data-sitekey') || cfEl.getAttribute('data-turnstile-sitekey'),
143
- requiresUserInteraction: false,
144
- }
145
- });
146
- }
147
- }
148
-
149
- // ─── 5. GeeTest v3 ─────────────────────────────────────────────────────────
150
- var geetestV3 = (typeof window.initGeetest !== 'undefined') ||
151
- !!document.querySelector('.geetest_radar_tip, .geetest_holder, .geetest_slider, [class*="geetest_"]');
152
- if (geetestV3) {
153
- var sliderEl = document.querySelector('.geetest_slider_button, .geetest_drag_thumb');
154
- var trackEl = document.querySelector('.geetest_slider_bar, .geetest_track');
155
- var trackWidth = trackEl ? trackEl.getBoundingClientRect().width : 280;
156
- var sliderSel = sliderEl ? '.geetest_slider_button' : '.geetest_holder';
157
- results.push({
158
- type: 'geetest',
159
- interactionType: 'drag',
160
- difficulty: 'hard',
161
- selector: sliderSel,
162
- confidence: 0.93,
163
- sliderTrackWidth: Math.round(trackWidth),
164
- metadata: {
165
- geetestVersion: 3,
166
- requiresUserInteraction: true,
167
- }
168
- });
169
- }
170
-
171
- // ─── 6. GeeTest v4 ─────────────────────────────────────────────────────────
172
- var geetestV4 = (typeof window.initGeetest4 !== 'undefined') ||
173
- !!document.querySelector('[class*="geetest"][class*="v4"], .gt4_root, .geetest4');
174
- if (geetestV4 && !geetestV3) {
175
- var gt4Slider = document.querySelector('.geetest-slider-btn, [class*="geetest4"][class*="slide"]');
176
- var gt4Track = document.querySelector('[class*="geetest4"][class*="track"], [class*="geetest4"][class*="bar"]');
177
- var gt4Width = gt4Track ? gt4Track.getBoundingClientRect().width : 260;
178
- results.push({
179
- type: 'geetest_v4',
180
- interactionType: 'drag',
181
- difficulty: 'hard',
182
- selector: gt4Slider ? '.geetest-slider-btn' : '.gt4_root',
183
- confidence: 0.9,
184
- sliderTrackWidth: Math.round(gt4Width),
185
- metadata: {
186
- geetestVersion: 4,
187
- requiresUserInteraction: true,
188
- }
189
- });
190
- }
191
-
192
- // ─── 7. FunCaptcha / Arkose Labs ────────────────────────────────────────────
193
- var funFrame = document.querySelector('iframe[src*="funcaptcha"], iframe[src*="arkoselabs"]');
194
- if (!funFrame) {
195
- funFrame = document.querySelector('iframe[title*="arkose"], iframe[title*="FunCaptcha"]');
196
- }
197
- if (funFrame && isVisible(funFrame)) {
198
- var funSrc = funFrame.src || '';
199
- results.push({
200
- type: 'funcaptcha',
201
- interactionType: 'rotate',
202
- difficulty: 'hard',
203
- selector: 'iframe[src*="funcaptcha"], iframe[src*="arkoselabs"]',
204
- iframe: funSrc,
205
- confidence: 0.88,
206
- metadata: {
207
- requiresUserInteraction: true,
208
- frameOrigin: funSrc.split('/')[2],
209
- }
210
- });
211
- }
212
-
213
- // ─── 8. Generic Slider CAPTCHA ──────────────────────────────────────────────
214
- if (!results.some(function(r) { return r.interactionType === 'drag'; })) {
215
- var sliderSelectors = [
216
- '.slider-captcha-track', '.slide-verify-slider', '.verify-slider',
217
- '[class*="slider"][class*="captcha"]', '[class*="captcha"][class*="slider"]',
218
- '[class*="slide-verify"]', '[class*="verify-wrap"]',
219
- '.nc_wrapper', '.nc-tpl', '[data-nc-scene]',
220
- 'div[class*="drag-verify"]', 'div[class*="dragger"]',
221
- ];
222
- for (var i = 0; i < sliderSelectors.length; i++) {
223
- var sEl = document.querySelector(sliderSelectors[i]);
224
- if (sEl && isVisible(sEl) && hasCaptchaKeyword(sEl.textContent || '')) {
225
- var trackEl2 = sEl.querySelector('[class*="track"], [class*="bar"], [class*="bg"]') || sEl;
226
- var tw = trackEl2.getBoundingClientRect().width || 280;
227
- results.push({
228
- type: 'slider',
229
- interactionType: 'drag',
230
- difficulty: 'hard',
231
- selector: sliderSelectors[i],
232
- confidence: 0.75,
233
- sliderTrackWidth: Math.round(tw),
234
- metadata: { requiresUserInteraction: true }
235
- });
236
- break;
237
- }
238
- }
239
- }
240
-
241
- // ─── 9. Press-and-Hold CAPTCHA ──────────────────────────────────────────────
242
- var holdSelectors = [
243
- '[class*="press-hold"]', '[class*="presshold"]', '[class*="hold-btn"]',
244
- '[class*="click-hold"]', 'button[data-hold]', '[data-captcha-hold]',
245
- '[class*="verify-btn"][class*="hold"]',
246
- ];
247
- for (var j = 0; j < holdSelectors.length; j++) {
248
- var hEl = document.querySelector(holdSelectors[j]);
249
- if (hEl && isVisible(hEl)) {
250
- // Try to read expected hold duration from attribute or text
251
- var holdMs = parseInt(hEl.getAttribute('data-hold-time') || hEl.getAttribute('data-duration') || '2000', 10);
252
- results.push({
253
- type: 'press_and_hold',
254
- interactionType: 'hold',
255
- difficulty: 'medium',
256
- selector: holdSelectors[j],
257
- confidence: 0.78,
258
- holdDurationMs: isNaN(holdMs) ? 2000 : holdMs,
259
- metadata: { requiresUserInteraction: true }
260
- });
261
- break;
262
- }
263
- }
264
- // Also look for countdown-style hold buttons
265
- var countdownBtns = document.querySelectorAll('button, [role="button"]');
266
- countdownBtns.forEach(function(btn) {
267
- if (!results.some(function(r) { return r.interactionType === 'hold'; })) {
268
- var txt = (btn.textContent || '') + (btn.getAttribute('aria-label') || '');
269
- if (/hold|press\s+and\s+hold|keep\s+press/i.test(txt) && isVisible(btn)) {
270
- results.push({
271
- type: 'press_and_hold',
272
- interactionType: 'hold',
273
- difficulty: 'medium',
274
- selector: null, // will be resolved to coords
275
- confidence: 0.7,
276
- holdDurationMs: 2500,
277
- metadata: { requiresUserInteraction: true }
278
- });
279
- }
280
- }
281
- });
282
-
283
- // ─── 10. DataDome ──────────────────────────────────────────────────────────
284
- var ddFrame = document.querySelector('iframe[src*="geo.captcha-delivery"], iframe[src*="datadome"]');
285
- var ddDiv = document.querySelector('#datadome, [id*="datadome"], [class*="datadome"]');
286
- if ((ddFrame && isVisible(ddFrame)) || (ddDiv && isVisible(ddDiv))) {
287
- var ddSrc = ddFrame ? (ddFrame.src || '') : '';
288
- results.push({
289
- type: 'datadome',
290
- interactionType: 'drag', // DataDome typically uses slider
291
- difficulty: 'hard',
292
- selector: ddFrame ? 'iframe[src*="captcha-delivery"]' : '#datadome',
293
- iframe: ddSrc,
294
- confidence: 0.88,
295
- metadata: { requiresUserInteraction: true }
296
- });
297
- }
298
-
299
- // ─── 11. PerimeterX ────────────────────────────────────────────────────────
300
- var pxEl = document.querySelector('#px-captcha, ._pxCaptcha, [id^="px-"][id$="captcha"]');
301
- if (pxEl && isVisible(pxEl)) {
302
- // PerimeterX is usually press-and-hold
303
- results.push({
304
- type: 'perimeter_x',
305
- interactionType: 'hold',
306
- difficulty: 'hard',
307
- selector: '#px-captcha',
308
- confidence: 0.88,
309
- holdDurationMs: 3000,
310
- metadata: { requiresUserInteraction: true }
311
- });
312
- }
313
-
314
- // ─── 12. Generic Custom CAPTCHA ────────────────────────────────────────────
315
- if (results.length === 0) {
316
- var customSels = [
317
- '.captcha-container', '[class*="captcha"]', '[id*="captcha"]',
318
- '[class*="challenge"]', '[id*="challenge"]',
319
- ];
320
- for (var k = 0; k < customSels.length; k++) {
321
- var cEls = document.querySelectorAll(customSels[k]);
322
- for (var m = 0; m < cEls.length; m++) {
323
- var cEl = cEls[m];
324
- if (isVisible(cEl) && hasCaptchaKeyword(cEl.textContent || '')) {
325
- // Try to classify interaction type from content
326
- var cText = (cEl.textContent || '').toLowerCase();
327
- var iType = 'unknown';
328
- if (/drag|slide|slider/i.test(cText)) iType = 'drag';
329
- else if (/hold|press/i.test(cText)) iType = 'hold';
330
- else if (/enter|type|text/i.test(cText)) iType = 'text';
331
- else if (/select|click.*image|image.*click/i.test(cText)) iType = 'image_select';
332
- else if (/math|\\d+\\s*[+\\-*/]\\s*\\d+/i.test(cText)) iType = 'math';
333
- results.push({
334
- type: 'custom',
335
- interactionType: iType,
336
- difficulty: 'hard',
337
- selector: customSels[k],
338
- confidence: 0.55,
339
- metadata: {
340
- hasChallenge: true,
341
- requiresUserInteraction: true,
342
- }
343
- });
344
- break;
345
- }
346
- }
347
- if (results.length > 0) break;
348
- }
349
- }
350
-
351
- // ─── Pick best result ───────────────────────────────────────────────────────
352
- if (results.length === 0) {
353
- return {
354
- detected: false,
355
- type: 'unknown',
356
- interactionType: 'unknown',
357
- difficulty: 'easy',
358
- selector: '',
359
- confidence: 0,
360
- metadata: {}
361
- };
362
- }
363
-
364
- results.sort(function(a, b) { return scoreResult(b) - scoreResult(a); });
365
- var best = results[0];
366
-
367
- return {
368
- detected: true,
369
- type: best.type,
370
- interactionType: best.interactionType || 'unknown',
371
- difficulty: best.difficulty,
372
- selector: best.selector || '',
373
- iframe: best.iframe,
374
- confidence: best.confidence,
375
- sliderTrackWidth: best.sliderTrackWidth,
376
- holdDurationMs: best.holdDurationMs,
377
- metadata: best.metadata || {}
378
- };
379
- })()
2
+ const DETECTION_SCRIPT = `
3
+ (function() {
4
+ 'use strict';
5
+ const results = [];
6
+
7
+ // ─── Helpers ────────────────────────────────────────────────────────────────
8
+
9
+ function extractSiteKey(url) {
10
+ const m = url.match(/[?&]k=([^&]+)/) ||
11
+ url.match(/[?&]sitekey=([^&]+)/) ||
12
+ url.match(/[?&]site_key=([^&]+)/);
13
+ return m ? decodeURIComponent(m[1]) : null;
14
+ }
15
+
16
+ function isVisible(el) {
17
+ if (!el) return false;
18
+ const r = el.getBoundingClientRect();
19
+ if (r.width === 0 && r.height === 0) return false;
20
+ const st = window.getComputedStyle(el);
21
+ return st.display !== 'none' && st.visibility !== 'hidden' && st.opacity !== '0';
22
+ }
23
+
24
+ function hasCaptchaKeyword(text) {
25
+ return /captcha|challenge|verify|robot|human|puzzle|slider|drag/i.test(text);
26
+ }
27
+
28
+ function scoreResult(r) {
29
+ // Higher = more confidence
30
+ const typeScore = {
31
+ recaptcha_v2: 0.95, recaptcha_v3: 0.9, hcaptcha: 0.95,
32
+ turnstile: 0.9, geetest: 0.95, geetest_v4: 0.92,
33
+ funcaptcha: 0.88, slider: 0.75, press_and_hold: 0.8,
34
+ datadome: 0.9, perimeter_x: 0.88, custom: 0.6, unknown: 0.3,
35
+ };
36
+ return (typeScore[r.type] || 0.5) * r.confidence;
37
+ }
38
+
39
+ // ─── 1. reCAPTCHA v2 ────────────────────────────────────────────────────────
40
+ document.querySelectorAll('iframe[src*="recaptcha"]').forEach(function(frame) {
41
+ const src = frame.src || '';
42
+ const isV2 = src.includes('/api2/') || src.includes('/enterprise/') || src.includes('recaptcha/api2');
43
+ if (isV2) {
44
+ const hasChallenge = src.includes('bframe') || src.includes('frame=bframe');
45
+ results.push({
46
+ type: 'recaptcha_v2',
47
+ interactionType: hasChallenge ? 'image_select' : 'checkbox',
48
+ difficulty: hasChallenge ? 'hard' : 'medium',
49
+ selector: 'iframe[src*="recaptcha"]',
50
+ iframe: src,
51
+ confidence: 0.95,
52
+ metadata: {
53
+ siteKey: extractSiteKey(src),
54
+ hasAudio: false,
55
+ hasChallenge: hasChallenge,
56
+ requiresUserInteraction: true,
57
+ }
58
+ });
59
+ }
60
+ });
61
+
62
+ // ─── 2. reCAPTCHA v3 (invisible) ────────────────────────────────────────────
63
+ var rcScripts = Array.from(document.scripts).filter(function(s) {
64
+ return s.src && s.src.includes('recaptcha/api.js') && !s.src.includes('enterprise');
65
+ });
66
+ if (rcScripts.length > 0 && !results.some(function(r) { return r.type === 'recaptcha_v2'; })) {
67
+ results.push({
68
+ type: 'recaptcha_v3',
69
+ interactionType: 'none',
70
+ difficulty: 'easy',
71
+ selector: 'script[src*="recaptcha/api.js"]',
72
+ confidence: 0.85,
73
+ metadata: {
74
+ siteKey: extractSiteKey(rcScripts[0].src),
75
+ requiresUserInteraction: false,
76
+ }
77
+ });
78
+ }
79
+
80
+ // ─── 3. hCaptcha ────────────────────────────────────────────────────────────
81
+ document.querySelectorAll('iframe[src*="hcaptcha"]').forEach(function(frame) {
82
+ const src = frame.src || '';
83
+ const isChallenge = src.includes('/challenge');
84
+ results.push({
85
+ type: 'hcaptcha',
86
+ interactionType: isChallenge ? 'image_select' : 'checkbox',
87
+ difficulty: isChallenge ? 'hard' : 'medium',
88
+ selector: 'iframe[src*="hcaptcha"]',
89
+ iframe: src,
90
+ confidence: 0.95,
91
+ metadata: {
92
+ siteKey: extractSiteKey(src),
93
+ requiresUserInteraction: true,
94
+ }
95
+ });
96
+ });
97
+ // hCaptcha widget div
98
+ if (!results.some(function(r) { return r.type === 'hcaptcha'; })) {
99
+ var hDiv = document.querySelector('[data-hcaptcha-widget-id], h-captcha, [data-sitekey][class*="h-captcha"]');
100
+ if (hDiv && isVisible(hDiv)) {
101
+ results.push({
102
+ type: 'hcaptcha',
103
+ interactionType: 'checkbox',
104
+ difficulty: 'medium',
105
+ selector: '[data-hcaptcha-widget-id]',
106
+ confidence: 0.88,
107
+ metadata: {
108
+ siteKey: hDiv.getAttribute('data-sitekey'),
109
+ requiresUserInteraction: true,
110
+ }
111
+ });
112
+ }
113
+ }
114
+
115
+ // ─── 4. Cloudflare Turnstile ────────────────────────────────────────────────
116
+ document.querySelectorAll('iframe[src*="challenges.cloudflare"]').forEach(function(frame) {
117
+ const src = frame.src || '';
118
+ results.push({
119
+ type: 'turnstile',
120
+ interactionType: 'none',
121
+ difficulty: 'easy',
122
+ selector: 'iframe[src*="challenges.cloudflare"]',
123
+ iframe: src,
124
+ confidence: 0.92,
125
+ metadata: {
126
+ siteKey: extractSiteKey(src),
127
+ requiresUserInteraction: false,
128
+ }
129
+ });
130
+ });
131
+ // Turnstile widget
132
+ if (!results.some(function(r) { return r.type === 'turnstile'; })) {
133
+ var cfEl = document.querySelector('[data-turnstile-sitekey], .cf-turnstile, cf-turnstile');
134
+ if (cfEl && isVisible(cfEl)) {
135
+ results.push({
136
+ type: 'turnstile',
137
+ interactionType: 'none',
138
+ difficulty: 'easy',
139
+ selector: '.cf-turnstile',
140
+ confidence: 0.88,
141
+ metadata: {
142
+ siteKey: cfEl.getAttribute('data-sitekey') || cfEl.getAttribute('data-turnstile-sitekey'),
143
+ requiresUserInteraction: false,
144
+ }
145
+ });
146
+ }
147
+ }
148
+
149
+ // ─── 5. GeeTest v3 ─────────────────────────────────────────────────────────
150
+ var geetestV3 = (typeof window.initGeetest !== 'undefined') ||
151
+ !!document.querySelector('.geetest_radar_tip, .geetest_holder, .geetest_slider, [class*="geetest_"]');
152
+ if (geetestV3) {
153
+ var sliderEl = document.querySelector('.geetest_slider_button, .geetest_drag_thumb');
154
+ var trackEl = document.querySelector('.geetest_slider_bar, .geetest_track');
155
+ var trackWidth = trackEl ? trackEl.getBoundingClientRect().width : 280;
156
+ var sliderSel = sliderEl ? '.geetest_slider_button' : '.geetest_holder';
157
+ results.push({
158
+ type: 'geetest',
159
+ interactionType: 'drag',
160
+ difficulty: 'hard',
161
+ selector: sliderSel,
162
+ confidence: 0.93,
163
+ sliderTrackWidth: Math.round(trackWidth),
164
+ metadata: {
165
+ geetestVersion: 3,
166
+ requiresUserInteraction: true,
167
+ }
168
+ });
169
+ }
170
+
171
+ // ─── 6. GeeTest v4 ─────────────────────────────────────────────────────────
172
+ var geetestV4 = (typeof window.initGeetest4 !== 'undefined') ||
173
+ !!document.querySelector('[class*="geetest"][class*="v4"], .gt4_root, .geetest4');
174
+ if (geetestV4 && !geetestV3) {
175
+ var gt4Slider = document.querySelector('.geetest-slider-btn, [class*="geetest4"][class*="slide"]');
176
+ var gt4Track = document.querySelector('[class*="geetest4"][class*="track"], [class*="geetest4"][class*="bar"]');
177
+ var gt4Width = gt4Track ? gt4Track.getBoundingClientRect().width : 260;
178
+ results.push({
179
+ type: 'geetest_v4',
180
+ interactionType: 'drag',
181
+ difficulty: 'hard',
182
+ selector: gt4Slider ? '.geetest-slider-btn' : '.gt4_root',
183
+ confidence: 0.9,
184
+ sliderTrackWidth: Math.round(gt4Width),
185
+ metadata: {
186
+ geetestVersion: 4,
187
+ requiresUserInteraction: true,
188
+ }
189
+ });
190
+ }
191
+
192
+ // ─── 7. FunCaptcha / Arkose Labs ────────────────────────────────────────────
193
+ var funFrame = document.querySelector('iframe[src*="funcaptcha"], iframe[src*="arkoselabs"]');
194
+ if (!funFrame) {
195
+ funFrame = document.querySelector('iframe[title*="arkose"], iframe[title*="FunCaptcha"]');
196
+ }
197
+ if (funFrame && isVisible(funFrame)) {
198
+ var funSrc = funFrame.src || '';
199
+ results.push({
200
+ type: 'funcaptcha',
201
+ interactionType: 'rotate',
202
+ difficulty: 'hard',
203
+ selector: 'iframe[src*="funcaptcha"], iframe[src*="arkoselabs"]',
204
+ iframe: funSrc,
205
+ confidence: 0.88,
206
+ metadata: {
207
+ requiresUserInteraction: true,
208
+ frameOrigin: funSrc.split('/')[2],
209
+ }
210
+ });
211
+ }
212
+
213
+ // ─── 8. Generic Slider CAPTCHA ──────────────────────────────────────────────
214
+ if (!results.some(function(r) { return r.interactionType === 'drag'; })) {
215
+ var sliderSelectors = [
216
+ '.slider-captcha-track', '.slide-verify-slider', '.verify-slider',
217
+ '[class*="slider"][class*="captcha"]', '[class*="captcha"][class*="slider"]',
218
+ '[class*="slide-verify"]', '[class*="verify-wrap"]',
219
+ '.nc_wrapper', '.nc-tpl', '[data-nc-scene]',
220
+ 'div[class*="drag-verify"]', 'div[class*="dragger"]',
221
+ ];
222
+ for (var i = 0; i < sliderSelectors.length; i++) {
223
+ var sEl = document.querySelector(sliderSelectors[i]);
224
+ if (sEl && isVisible(sEl) && hasCaptchaKeyword(sEl.textContent || '')) {
225
+ var trackEl2 = sEl.querySelector('[class*="track"], [class*="bar"], [class*="bg"]') || sEl;
226
+ var tw = trackEl2.getBoundingClientRect().width || 280;
227
+ results.push({
228
+ type: 'slider',
229
+ interactionType: 'drag',
230
+ difficulty: 'hard',
231
+ selector: sliderSelectors[i],
232
+ confidence: 0.75,
233
+ sliderTrackWidth: Math.round(tw),
234
+ metadata: { requiresUserInteraction: true }
235
+ });
236
+ break;
237
+ }
238
+ }
239
+ }
240
+
241
+ // ─── 9. Press-and-Hold CAPTCHA ──────────────────────────────────────────────
242
+ var holdSelectors = [
243
+ '[class*="press-hold"]', '[class*="presshold"]', '[class*="hold-btn"]',
244
+ '[class*="click-hold"]', 'button[data-hold]', '[data-captcha-hold]',
245
+ '[class*="verify-btn"][class*="hold"]',
246
+ ];
247
+ for (var j = 0; j < holdSelectors.length; j++) {
248
+ var hEl = document.querySelector(holdSelectors[j]);
249
+ if (hEl && isVisible(hEl)) {
250
+ // Try to read expected hold duration from attribute or text
251
+ var holdMs = parseInt(hEl.getAttribute('data-hold-time') || hEl.getAttribute('data-duration') || '2000', 10);
252
+ results.push({
253
+ type: 'press_and_hold',
254
+ interactionType: 'hold',
255
+ difficulty: 'medium',
256
+ selector: holdSelectors[j],
257
+ confidence: 0.78,
258
+ holdDurationMs: isNaN(holdMs) ? 2000 : holdMs,
259
+ metadata: { requiresUserInteraction: true }
260
+ });
261
+ break;
262
+ }
263
+ }
264
+ // Also look for countdown-style hold buttons
265
+ var countdownBtns = document.querySelectorAll('button, [role="button"]');
266
+ countdownBtns.forEach(function(btn) {
267
+ if (!results.some(function(r) { return r.interactionType === 'hold'; })) {
268
+ var txt = (btn.textContent || '') + (btn.getAttribute('aria-label') || '');
269
+ if (/hold|press\s+and\s+hold|keep\s+press/i.test(txt) && isVisible(btn)) {
270
+ results.push({
271
+ type: 'press_and_hold',
272
+ interactionType: 'hold',
273
+ difficulty: 'medium',
274
+ selector: null, // will be resolved to coords
275
+ confidence: 0.7,
276
+ holdDurationMs: 2500,
277
+ metadata: { requiresUserInteraction: true }
278
+ });
279
+ }
280
+ }
281
+ });
282
+
283
+ // ─── 10. DataDome ──────────────────────────────────────────────────────────
284
+ var ddFrame = document.querySelector('iframe[src*="geo.captcha-delivery"], iframe[src*="datadome"]');
285
+ var ddDiv = document.querySelector('#datadome, [id*="datadome"], [class*="datadome"]');
286
+ if ((ddFrame && isVisible(ddFrame)) || (ddDiv && isVisible(ddDiv))) {
287
+ var ddSrc = ddFrame ? (ddFrame.src || '') : '';
288
+ results.push({
289
+ type: 'datadome',
290
+ interactionType: 'drag', // DataDome typically uses slider
291
+ difficulty: 'hard',
292
+ selector: ddFrame ? 'iframe[src*="captcha-delivery"]' : '#datadome',
293
+ iframe: ddSrc,
294
+ confidence: 0.88,
295
+ metadata: { requiresUserInteraction: true }
296
+ });
297
+ }
298
+
299
+ // ─── 11. PerimeterX ────────────────────────────────────────────────────────
300
+ var pxEl = document.querySelector('#px-captcha, ._pxCaptcha, [id^="px-"][id$="captcha"]');
301
+ if (pxEl && isVisible(pxEl)) {
302
+ // PerimeterX is usually press-and-hold
303
+ results.push({
304
+ type: 'perimeter_x',
305
+ interactionType: 'hold',
306
+ difficulty: 'hard',
307
+ selector: '#px-captcha',
308
+ confidence: 0.88,
309
+ holdDurationMs: 3000,
310
+ metadata: { requiresUserInteraction: true }
311
+ });
312
+ }
313
+
314
+ // ─── 12. Generic Custom CAPTCHA ────────────────────────────────────────────
315
+ if (results.length === 0) {
316
+ var customSels = [
317
+ '.captcha-container', '[class*="captcha"]', '[id*="captcha"]',
318
+ '[class*="challenge"]', '[id*="challenge"]',
319
+ ];
320
+ for (var k = 0; k < customSels.length; k++) {
321
+ var cEls = document.querySelectorAll(customSels[k]);
322
+ for (var m = 0; m < cEls.length; m++) {
323
+ var cEl = cEls[m];
324
+ if (isVisible(cEl) && hasCaptchaKeyword(cEl.textContent || '')) {
325
+ // Try to classify interaction type from content
326
+ var cText = (cEl.textContent || '').toLowerCase();
327
+ var iType = 'unknown';
328
+ if (/drag|slide|slider/i.test(cText)) iType = 'drag';
329
+ else if (/hold|press/i.test(cText)) iType = 'hold';
330
+ else if (/enter|type|text/i.test(cText)) iType = 'text';
331
+ else if (/select|click.*image|image.*click/i.test(cText)) iType = 'image_select';
332
+ else if (/math|\\d+\\s*[+\\-*/]\\s*\\d+/i.test(cText)) iType = 'math';
333
+ results.push({
334
+ type: 'custom',
335
+ interactionType: iType,
336
+ difficulty: 'hard',
337
+ selector: customSels[k],
338
+ confidence: 0.55,
339
+ metadata: {
340
+ hasChallenge: true,
341
+ requiresUserInteraction: true,
342
+ }
343
+ });
344
+ break;
345
+ }
346
+ }
347
+ if (results.length > 0) break;
348
+ }
349
+ }
350
+
351
+ // ─── Pick best result ───────────────────────────────────────────────────────
352
+ if (results.length === 0) {
353
+ return {
354
+ detected: false,
355
+ type: 'unknown',
356
+ interactionType: 'unknown',
357
+ difficulty: 'easy',
358
+ selector: '',
359
+ confidence: 0,
360
+ metadata: {}
361
+ };
362
+ }
363
+
364
+ results.sort(function(a, b) { return scoreResult(b) - scoreResult(a); });
365
+ var best = results[0];
366
+
367
+ return {
368
+ detected: true,
369
+ type: best.type,
370
+ interactionType: best.interactionType || 'unknown',
371
+ difficulty: best.difficulty,
372
+ selector: best.selector || '',
373
+ iframe: best.iframe,
374
+ confidence: best.confidence,
375
+ sliderTrackWidth: best.sliderTrackWidth,
376
+ holdDurationMs: best.holdDurationMs,
377
+ metadata: best.metadata || {}
378
+ };
379
+ })()
380
380
  `;
381
381
  /**
382
382
  * Comprehensive CAPTCHA detection using multiple heuristics.
@@ -425,13 +425,13 @@ export async function isCaptchaBlocking(session) {
425
425
  if (!detection.selector)
426
426
  return true; // Unknown selector — assume blocking
427
427
  const blockingCheck = await session.Runtime.evaluate({
428
- expression: `(function(selector) {
429
- try {
430
- var el = document.querySelector(selector);
431
- if (!el) return false;
432
- var r = el.getBoundingClientRect();
433
- return r.width > 0 && r.height > 0 && r.top < window.innerHeight;
434
- } catch(e) { return false; }
428
+ expression: `(function(selector) {
429
+ try {
430
+ var el = document.querySelector(selector);
431
+ if (!el) return false;
432
+ var r = el.getBoundingClientRect();
433
+ return r.width > 0 && r.height > 0 && r.top < window.innerHeight;
434
+ } catch(e) { return false; }
435
435
  })(${JSON.stringify(detection.selector)})`,
436
436
  returnByValue: true,
437
437
  });
@@ -444,16 +444,16 @@ export async function isCaptchaBlocking(session) {
444
444
  export async function quickCaptchaCheck(session) {
445
445
  try {
446
446
  const r = await session.Runtime.evaluate({
447
- expression: `(function() {
448
- var checks = [
449
- !!document.querySelector('iframe[src*="recaptcha"]'),
450
- !!document.querySelector('iframe[src*="hcaptcha"]'),
451
- !!document.querySelector('iframe[src*="challenges.cloudflare"]'),
452
- !!document.querySelector('.geetest_holder, .geetest_slider'),
453
- !!document.querySelector('[id^="px-"][id$="captcha"], #datadome'),
454
- !!document.querySelector('iframe[src*="funcaptcha"], iframe[src*="arkoselabs"]'),
455
- ];
456
- return checks.some(Boolean);
447
+ expression: `(function() {
448
+ var checks = [
449
+ !!document.querySelector('iframe[src*="recaptcha"]'),
450
+ !!document.querySelector('iframe[src*="hcaptcha"]'),
451
+ !!document.querySelector('iframe[src*="challenges.cloudflare"]'),
452
+ !!document.querySelector('.geetest_holder, .geetest_slider'),
453
+ !!document.querySelector('[id^="px-"][id$="captcha"], #datadome'),
454
+ !!document.querySelector('iframe[src*="funcaptcha"], iframe[src*="arkoselabs"]'),
455
+ ];
456
+ return checks.some(Boolean);
457
457
  })()`,
458
458
  returnByValue: true,
459
459
  });