@jshookmcp/jshook 0.1.6 → 0.1.8

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 (65) hide show
  1. package/LICENSE +661 -661
  2. package/README.md +145 -100
  3. package/README.zh.md +81 -36
  4. package/dist/constants.d.ts +1 -1
  5. package/dist/constants.js +3 -1
  6. package/dist/index.js +0 -0
  7. package/dist/modules/analyzer/QualityAnalyzer.js +1 -1
  8. package/dist/modules/browser/BrowserDiscovery.js +2 -2
  9. package/dist/modules/browser/BrowserModeManager.js +3 -3
  10. package/dist/modules/captcha/AICaptchaDetector.d.ts +12 -16
  11. package/dist/modules/captcha/AICaptchaDetector.js +229 -209
  12. package/dist/modules/captcha/CaptchaDetector.constants.d.ts +2 -0
  13. package/dist/modules/captcha/CaptchaDetector.constants.js +116 -25
  14. package/dist/modules/captcha/CaptchaDetector.d.ts +2 -11
  15. package/dist/modules/captcha/CaptchaDetector.js +102 -51
  16. package/dist/modules/captcha/types.d.ts +46 -0
  17. package/dist/modules/captcha/types.js +52 -0
  18. package/dist/modules/deobfuscator/AdvancedDeobfuscator.d.ts +15 -20
  19. package/dist/modules/deobfuscator/AdvancedDeobfuscator.js +66 -234
  20. package/dist/modules/deobfuscator/Deobfuscator.d.ts +3 -10
  21. package/dist/modules/deobfuscator/Deobfuscator.js +125 -404
  22. package/dist/modules/deobfuscator/webcrack.d.ts +13 -0
  23. package/dist/modules/deobfuscator/webcrack.js +164 -0
  24. package/dist/modules/detector/ObfuscationDetector.d.ts +6 -0
  25. package/dist/modules/detector/ObfuscationDetector.js +53 -2
  26. package/dist/modules/hook/AIHookGenerator.js +1 -1
  27. package/dist/modules/process/MacProcessManager.js +25 -25
  28. package/dist/modules/process/memory/availability.js +49 -49
  29. package/dist/modules/process/memory/injector.js +185 -185
  30. package/dist/modules/process/memory/reader.js +50 -50
  31. package/dist/modules/process/memory/scanner.js +165 -165
  32. package/dist/modules/process/memory/writer.js +55 -55
  33. package/dist/native/scripts/linux/enum-windows.sh +12 -12
  34. package/dist/native/scripts/macos/enum-windows.applescript +22 -22
  35. package/dist/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
  36. package/dist/native/scripts/windows/enum-windows.ps1 +44 -44
  37. package/dist/native/scripts/windows/inject-dll.ps1 +21 -21
  38. package/dist/server/domains/analysis/definitions.js +223 -2
  39. package/dist/server/domains/analysis/handlers.impl.d.ts +2 -3
  40. package/dist/server/domains/analysis/handlers.impl.js +60 -15
  41. package/dist/server/domains/analysis/manifest.js +2 -5
  42. package/dist/server/domains/browser/definitions.tools.behavior.js +36 -24
  43. package/dist/server/domains/browser/definitions.tools.page-core.js +53 -53
  44. package/dist/server/domains/browser/definitions.tools.runtime.js +40 -40
  45. package/dist/server/domains/browser/definitions.tools.security.js +80 -77
  46. package/dist/server/domains/browser/handlers/camoufox-flow.js +0 -1
  47. package/dist/server/domains/browser/handlers/captcha-solver.d.ts +1 -1
  48. package/dist/server/domains/browser/handlers/captcha-solver.js +121 -54
  49. package/dist/server/domains/browser/handlers/page-navigation.js +0 -2
  50. package/dist/server/domains/browser/handlers.impl.d.ts +1 -1
  51. package/dist/server/domains/browser/handlers.impl.js +3 -3
  52. package/dist/server/domains/browser/manifest.js +1 -1
  53. package/dist/server/domains/shared/modules.d.ts +1 -0
  54. package/dist/server/domains/transform/handlers.impl.transform-base.js +102 -102
  55. package/dist/server/domains/workflow/handlers.impl.workflow-base.js +51 -51
  56. package/dist/types/deobfuscator.d.ts +43 -1
  57. package/dist/types/index.d.ts +1 -1
  58. package/dist/utils/config.js +19 -10
  59. package/package.json +30 -44
  60. package/scripts/postinstall.cjs +37 -0
  61. package/src/native/scripts/linux/enum-windows.sh +12 -12
  62. package/src/native/scripts/macos/enum-windows.applescript +22 -22
  63. package/src/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
  64. package/src/native/scripts/windows/enum-windows.ps1 +44 -44
  65. package/src/native/scripts/windows/inject-dll.ps1 +21 -21
@@ -1,5 +1,5 @@
1
1
  import { logger } from '../../../../utils/logger.js';
2
- import { CAPTCHA_2CAPTCHA_BASE_URL, CAPTCHA_SUBMIT_TIMEOUT_MS, CAPTCHA_POLL_INTERVAL_MS, CAPTCHA_RESULT_TIMEOUT_MS, CAPTCHA_DEFAULT_TIMEOUT_MS, CAPTCHA_MIN_TIMEOUT_MS, CAPTCHA_MAX_TIMEOUT_MS, CAPTCHA_MAX_RETRIES, CAPTCHA_DEFAULT_RETRIES, } from '../../../../constants.js';
2
+ import { CAPTCHA_SOLVER_BASE_URL, CAPTCHA_SUBMIT_TIMEOUT_MS, CAPTCHA_POLL_INTERVAL_MS, CAPTCHA_RESULT_TIMEOUT_MS, CAPTCHA_DEFAULT_TIMEOUT_MS, CAPTCHA_MIN_TIMEOUT_MS, CAPTCHA_MAX_TIMEOUT_MS, CAPTCHA_MAX_RETRIES, CAPTCHA_DEFAULT_RETRIES, } from '../../../../constants.js';
3
3
  function sleep(ms) {
4
4
  return new Promise(resolve => setTimeout(resolve, ms));
5
5
  }
@@ -14,15 +14,64 @@ function toErrorResponse(tool, error, extra = {}) {
14
14
  ...extra,
15
15
  });
16
16
  }
17
+ function normalizeSolverMode(rawMode) {
18
+ const value = typeof rawMode === 'string' ? rawMode.toLowerCase() : '';
19
+ if (value === 'hook')
20
+ return 'hook';
21
+ if (value === 'external_service')
22
+ return 'external_service';
23
+ if (value === '2captcha' || value === 'anticaptcha' || value === 'capsolver') {
24
+ return 'external_service';
25
+ }
26
+ return 'manual';
27
+ }
28
+ function normalizeChallengeTypeHint(rawType) {
29
+ const value = typeof rawType === 'string' ? rawType.toLowerCase() : '';
30
+ if (value === 'image')
31
+ return 'image';
32
+ if (value === 'widget' ||
33
+ value === 'recaptcha_v2' ||
34
+ value === 'recaptcha_v3' ||
35
+ value === 'hcaptcha' ||
36
+ value === 'funcaptcha' ||
37
+ value === 'turnstile') {
38
+ return 'widget';
39
+ }
40
+ if (value === 'browser_check' || value === 'managed_widget') {
41
+ return 'browser_check';
42
+ }
43
+ return 'auto';
44
+ }
45
+ function resolveLegacyServiceOverride(rawProvider) {
46
+ if (typeof rawProvider !== 'string' || !rawProvider.trim()) {
47
+ return undefined;
48
+ }
49
+ return rawProvider.trim().toLowerCase();
50
+ }
51
+ function resolveExternalServiceName(args) {
52
+ const legacyOverride = resolveLegacyServiceOverride(args.provider);
53
+ const configured = (process.env.CAPTCHA_PROVIDER || '').trim().toLowerCase();
54
+ return legacyOverride || configured || '2captcha';
55
+ }
17
56
  async function solveWith2Captcha(apiKey, params, timeoutMs) {
18
57
  const start = Date.now();
19
- const baseUrl = CAPTCHA_2CAPTCHA_BASE_URL;
58
+ const baseUrl = CAPTCHA_SOLVER_BASE_URL;
59
+ if (!baseUrl) {
60
+ throw new Error('CAPTCHA_SOLVER_BASE_URL must be configured before using external_service mode.');
61
+ }
20
62
  const submitBody = {
21
63
  key: apiKey,
22
64
  json: 1,
23
65
  };
24
- if (params.type === 'turnstile' || params.type === 'recaptcha_v2' || params.type === 'hcaptcha') {
25
- submitBody.method = params.type === 'turnstile' ? 'turnstile' : params.type === 'hcaptcha' ? 'hcaptcha' : 'userrecaptcha';
66
+ if (params.taskKind === 'turnstile' ||
67
+ params.taskKind === 'recaptcha_v2' ||
68
+ params.taskKind === 'hcaptcha') {
69
+ submitBody.method =
70
+ params.taskKind === 'turnstile'
71
+ ? 'turnstile'
72
+ : params.taskKind === 'hcaptcha'
73
+ ? 'hcaptcha'
74
+ : 'userrecaptcha';
26
75
  submitBody.sitekey = params.siteKey;
27
76
  submitBody.pageurl = params.pageUrl;
28
77
  }
@@ -61,8 +110,8 @@ async function solveWith2Captcha(apiKey, params, timeoutMs) {
61
110
  if (resultData.status === 1) {
62
111
  return {
63
112
  token: resultData.request,
64
- type: params.type,
65
- provider: '2captcha',
113
+ challengeType: params.taskKind === 'image' ? 'image' : 'widget',
114
+ mode: 'external_service',
66
115
  durationMs: Date.now() - start,
67
116
  };
68
117
  }
@@ -76,73 +125,87 @@ export async function handleCaptchaVisionSolve(args, collector) {
76
125
  const page = await collector.getActivePage();
77
126
  if (!page)
78
127
  throw new Error('No active page.');
79
- const provider = args.provider || process.env.CAPTCHA_PROVIDER || 'manual';
128
+ const mode = normalizeSolverMode(args.mode ?? args.provider ?? process.env.CAPTCHA_PROVIDER);
129
+ const externalService = resolveExternalServiceName(args);
80
130
  const apiKey = args.apiKey || process.env.CAPTCHA_API_KEY || '';
81
- const typeHint = args.typeHint || 'auto';
131
+ const challengeTypeHint = normalizeChallengeTypeHint(args.challengeType ?? args.typeHint);
82
132
  const timeoutMs = Math.min(Math.max(args.timeoutMs ?? CAPTCHA_DEFAULT_TIMEOUT_MS, CAPTCHA_MIN_TIMEOUT_MS), CAPTCHA_MAX_TIMEOUT_MS);
83
133
  const maxRetries = Math.min(Math.max(args.maxRetries ?? CAPTCHA_DEFAULT_RETRIES, 0), CAPTCHA_MAX_RETRIES);
84
- let captchaType = typeHint;
134
+ let challengeType = challengeTypeHint;
135
+ let taskKind = challengeTypeHint === 'image' ? 'image' : 'recaptcha_v2';
85
136
  let siteKey = args.siteKey;
86
137
  const pageUrl = args.pageUrl || page.url();
87
- if (captchaType === 'auto') {
138
+ if (challengeType === 'auto') {
88
139
  const detected = await page.evaluate(() => {
89
140
  if (document.querySelector('[data-sitekey]')) {
90
141
  const el = document.querySelector('[data-sitekey]');
91
142
  const sk = el?.getAttribute('data-sitekey') || '';
92
- if (document.querySelector('.cf-turnstile'))
93
- return { type: 'turnstile', siteKey: sk };
94
- if (document.querySelector('.h-captcha'))
95
- return { type: 'hcaptcha', siteKey: sk };
96
- return { type: 'recaptcha_v2', siteKey: sk };
143
+ if (document.querySelector('.cf-turnstile')) {
144
+ return { challengeType: 'widget', taskKind: 'turnstile', siteKey: sk };
145
+ }
146
+ if (document.querySelector('.h-captcha')) {
147
+ return { challengeType: 'widget', taskKind: 'hcaptcha', siteKey: sk };
148
+ }
149
+ return { challengeType: 'widget', taskKind: 'recaptcha_v2', siteKey: sk };
150
+ }
151
+ if (document.querySelector('iframe[src*="recaptcha"]')) {
152
+ return { challengeType: 'widget', taskKind: 'recaptcha_v2', siteKey: '' };
153
+ }
154
+ if (document.querySelector('iframe[src*="hcaptcha"]')) {
155
+ return { challengeType: 'widget', taskKind: 'hcaptcha', siteKey: '' };
97
156
  }
98
- if (document.querySelector('iframe[src*="recaptcha"]'))
99
- return { type: 'recaptcha_v2', siteKey: '' };
100
- if (document.querySelector('iframe[src*="hcaptcha"]'))
101
- return { type: 'hcaptcha', siteKey: '' };
102
- if (document.querySelector('.cf-turnstile'))
103
- return { type: 'turnstile', siteKey: '' };
104
- return { type: 'image', siteKey: '' };
157
+ if (document.querySelector('.cf-turnstile')) {
158
+ return { challengeType: 'widget', taskKind: 'turnstile', siteKey: '' };
159
+ }
160
+ return { challengeType: 'image', taskKind: 'image', siteKey: '' };
105
161
  });
106
- captchaType = detected.type;
162
+ challengeType = detected.challengeType;
163
+ taskKind = detected.taskKind;
107
164
  if (!siteKey && detected.siteKey)
108
165
  siteKey = detected.siteKey;
109
166
  }
110
- if (provider === 'manual') {
167
+ else if (challengeType === 'image') {
168
+ taskKind = 'image';
169
+ }
170
+ else {
171
+ taskKind = 'recaptcha_v2';
172
+ }
173
+ if (mode === 'manual') {
111
174
  return toTextResponse({
112
175
  success: true,
113
176
  mode: 'manual',
114
- captchaType,
177
+ challengeType,
115
178
  siteKey: siteKey ?? null,
116
179
  instruction: 'Please solve the CAPTCHA manually in the browser, then continue.',
117
- hint: 'Set CAPTCHA_PROVIDER and CAPTCHA_API_KEY env vars for automatic solving.',
180
+ hint: 'Configure an external solver service and CAPTCHA_API_KEY to automate this flow.',
118
181
  });
119
182
  }
120
183
  if (!apiKey) {
121
- return toErrorResponse('captcha_vision_solve', new Error(`API key required for provider "${provider}". Set CAPTCHA_API_KEY env var.`));
184
+ return toErrorResponse('captcha_vision_solve', new Error('External solver credentials are required. Set CAPTCHA_API_KEY.'));
122
185
  }
123
186
  let lastError = null;
124
187
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
125
188
  try {
126
189
  let result;
127
- if (provider === '2captcha') {
190
+ if (externalService === '2captcha') {
128
191
  result = await solveWith2Captcha(apiKey, {
129
- type: captchaType,
192
+ taskKind,
130
193
  siteKey,
131
194
  pageUrl,
132
195
  }, timeoutMs);
133
196
  }
134
- else if (provider === 'anticaptcha' || provider === 'capsolver') {
135
- throw new Error(`Provider "${provider}" is not yet implemented. ` +
136
- `Currently only "2captcha" and "manual" are supported.`);
197
+ else if (externalService === 'anticaptcha' || externalService === 'capsolver') {
198
+ throw new Error('The selected external solver service is not yet implemented. ' +
199
+ 'Currently only the configured primary service and manual mode are supported.');
137
200
  }
138
201
  else {
139
- throw new Error(`Unsupported provider: ${provider}`);
202
+ throw new Error('Unsupported external solver service.');
140
203
  }
141
204
  return toTextResponse({
142
205
  success: true,
143
206
  token: result.token,
144
- captchaType: result.type,
145
- provider: result.provider,
207
+ challengeType: result.challengeType,
208
+ mode: result.mode,
146
209
  durationMs: result.durationMs,
147
210
  attempt: attempt + 1,
148
211
  });
@@ -153,17 +216,18 @@ export async function handleCaptchaVisionSolve(args, collector) {
153
216
  }
154
217
  }
155
218
  return toErrorResponse('captcha_vision_solve', lastError ?? new Error('All attempts failed'), {
156
- captchaType,
157
- provider,
219
+ challengeType,
220
+ mode,
158
221
  maxRetries,
159
- suggestion: 'Try a different provider or solve manually.',
222
+ suggestion: 'Try manual mode or adjust the external solver configuration.',
160
223
  });
161
224
  }
162
- export async function handleTurnstileSolve(args, collector) {
225
+ export async function handleWidgetChallengeSolve(args, collector) {
163
226
  const page = await collector.getActivePage();
164
227
  if (!page)
165
228
  throw new Error('No active page.');
166
- const provider = args.provider || process.env.CAPTCHA_PROVIDER || 'manual';
229
+ const mode = normalizeSolverMode(args.mode ?? args.provider ?? process.env.CAPTCHA_PROVIDER);
230
+ const externalService = resolveExternalServiceName(args);
167
231
  const apiKey = args.apiKey || process.env.CAPTCHA_API_KEY || '';
168
232
  const timeoutMs = Math.min(Math.max(args.timeoutMs ?? 120_000, 5_000), 600_000);
169
233
  const injectToken = args.injectToken ?? true;
@@ -176,9 +240,9 @@ export async function handleTurnstileSolve(args, collector) {
176
240
  }) || undefined;
177
241
  }
178
242
  if (!siteKey) {
179
- return toErrorResponse('turnstile_solve', new Error('Could not detect Turnstile siteKey. Provide it manually or ensure the page has a Turnstile widget.'));
243
+ return toErrorResponse('widget_challenge_solve', new Error('Could not detect the widget siteKey. Provide it manually or ensure the page exposes a site key.'));
180
244
  }
181
- if (provider === 'hook') {
245
+ if (mode === 'hook') {
182
246
  const hookTimeoutMs = Math.min(timeoutMs, 30_000);
183
247
  const token = await page.evaluate((hookTimeout) => {
184
248
  return new Promise((resolve, reject) => {
@@ -195,7 +259,7 @@ export async function handleTurnstileSolve(args, collector) {
195
259
  }
196
260
  else {
197
261
  clearTimeout(timeout);
198
- reject(new Error('No turnstile callbacks found. Try provider: "2captcha" instead.'));
262
+ reject(new Error('No widget callbacks found. Try external_service mode instead.'));
199
263
  }
200
264
  });
201
265
  }, hookTimeoutMs).catch(() => null);
@@ -204,29 +268,31 @@ export async function handleTurnstileSolve(args, collector) {
204
268
  success: true,
205
269
  token,
206
270
  method: 'hook',
271
+ challengeType: 'widget',
207
272
  siteKey,
208
273
  });
209
274
  }
210
275
  }
211
- if (provider === 'manual') {
276
+ if (mode === 'manual') {
212
277
  return toTextResponse({
213
278
  success: true,
214
279
  mode: 'manual',
280
+ challengeType: 'widget',
215
281
  siteKey,
216
282
  pageUrl,
217
- instruction: 'Please complete the Turnstile challenge manually.',
283
+ instruction: 'Please complete the widget challenge manually.',
218
284
  });
219
285
  }
220
- if (provider !== '2captcha') {
221
- return toErrorResponse('turnstile_solve', new Error(`Provider "${provider}" is not yet implemented for Turnstile. ` +
222
- `Currently only "2captcha", "manual", and "hook" are supported.`));
286
+ if (externalService !== '2captcha') {
287
+ return toErrorResponse('widget_challenge_solve', new Error('The selected external solver service is not implemented for this widget flow. ' +
288
+ 'Currently only the configured primary service, manual mode, and hook mode are supported.'));
223
289
  }
224
290
  if (!apiKey) {
225
- return toErrorResponse('turnstile_solve', new Error('API key required'));
291
+ return toErrorResponse('widget_challenge_solve', new Error('External solver credentials are required.'));
226
292
  }
227
293
  try {
228
294
  const result = await solveWith2Captcha(apiKey, {
229
- type: 'turnstile',
295
+ taskKind: 'turnstile',
230
296
  siteKey,
231
297
  pageUrl,
232
298
  }, timeoutMs);
@@ -244,17 +310,18 @@ export async function handleTurnstileSolve(args, collector) {
244
310
  return toTextResponse({
245
311
  success: true,
246
312
  token: result.token,
313
+ challengeType: result.challengeType,
247
314
  siteKey,
248
- provider: result.provider,
315
+ mode: result.mode,
249
316
  durationMs: result.durationMs,
250
317
  injected: injectToken,
251
318
  });
252
319
  }
253
320
  catch (error) {
254
- return toErrorResponse('turnstile_solve', error, {
321
+ return toErrorResponse('widget_challenge_solve', error, {
255
322
  siteKey,
256
- provider,
257
- suggestion: 'Try provider: "manual" or provider: "hook".',
323
+ mode,
324
+ suggestion: 'Try manual mode or hook mode.',
258
325
  });
259
326
  }
260
327
  }
@@ -23,7 +23,6 @@ export class PageNavigationHandlers {
23
23
  text: JSON.stringify({
24
24
  success: true,
25
25
  driver: 'camoufox',
26
- captcha_detected: false,
27
26
  url: page.url(),
28
27
  title: await page.title(),
29
28
  network_monitoring: {
@@ -51,7 +50,6 @@ export class PageNavigationHandlers {
51
50
  type: 'text',
52
51
  text: JSON.stringify({
53
52
  success: true,
54
- captcha_detected: false,
55
53
  url: currentUrl,
56
54
  title,
57
55
  network_monitoring: {
@@ -394,6 +394,6 @@ export declare class BrowserToolHandlers {
394
394
  handleHumanScroll(args: Record<string, unknown>): Promise<unknown>;
395
395
  handleHumanTyping(args: Record<string, unknown>): Promise<unknown>;
396
396
  handleCaptchaVisionSolve(args: Record<string, unknown>): Promise<unknown>;
397
- handleTurnstileSolve(args: Record<string, unknown>): Promise<unknown>;
397
+ handleWidgetChallengeSolve(args: Record<string, unknown>): Promise<unknown>;
398
398
  }
399
399
  export { BrowserControlHandlers, CamoufoxBrowserHandlers, PageNavigationHandlers, PageInteractionHandlers, PageEvaluationHandlers, PageDataHandlers, DOMQueryHandlers, DOMStyleHandlers, DOMSearchHandlers, ConsoleHandlers, ScriptManagementHandlers, CaptchaHandlers, StealthInjectionHandlers, FrameworkStateHandlers, IndexedDBDumpHandlers, DetailedDataHandlers };
@@ -20,7 +20,7 @@ import { IndexedDBDumpHandlers } from '../../domains/browser/handlers/indexeddb-
20
20
  import { DetailedDataHandlers } from '../../domains/browser/handlers/detailed-data.js';
21
21
  import { initializeBrowserHandlerModules } from '../../domains/browser/handlers/facade-initializer.js';
22
22
  import { handleHumanMouse, handleHumanScroll, handleHumanTyping, } from '../../domains/browser/handlers/human-behavior.js';
23
- import { handleCaptchaVisionSolve, handleTurnstileSolve, } from '../../domains/browser/handlers/captcha-solver.js';
23
+ import { handleCaptchaVisionSolve, handleWidgetChallengeSolve, } from '../../domains/browser/handlers/captcha-solver.js';
24
24
  import { handleCamoufoxLaunchFlow, handleCamoufoxNavigateFlow, } from '../../domains/browser/handlers/camoufox-flow.js';
25
25
  export class BrowserToolHandlers {
26
26
  collector;
@@ -381,8 +381,8 @@ export class BrowserToolHandlers {
381
381
  async handleCaptchaVisionSolve(args) {
382
382
  return handleCaptchaVisionSolve(args, this.collector);
383
383
  }
384
- async handleTurnstileSolve(args) {
385
- return handleTurnstileSolve(args, this.collector);
384
+ async handleWidgetChallengeSolve(args) {
385
+ return handleWidgetChallengeSolve(args, this.collector);
386
386
  }
387
387
  }
388
388
  export { BrowserControlHandlers, CamoufoxBrowserHandlers, PageNavigationHandlers, PageInteractionHandlers, PageEvaluationHandlers, PageDataHandlers, DOMQueryHandlers, DOMStyleHandlers, DOMSearchHandlers, ConsoleHandlers, ScriptManagementHandlers, CaptchaHandlers, StealthInjectionHandlers, FrameworkStateHandlers, IndexedDBDumpHandlers, DetailedDataHandlers };
@@ -79,7 +79,7 @@ const manifest = {
79
79
  { tool: t('human_scroll'), domain: DOMAIN, bind: b((h, a) => h.handleHumanScroll(a)) },
80
80
  { tool: t('human_typing'), domain: DOMAIN, bind: b((h, a) => h.handleHumanTyping(a)) },
81
81
  { tool: t('captcha_vision_solve'), domain: DOMAIN, bind: b((h, a) => h.handleCaptchaVisionSolve(a)) },
82
- { tool: t('turnstile_solve'), domain: DOMAIN, bind: b((h, a) => h.handleTurnstileSolve(a)) },
82
+ { tool: t('widget_challenge_solve'), domain: DOMAIN, bind: b((h, a) => h.handleWidgetChallengeSolve(a)) },
83
83
  ],
84
84
  };
85
85
  export default manifest;
@@ -1,6 +1,7 @@
1
1
  export { CodeAnalyzer } from '../../../modules/analyzer/CodeAnalyzer.js';
2
2
  export { CamoufoxBrowserManager } from '../../../modules/browser/CamoufoxBrowserManager.js';
3
3
  export { AICaptchaDetector } from '../../../modules/captcha/AICaptchaDetector.js';
4
+ export type { CaptchaType, CaptchaProviderHint, CaptchaDetectionResult, AICaptchaDetectionResult, CaptchaDetectionConfig, } from '../../../modules/captcha/types.js';
4
5
  export { CodeCollector } from '../../../modules/collector/CodeCollector.js';
5
6
  export { DOMInspector } from '../../../modules/collector/DOMInspector.js';
6
7
  export { PageController } from '../../../modules/collector/PageController.js';
@@ -28,98 +28,98 @@ export const CRYPTO_KEYWORDS = [
28
28
  'aes',
29
29
  'rsa',
30
30
  ];
31
- const CRYPTO_TEST_WORKER_SCRIPT = `
32
- const __bootstrap = async () => {
33
- const [workerThreads, vm, perfHooks] = await Promise.all([
34
- import('node:worker_threads'),
35
- import('node:vm'),
36
- import('node:perf_hooks'),
37
- ]);
38
-
39
- const parentPort = workerThreads.parentPort;
40
- const performance = perfHooks.performance;
41
-
42
- if (!parentPort) {
43
- throw new Error('worker parentPort is unavailable');
44
- }
45
-
46
- function normalizeOutput(value) {
47
- if (value === undefined) return '__undefined__';
48
- if (value === null) return 'null';
49
- if (typeof value === 'string') return value;
50
- if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value);
51
- try { return JSON.stringify(value); } catch { return String(value); }
52
- }
53
-
54
- parentPort.on('message', async (msg) => {
55
- const { jobId, payload } = msg;
56
- try {
57
- const { code, functionName, testInputs } = payload;
58
- const sandbox = {
59
- console: { log() {}, warn() {}, error() {} },
60
- Buffer,
61
- TextEncoder,
62
- TextDecoder,
63
- atob: (v) => Buffer.from(String(v), 'base64').toString('binary'),
64
- btoa: (v) => Buffer.from(String(v), 'binary').toString('base64'),
65
- };
66
- sandbox.globalThis = sandbox;
67
- const context = vm.createContext(sandbox);
68
-
69
- const isValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(functionName);
70
- const bindCode = isValidIdentifier
71
- ? "\\n;globalThis.__targetFn = (typeof " + functionName + " !== 'undefined' ? " + functionName + " : globalThis[" + JSON.stringify(functionName) + "]);"
72
- : "\\n;globalThis.__targetFn = globalThis[" + JSON.stringify(functionName) + "];";
73
-
74
- const script = new vm.Script(code + bindCode, { timeout: 5000 });
75
- script.runInContext(context, { timeout: 5000 });
76
-
77
- const targetFn = context.__targetFn;
78
- if (typeof targetFn !== 'function') {
79
- throw new Error("Function not found or not callable: " + functionName);
80
- }
81
-
82
- const rows = [];
83
- for (const input of testInputs) {
84
- const started = performance.now();
85
- try {
86
- const raw = targetFn(input);
87
- const resolved = raw && typeof raw.then === 'function' ? await raw : raw;
88
- rows.push({
89
- input,
90
- output: normalizeOutput(resolved),
91
- duration: Number((performance.now() - started).toFixed(3)),
92
- });
93
- } catch (err) {
94
- rows.push({
95
- input,
96
- output: '',
97
- error: err && err.message ? err.message : String(err),
98
- duration: Number((performance.now() - started).toFixed(3)),
99
- });
100
- }
101
- }
102
-
103
- parentPort.postMessage({ jobId, ok: true, result: { ok: true, results: rows } });
104
- } catch (error) {
105
- parentPort.postMessage({
106
- jobId,
107
- ok: true,
108
- result: {
109
- ok: false,
110
- error: error && error.message ? error.message : String(error),
111
- results: [],
112
- },
113
- });
114
- }
115
- });
116
- };
117
-
118
- __bootstrap().catch((error) => {
119
- if (typeof console !== 'undefined' && typeof console.error === 'function') {
120
- console.error('crypto harness worker bootstrap failed:', error && error.message ? error.message : String(error));
121
- }
122
- });
31
+ const CRYPTO_TEST_WORKER_SCRIPT = `
32
+ const __bootstrap = async () => {
33
+ const [workerThreads, vm, perfHooks] = await Promise.all([
34
+ import('node:worker_threads'),
35
+ import('node:vm'),
36
+ import('node:perf_hooks'),
37
+ ]);
38
+
39
+ const parentPort = workerThreads.parentPort;
40
+ const performance = perfHooks.performance;
41
+
42
+ if (!parentPort) {
43
+ throw new Error('worker parentPort is unavailable');
44
+ }
45
+
46
+ function normalizeOutput(value) {
47
+ if (value === undefined) return '__undefined__';
48
+ if (value === null) return 'null';
49
+ if (typeof value === 'string') return value;
50
+ if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value);
51
+ try { return JSON.stringify(value); } catch { return String(value); }
52
+ }
53
+
54
+ parentPort.on('message', async (msg) => {
55
+ const { jobId, payload } = msg;
56
+ try {
57
+ const { code, functionName, testInputs } = payload;
58
+ const sandbox = {
59
+ console: { log() {}, warn() {}, error() {} },
60
+ Buffer,
61
+ TextEncoder,
62
+ TextDecoder,
63
+ atob: (v) => Buffer.from(String(v), 'base64').toString('binary'),
64
+ btoa: (v) => Buffer.from(String(v), 'binary').toString('base64'),
65
+ };
66
+ sandbox.globalThis = sandbox;
67
+ const context = vm.createContext(sandbox);
68
+
69
+ const isValidIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(functionName);
70
+ const bindCode = isValidIdentifier
71
+ ? "\\n;globalThis.__targetFn = (typeof " + functionName + " !== 'undefined' ? " + functionName + " : globalThis[" + JSON.stringify(functionName) + "]);"
72
+ : "\\n;globalThis.__targetFn = globalThis[" + JSON.stringify(functionName) + "];";
73
+
74
+ const script = new vm.Script(code + bindCode, { timeout: 5000 });
75
+ script.runInContext(context, { timeout: 5000 });
76
+
77
+ const targetFn = context.__targetFn;
78
+ if (typeof targetFn !== 'function') {
79
+ throw new Error("Function not found or not callable: " + functionName);
80
+ }
81
+
82
+ const rows = [];
83
+ for (const input of testInputs) {
84
+ const started = performance.now();
85
+ try {
86
+ const raw = targetFn(input);
87
+ const resolved = raw && typeof raw.then === 'function' ? await raw : raw;
88
+ rows.push({
89
+ input,
90
+ output: normalizeOutput(resolved),
91
+ duration: Number((performance.now() - started).toFixed(3)),
92
+ });
93
+ } catch (err) {
94
+ rows.push({
95
+ input,
96
+ output: '',
97
+ error: err && err.message ? err.message : String(err),
98
+ duration: Number((performance.now() - started).toFixed(3)),
99
+ });
100
+ }
101
+ }
102
+
103
+ parentPort.postMessage({ jobId, ok: true, result: { ok: true, results: rows } });
104
+ } catch (error) {
105
+ parentPort.postMessage({
106
+ jobId,
107
+ ok: true,
108
+ result: {
109
+ ok: false,
110
+ error: error && error.message ? error.message : String(error),
111
+ results: [],
112
+ },
113
+ });
114
+ }
115
+ });
116
+ };
117
+
118
+ __bootstrap().catch((error) => {
119
+ if (typeof console !== 'undefined' && typeof console.error === 'function') {
120
+ console.error('crypto harness worker bootstrap failed:', error && error.message ? error.message : String(error));
121
+ }
122
+ });
123
123
  `;
124
124
  export class TransformToolHandlersBase {
125
125
  collector;
@@ -333,16 +333,16 @@ export class TransformToolHandlersBase {
333
333
  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value);
334
334
  }
335
335
  buildCryptoPolyfills() {
336
- return `
337
- const __textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;
338
- const __textDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
339
-
340
- if (typeof globalThis.atob === 'undefined') {
341
- globalThis.atob = (value) => Buffer.from(String(value), 'base64').toString('binary');
342
- }
343
- if (typeof globalThis.btoa === 'undefined') {
344
- globalThis.btoa = (value) => Buffer.from(String(value), 'binary').toString('base64');
345
- }
336
+ return `
337
+ const __textEncoder = typeof TextEncoder !== 'undefined' ? new TextEncoder() : null;
338
+ const __textDecoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
339
+
340
+ if (typeof globalThis.atob === 'undefined') {
341
+ globalThis.atob = (value) => Buffer.from(String(value), 'base64').toString('binary');
342
+ }
343
+ if (typeof globalThis.btoa === 'undefined') {
344
+ globalThis.btoa = (value) => Buffer.from(String(value), 'binary').toString('base64');
345
+ }
346
346
  `.trim();
347
347
  }
348
348
  async runCryptoHarness(code, functionName, testInputs) {