brave-real-browser-mcp-server 2.41.11 → 2.41.12

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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.41.11",
3
+ "version": "2.41.12",
4
4
  "description": "MCP Server for Brave Real Browser - Puppeteer with Brave Browser, Stealth Mode, Ad Blocker, and Turnstile Auto-Solver for undetectable web automation.",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/esm/index.mjs",
@@ -74,7 +74,7 @@
74
74
  "license": "ISC",
75
75
  "dependencies": {
76
76
  "@modelcontextprotocol/sdk": "^1.25.3",
77
- "brave-real-puppeteer-core": "^24.37.1-brave.10",
77
+ "brave-real-puppeteer-core": "^24.37.1-brave.11",
78
78
  "ghost-cursor": "^1.4.2",
79
79
  "puppeteer-extra": "^3.3.6",
80
80
  "puppeteer-extra-plugin-stealth": "^2.11.2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-blocker",
3
- "version": "1.17.11",
3
+ "version": "1.17.12",
4
4
  "description": "Advanced uBlock Origin management and stealth features for Brave Real Browser",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -64,7 +64,7 @@
64
64
  "@types/adm-zip": "^0.5.5",
65
65
  "@types/fs-extra": "^11.0.4",
66
66
  "@types/node": "^20.0.0",
67
- "brave-real-puppeteer-core": "^24.37.1-brave.10",
67
+ "brave-real-puppeteer-core": "^24.37.1-brave.11",
68
68
  "mocha": "^10.4.0",
69
69
  "puppeteer-core": ">=24.0.0",
70
70
  "sinon": "^17.0.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-launcher",
3
- "version": "1.23.11",
3
+ "version": "1.23.12",
4
4
  "description": "Launch Brave Browser with ease from node. Based on chrome-launcher with Brave-specific support.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -54,7 +54,7 @@
54
54
  "typescript": "^5.0.0"
55
55
  },
56
56
  "dependencies": {
57
- "brave-real-blocker": "^1.17.11",
57
+ "brave-real-blocker": "^1.17.12",
58
58
  "escape-string-regexp": "^5.0.0",
59
59
  "is-wsl": "^3.1.0",
60
60
  "which": "^6.0.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-playwright-core",
3
- "version": "1.59.11",
3
+ "version": "1.59.12",
4
4
  "description": "Brave-optimized Playwright Core (v1.57.0) with comprehensive stealth patches and error stack sanitization",
5
5
  "keywords": [
6
6
  "playwright",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-puppeteer-core",
3
- "version": "24.37.1-brave.10",
3
+ "version": "24.37.1-brave.11",
4
4
  "description": "🦁 Brave Real-World Optimized Puppeteer & Playwright Core with 1-5ms ultra-fast timing, 50+ professional stealth features, intelligent browser auto-detection, and 100% bot detection bypass. Features cross-platform Brave browser integration, comprehensive anti-detection, and breakthrough performance improvements.",
5
5
  "keywords": [
6
6
  "automation",
@@ -134,13 +134,13 @@
134
134
  "test-version": "node ./scripts/test-version-management.js"
135
135
  },
136
136
  "dependencies": {
137
- "brave-real-launcher": "^1.23.11",
137
+ "brave-real-launcher": "^1.23.12",
138
138
  "get-east-asian-width": "^1.4.0",
139
139
  "yargs": "^18.0.0"
140
140
  },
141
141
  "optionalDependencies": {
142
- "playwright-core": "^1.58.2",
143
- "puppeteer-core": "^24.37.2"
142
+ "playwright-core": "^1.58.1",
143
+ "puppeteer-core": "^24.37.1"
144
144
  },
145
145
  "devDependencies": {
146
146
  "test": "^3.3.0"
@@ -12,38 +12,38 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
12
12
  const text = document.getText();
13
13
  const lines = text.split('\n');
14
14
  const diagnostics = [];
15
-
15
+
16
16
  // State tracking
17
17
  let browserInitialized = false;
18
18
  let browserClosed = false;
19
19
  let currentPage = null;
20
20
  let networkRecording = false;
21
-
21
+
22
22
  // Tool categories
23
23
  const browserRequiredTools = [
24
24
  'navigate', 'get_content', 'wait', 'click', 'type', 'solve_captcha',
25
25
  'random_scroll', 'find_element', 'save_content_as_markdown', 'search_regex',
26
26
  'extract_json', 'scrape_meta_tags', 'press_key', 'deep_analysis',
27
27
  'network_recorder', 'link_harvester', 'cookie_manager', 'iframe_handler',
28
- 'stream_extractor', 'js_scrape', 'execute_js', 'player_api_hook', 'form_automator',
28
+ 'stream_extractor', 'js_scrape', 'execute_js', 'player_api_hook',
29
29
  'redirect_tracer', 'file_downloader'
30
30
  ];
31
-
31
+
32
32
  const pageRequiredTools = [
33
33
  'get_content', 'click', 'type', 'random_scroll', 'find_element',
34
34
  'save_content_as_markdown', 'search_regex', 'extract_json', 'scrape_meta_tags',
35
35
  'deep_analysis', 'link_harvester', 'iframe_handler', 'stream_extractor',
36
- 'js_scrape', 'execute_js', 'player_api_hook', 'form_automator', 'press_key'
36
+ 'js_scrape', 'execute_js', 'player_api_hook', 'press_key'
37
37
  ];
38
-
38
+
39
39
  // Parse all tool calls with their parameters
40
40
  const toolCalls = parseAllToolCalls(text, lines, tools);
41
-
41
+
42
42
  for (const call of toolCalls) {
43
43
  if (diagnostics.length >= maxDiagnostics) break;
44
-
44
+
45
45
  const tool = tools.find(t => t.name === call.name);
46
-
46
+
47
47
  // 1. Unknown tool check
48
48
  if (!tool) {
49
49
  diagnostics.push({
@@ -56,7 +56,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
56
56
  });
57
57
  continue;
58
58
  }
59
-
59
+
60
60
  // 2. Browser state tracking
61
61
  if (call.name === 'browser_init') {
62
62
  if (browserInitialized && !browserClosed) {
@@ -71,7 +71,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
71
71
  browserInitialized = true;
72
72
  browserClosed = false;
73
73
  }
74
-
74
+
75
75
  if (call.name === 'browser_close') {
76
76
  if (!browserInitialized) {
77
77
  diagnostics.push({
@@ -85,11 +85,11 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
85
85
  browserClosed = true;
86
86
  currentPage = null;
87
87
  }
88
-
88
+
89
89
  if (call.name === 'navigate') {
90
90
  currentPage = call.params?.url || 'page';
91
91
  }
92
-
92
+
93
93
  // 3. Browser required check
94
94
  if (browserRequiredTools.includes(call.name) && !browserInitialized) {
95
95
  diagnostics.push({
@@ -100,7 +100,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
100
100
  code: 'browser-not-init'
101
101
  });
102
102
  }
103
-
103
+
104
104
  // 4. Browser closed check
105
105
  if (browserRequiredTools.includes(call.name) && browserClosed) {
106
106
  diagnostics.push({
@@ -111,7 +111,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
111
111
  code: 'browser-closed'
112
112
  });
113
113
  }
114
-
114
+
115
115
  // 5. Page required check (navigate must be called first)
116
116
  if (pageRequiredTools.includes(call.name) && !currentPage && browserInitialized) {
117
117
  diagnostics.push({
@@ -122,7 +122,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
122
122
  code: 'no-page-loaded'
123
123
  });
124
124
  }
125
-
125
+
126
126
  // 6. Network recorder state
127
127
  if (call.name === 'network_recorder') {
128
128
  if (call.params?.action === 'start') {
@@ -140,7 +140,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
140
140
  networkRecording = false;
141
141
  }
142
142
  }
143
-
143
+
144
144
  // 7. Required parameters check
145
145
  if (tool.inputSchema?.required) {
146
146
  for (const reqParam of tool.inputSchema.required) {
@@ -156,12 +156,12 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
156
156
  }
157
157
  }
158
158
  }
159
-
159
+
160
160
  // 8. Parameter validation
161
161
  if (call.params && tool.inputSchema?.properties) {
162
162
  for (const [paramName, paramValue] of Object.entries(call.params)) {
163
163
  const schema = tool.inputSchema.properties[paramName];
164
-
164
+
165
165
  // Unknown parameter
166
166
  if (!schema) {
167
167
  diagnostics.push({
@@ -174,7 +174,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
174
174
  });
175
175
  continue;
176
176
  }
177
-
177
+
178
178
  // Type validation
179
179
  const typeError = validateParamType(paramValue, schema, paramName);
180
180
  if (typeError) {
@@ -186,7 +186,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
186
186
  code: 'type-error'
187
187
  });
188
188
  }
189
-
189
+
190
190
  // Enum validation
191
191
  if (schema.enum && !schema.enum.includes(paramValue)) {
192
192
  diagnostics.push({
@@ -197,7 +197,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
197
197
  code: 'invalid-enum'
198
198
  });
199
199
  }
200
-
200
+
201
201
  // URL validation
202
202
  if (paramName === 'url' && typeof paramValue === 'string') {
203
203
  const urlError = validateUrl(paramValue);
@@ -211,7 +211,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
211
211
  });
212
212
  }
213
213
  }
214
-
214
+
215
215
  // Selector validation
216
216
  if (paramName === 'selector' && typeof paramValue === 'string') {
217
217
  const selectorError = validateSelector(paramValue);
@@ -225,7 +225,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
225
225
  });
226
226
  }
227
227
  }
228
-
228
+
229
229
  // Timeout validation
230
230
  if (paramName === 'timeout' && typeof paramValue === 'number') {
231
231
  if (paramValue < 0) {
@@ -246,7 +246,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
246
246
  });
247
247
  }
248
248
  }
249
-
249
+
250
250
  // Delay validation
251
251
  if (paramName === 'delay' && typeof paramValue === 'number') {
252
252
  if (paramValue < 0) {
@@ -269,7 +269,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
269
269
  }
270
270
  }
271
271
  }
272
-
272
+
273
273
  // 9. Tool-specific validations
274
274
  if (call.name === 'type' && call.params?.text === '') {
275
275
  diagnostics.push({
@@ -280,7 +280,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
280
280
  code: 'empty-text'
281
281
  });
282
282
  }
283
-
283
+
284
284
  if (call.name === 'wait' && call.params?.type === 'timeout') {
285
285
  const waitValue = parseInt(call.params?.value);
286
286
  if (waitValue > 30000) {
@@ -293,7 +293,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
293
293
  });
294
294
  }
295
295
  }
296
-
296
+
297
297
  if (call.name === 'execute_js' && call.params?.code) {
298
298
  const jsCode = call.params.code;
299
299
  if (jsCode.includes('eval(') || jsCode.includes('Function(')) {
@@ -306,7 +306,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
306
306
  });
307
307
  }
308
308
  }
309
-
309
+
310
310
  if (call.name === 'file_downloader' && call.params?.directory) {
311
311
  const dir = call.params.directory;
312
312
  if (dir.includes('..') || dir.startsWith('/')) {
@@ -320,7 +320,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
320
320
  }
321
321
  }
322
322
  }
323
-
323
+
324
324
  // 10. Document-level checks
325
325
  if (browserInitialized && !browserClosed) {
326
326
  diagnostics.push({
@@ -331,7 +331,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
331
331
  code: 'missing-cleanup'
332
332
  });
333
333
  }
334
-
334
+
335
335
  if (networkRecording) {
336
336
  diagnostics.push({
337
337
  severity: DiagnosticSeverity.Information,
@@ -341,7 +341,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
341
341
  code: 'network-not-stopped'
342
342
  });
343
343
  }
344
-
344
+
345
345
  // 11. Check for duplicate tool calls (potential bugs)
346
346
  const navigateCalls = toolCalls.filter(c => c.name === 'navigate');
347
347
  if (navigateCalls.length > 5) {
@@ -353,7 +353,7 @@ function getDiagnostics(document, tools, lang, maxDiagnostics = 100) {
353
353
  code: 'many-navigates'
354
354
  });
355
355
  }
356
-
356
+
357
357
  return diagnostics.slice(0, maxDiagnostics);
358
358
  }
359
359
 
@@ -364,15 +364,15 @@ function parseAllToolCalls(text, lines, tools) {
364
364
  const calls = [];
365
365
  const toolNames = tools.map(t => t.name);
366
366
  const toolRegex = new RegExp(`(${toolNames.join('|')})\\s*\\(\\s*(\\{[^}]*\\})?`, 'g');
367
-
367
+
368
368
  for (let lineNum = 0; lineNum < lines.length; lineNum++) {
369
369
  const line = lines[lineNum];
370
370
  let match;
371
-
371
+
372
372
  while ((match = toolRegex.exec(line)) !== null) {
373
373
  const name = match[1];
374
374
  const paramsStr = match[2];
375
-
375
+
376
376
  calls.push({
377
377
  name,
378
378
  params: paramsStr ? parseParams(paramsStr) : null,
@@ -383,10 +383,10 @@ function parseAllToolCalls(text, lines, tools) {
383
383
  }
384
384
  });
385
385
  }
386
-
386
+
387
387
  toolRegex.lastIndex = 0;
388
388
  }
389
-
389
+
390
390
  return calls;
391
391
  }
392
392
 
@@ -401,7 +401,7 @@ function parseParams(paramsStr) {
401
401
  .replace(/(\w+)\s*:/g, '"$1":')
402
402
  .replace(/,\s*}/g, '}')
403
403
  .replace(/\n/g, ' ');
404
-
404
+
405
405
  return JSON.parse(jsonStr);
406
406
  } catch (e) {
407
407
  return null;
@@ -414,7 +414,7 @@ function parseParams(paramsStr) {
414
414
  function validateParamType(value, schema, paramName) {
415
415
  const expectedType = schema.type;
416
416
  const actualType = typeof value;
417
-
417
+
418
418
  if (expectedType === 'string' && actualType !== 'string') {
419
419
  return `Parameter ${paramName}: Expected string, got ${actualType}`;
420
420
  }
@@ -430,7 +430,7 @@ function validateParamType(value, schema, paramName) {
430
430
  if (expectedType === 'object' && (actualType !== 'object' || value === null || Array.isArray(value))) {
431
431
  return `Parameter ${paramName}: Expected object, got ${actualType}`;
432
432
  }
433
-
433
+
434
434
  return null;
435
435
  }
436
436
 
@@ -441,7 +441,7 @@ function validateUrl(url) {
441
441
  if (!url || url.trim() === '') {
442
442
  return 'URL cannot be empty';
443
443
  }
444
-
444
+
445
445
  try {
446
446
  const parsed = new URL(url);
447
447
  if (!['http:', 'https:'].includes(parsed.protocol)) {
@@ -450,7 +450,7 @@ function validateUrl(url) {
450
450
  } catch (e) {
451
451
  return 'Invalid URL format. Example: https://example.com';
452
452
  }
453
-
453
+
454
454
  return null;
455
455
  }
456
456
 
@@ -461,20 +461,20 @@ function validateSelector(selector) {
461
461
  if (!selector || selector.trim() === '') {
462
462
  return 'Selector cannot be empty';
463
463
  }
464
-
464
+
465
465
  // Check for common mistakes
466
466
  if (selector.includes(' ')) {
467
467
  return 'Selector contains multiple consecutive spaces';
468
468
  }
469
-
469
+
470
470
  if (/^[0-9]/.test(selector) && !selector.startsWith('[')) {
471
471
  return 'Selector cannot start with a number (unless in attribute selector)';
472
472
  }
473
-
473
+
474
474
  // Check balanced brackets
475
475
  const brackets = { '[': ']', '(': ')' };
476
476
  const stack = [];
477
-
477
+
478
478
  for (const char of selector) {
479
479
  if (char in brackets) {
480
480
  stack.push(brackets[char]);
@@ -484,16 +484,16 @@ function validateSelector(selector) {
484
484
  }
485
485
  }
486
486
  }
487
-
487
+
488
488
  if (stack.length > 0) {
489
489
  return 'Unclosed bracket in selector';
490
490
  }
491
-
491
+
492
492
  // Check for empty attribute selector
493
493
  if (selector.includes('[]')) {
494
494
  return 'Empty attribute selector []';
495
495
  }
496
-
496
+
497
497
  return null;
498
498
  }
499
499
 
@@ -502,7 +502,7 @@ function validateSelector(selector) {
502
502
  */
503
503
  function findSimilarTools(name, tools) {
504
504
  const lowerName = name.toLowerCase();
505
-
505
+
506
506
  return tools
507
507
  .map(t => ({
508
508
  name: t.name,
@@ -520,14 +520,14 @@ function findSimilarTools(name, tools) {
520
520
  function calculateSimilarity(s1, s2) {
521
521
  const longer = s1.length > s2.length ? s1 : s2;
522
522
  const shorter = s1.length > s2.length ? s2 : s1;
523
-
523
+
524
524
  if (longer.length === 0) return 1.0;
525
-
525
+
526
526
  // Quick check for common prefix
527
527
  if (longer.startsWith(shorter) || shorter.startsWith(longer.substring(0, 3))) {
528
528
  return 0.8;
529
529
  }
530
-
530
+
531
531
  // Levenshtein distance
532
532
  const costs = [];
533
533
  for (let i = 0; i <= s1.length; i++) {
@@ -546,7 +546,7 @@ function calculateSimilarity(s1, s2) {
546
546
  }
547
547
  if (i > 0) costs[s2.length] = lastValue;
548
548
  }
549
-
549
+
550
550
  return (longer.length - costs[s2.length]) / longer.length;
551
551
  }
552
552
 
@@ -21,7 +21,7 @@ module.exports = {
21
21
  click: { label: 'Click Element', detail: 'Click with human-like behavior', documentation: 'Clicks on an element using ghost-cursor.', parameters: { selector: 'Element selector', humanLike: 'Use realistic movement', clickCount: 'Number of clicks', delay: 'Click delay' } },
22
22
  type: { label: 'Type Text', detail: 'Type into input field', documentation: 'Types text with realistic keystroke delays.', parameters: { selector: 'Input selector', text: 'Text to type', delay: 'Keystroke delay', clear: 'Clear first' } },
23
23
  browser_close: { label: 'Close Browser', detail: 'Close and cleanup', documentation: 'Closes the browser.', parameters: { force: 'Force close' } },
24
- solve_captcha: { label: 'Solve CAPTCHA', detail: 'Auto-solve CAPTCHAs', documentation: 'Automatically solves Turnstile, reCAPTCHA, hCaptcha.', parameters: { type: 'CAPTCHA type', timeout: 'Max solve time' } },
24
+ solve_captcha: { label: 'Solve CAPTCHA + Form', detail: 'Auto-solve + Fill forms', documentation: 'Solves CAPTCHAs and fills forms with AI field matching.', parameters: { type: 'CAPTCHA type', timeout: 'Max time', formData: 'Form data', submit: 'Submit form' } },
25
25
  random_scroll: { label: 'Random Scroll', detail: 'Human-like scrolling', documentation: 'Scrolls with human-like behavior.', parameters: { direction: 'Scroll direction', amount: 'Scroll amount', smooth: 'Smooth scrolling' } },
26
26
  find_element: { label: 'Find Element', detail: 'Locate elements', documentation: 'Finds elements by selector/xpath/text.', parameters: { selector: 'CSS selector', xpath: 'XPath', text: 'Text content', multiple: 'Return all' } },
27
27
  save_content_as_markdown: { label: 'Save as Markdown', detail: 'Export to MD', documentation: 'Saves page as Markdown file.', parameters: { filename: 'Output filename', selector: 'Content selector', includeImages: 'Include images', includeMeta: 'Include meta' } },
@@ -40,8 +40,7 @@ module.exports = {
40
40
  stream_extractor: { label: 'Extract Streams', detail: 'Get video/audio', documentation: 'Extracts stream URLs.', parameters: { types: 'Stream types', quality: 'Quality' } },
41
41
  js_scrape: { label: 'JS Scrape', detail: 'Scrape JS content', documentation: 'Scrapes JS-rendered content.', parameters: { selector: 'CSS selector', waitForJS: 'Wait for JS', timeout: 'Max wait' } },
42
42
  execute_js: { label: 'Execute JS', detail: 'Run JavaScript', documentation: 'Executes custom JavaScript.', parameters: { code: 'JS code', returnValue: 'Return result' } },
43
- player_api_hook: { label: 'Player API Hook', detail: 'Video player control', documentation: 'Hooks into video players.', parameters: { playerType: 'Player type', action: 'Action' } },
44
- form_automator: { label: 'Automate Form', detail: 'Fill and submit', documentation: 'Auto-fills and submits forms.', parameters: { selector: 'Form selector', data: 'Form data', submit: 'Submit', humanLike: 'Human delays' } }
43
+ player_api_hook: { label: 'Player API Hook', detail: 'Video player control', documentation: 'Hooks into video players.', parameters: { playerType: 'Player type', action: 'Action' } }
45
44
  },
46
45
  diagnostics: {
47
46
  browserNotInit: 'Browser not initialized. Call browser_init first.',
@@ -11,7 +11,7 @@ module.exports = {
11
11
  click: { label: 'क्लिक करें', detail: 'मानव-जैसा क्लिक', documentation: 'ghost-cursor से क्लिक करता है।', parameters: { selector: 'एलीमेंट selector', humanLike: 'वास्तविक मूवमेंट', clickCount: 'क्लिक संख्या', delay: 'देरी' } },
12
12
  type: { label: 'टाइप करें', detail: 'इनपुट में टाइप', documentation: 'वास्तविक कीस्ट्रोक के साथ टाइप करता है।', parameters: { selector: 'इनपुट selector', text: 'टेक्स्ट', delay: 'कीस्ट्रोक देरी', clear: 'पहले साफ करें' } },
13
13
  browser_close: { label: 'ब्राउज़र बंद करें', detail: 'बंद और क्लीनअप', documentation: 'ब्राउज़र बंद करता है।', parameters: { force: 'फोर्स क्लोज़' } },
14
- solve_captcha: { label: 'CAPTCHA हल करें', detail: 'ऑटो-सॉल्व', documentation: 'Turnstile, reCAPTCHA हल करता है।', parameters: { type: 'CAPTCHA प्रकार', timeout: 'अधिकतम समय' } },
14
+ solve_captcha: { label: 'CAPTCHA + फॉर्म', detail: 'ऑटो-सॉल्व + फॉर्म भरें', documentation: 'CAPTCHA हल करता है और फॉर्म भी भरता है।', parameters: { type: 'CAPTCHA प्रकार', timeout: 'अधिकतम समय', formData: 'फॉर्म डेटा', submit: 'सबमिट करें' } },
15
15
  random_scroll: { label: 'रैंडम स्क्रॉल', detail: 'मानव-जैसा स्क्रॉल', documentation: 'रैंडम स्क्रॉल करता है।', parameters: { direction: 'दिशा', amount: 'मात्रा', smooth: 'स्मूथ' } },
16
16
  find_element: { label: 'एलीमेंट खोजें', detail: 'पेज पर खोजें', documentation: 'selector/xpath से खोजता है।', parameters: { selector: 'CSS selector', xpath: 'XPath', text: 'टेक्स्ट', multiple: 'सभी लौटाएं' } },
17
17
  save_content_as_markdown: { label: 'MD में सेव', detail: 'Markdown एक्सपोर्ट', documentation: 'Markdown फाइल में सेव करता है।', parameters: { filename: 'फाइलनेम', selector: 'कंटेंट selector', includeImages: 'इमेज शामिल', includeMeta: 'मेटा शामिल' } },
@@ -30,8 +30,7 @@ module.exports = {
30
30
  stream_extractor: { label: 'स्ट्रीम निकालें', detail: 'वीडियो/ऑडियो URL', documentation: 'स्ट्रीम URLs निकालता है।', parameters: { types: 'प्रकार', quality: 'क्वालिटी' } },
31
31
  js_scrape: { label: 'JS स्क्रैप', detail: 'JS कंटेंट', documentation: 'JS-रेंडर्ड कंटेंट।', parameters: { selector: 'selector', waitForJS: 'JS इंतजार', timeout: 'अधिकतम' } },
32
32
  execute_js: { label: 'JS चलाएं', detail: 'JavaScript रन', documentation: 'कस्टम JS चलाता है।', parameters: { code: 'कोड', returnValue: 'रिज़ल्ट लौटाएं' } },
33
- player_api_hook: { label: 'Player Hook', detail: 'वीडियो प्लेयर', documentation: 'प्लेयर में हुक करता है।', parameters: { playerType: 'प्लेयर', action: 'एक्शन' } },
34
- form_automator: { label: 'फॉर्म ऑटोमेट', detail: 'फॉर्म भरें', documentation: 'फॉर्म भरता और सबमिट करता है।', parameters: { selector: 'फॉर्म selector', data: 'डेटा', submit: 'सबमिट', humanLike: 'मानव देरी' } }
33
+ player_api_hook: { label: 'Player Hook', detail: 'वीडियो प्लेयर', documentation: 'प्लेयर में हुक करता है।', parameters: { playerType: 'प्लेयर', action: 'एक्शन' } }
35
34
  },
36
35
  diagnostics: {
37
36
  browserNotInit: 'ब्राउज़र इनिशियलाइज़ नहीं। पहले browser_init करें।',
@@ -240,93 +240,242 @@ const decoders = {
240
240
  * Tool Handlers Object
241
241
  */
242
242
  const handlers = {
243
- // 0. Smart Form Filler (NEW - Universal Form Automation)
244
- async smart_form_filler(params = {}) {
245
- const { page } = requireBrowser();
246
- const {
247
- formData = {},
248
- validateBeforeSubmit = true,
249
- humanLike = true,
250
- submitAfter = false,
251
- submitSelector = 'button[type="submit"], input[type="submit"], .btn-primary',
252
- solveCaptcha = false,
253
- captchaOptions = {}
254
- } = params;
243
+ // ═══════════════════════════════════════════════════════════════
244
+ // HELPER: Full Page Analyzer - Detect ALL inputs on page
245
+ // ═══════════════════════════════════════════════════════════════
246
+ async _analyzeFullPage(page) {
247
+ return await page.evaluate(() => {
248
+ const inputs = [];
249
+ const allInputs = document.querySelectorAll('input, textarea, select');
250
+
251
+ allInputs.forEach((el, index) => {
252
+ if (el.type === 'hidden' || el.offsetParent === null) return;
253
+
254
+ // Find associated label
255
+ let label = '';
256
+ if (el.id) {
257
+ const labelEl = document.querySelector(`label[for="${el.id}"]`);
258
+ if (labelEl) label = labelEl.textContent.trim();
259
+ }
260
+ if (!label) {
261
+ const parent = el.closest('label, .form-group, .field');
262
+ if (parent) label = parent.textContent?.split('\n')[0]?.trim() || '';
263
+ }
264
+
265
+ inputs.push({
266
+ index,
267
+ tag: el.tagName.toLowerCase(),
268
+ type: el.type || 'text',
269
+ name: el.name || '',
270
+ id: el.id || '',
271
+ placeholder: el.placeholder || '',
272
+ label: label,
273
+ required: el.required,
274
+ value: el.value || '',
275
+ selector: el.id ? `#${el.id}` : (el.name ? `[name="${el.name}"]` : `input[type="${el.type}"]:nth-of-type(${index + 1})`)
276
+ });
277
+ });
255
278
 
256
- notifyProgress('smart_form_filler', 'started', '🤖 Universal form automation started');
279
+ // Detect captcha elements
280
+ const captcha = {
281
+ image: document.querySelector('img[src*="captcha"], img[id*="captcha"], .captcha-image')?.src || null,
282
+ input: document.querySelector('input[name*="captcha"], input[id*="captcha"]')?.id || null
283
+ };
257
284
 
258
- try {
259
- // Load universal form analyzer
260
- const formAnalyzer = require('../../lib/universal-form-analyzer');
285
+ // Detect submit button
286
+ const submitBtn = document.querySelector('button[type="submit"], input[type="submit"], button.submit');
261
287
 
262
- // Step 1: Analyze forms on page
263
- notifyProgress('smart_form_filler', 'progress', '🔍 Analyzing page forms...');
264
- const forms = await formAnalyzer.analyzeAllForms(page);
288
+ return {
289
+ inputs,
290
+ captcha,
291
+ submitButton: submitBtn ? (submitBtn.id ? `#${submitBtn.id}` : 'button[type="submit"]') : null,
292
+ totalInputs: inputs.length
293
+ };
294
+ });
295
+ },
265
296
 
266
- if (forms.length === 0) {
267
- return { success: false, error: 'No forms found on page' };
268
- }
297
+ // ═══════════════════════════════════════════════════════════════
298
+ // HELPER: Fill form fields with Sequential Tab Navigation
299
+ // ═══════════════════════════════════════════════════════════════
300
+ async _fillFormFields(page, formData, formSelector, humanLike = true, aiMatch = true) {
301
+ const targetForm = formSelector || 'form';
302
+ const fields = Object.keys(formData || {});
303
+ let filledCount = 0;
304
+ const filledFields = [];
305
+ const unfilledFields = [];
269
306
 
270
- notifyProgress('smart_form_filler', 'progress', `Found ${forms.length} form(s) with ${forms[0].fields.length} fields`);
307
+ // First, analyze the full page
308
+ const pageInfo = await this._analyzeFullPage(page);
309
+ notifyProgress('solve_captcha', 'progress', `🔍 Page analyzed: ${pageInfo.totalInputs} inputs found`);
271
310
 
272
- // Step 2: Fill form with smart field matching
273
- const result = await formAnalyzer.fillForm(page, formData, {
274
- validateBeforeSubmit,
275
- humanLike,
276
- submitAfter: false, // We'll handle submit separately
277
- });
311
+ for (const [field, value] of Object.entries(formData || {})) {
312
+ // Enhanced AI Field Matching - uses pageInfo for better matching
313
+ let bestMatch = null;
314
+ let bestScore = 0;
278
315
 
279
- if (!result.success) {
280
- notifyProgress('smart_form_filler', 'error', result.error);
281
- return result;
316
+ for (const input of pageInfo.inputs) {
317
+ let score = 0;
318
+ const fieldLower = field.toLowerCase();
319
+
320
+ // Exact matches
321
+ if (input.name.toLowerCase() === fieldLower) score = 100;
322
+ else if (input.id.toLowerCase() === fieldLower) score = 95;
323
+ // Partial matches
324
+ else if (input.name.toLowerCase().includes(fieldLower)) score = 80;
325
+ else if (input.id.toLowerCase().includes(fieldLower)) score = 75;
326
+ else if (input.placeholder.toLowerCase().includes(fieldLower)) score = 70;
327
+ else if (input.label.toLowerCase().includes(fieldLower)) score = 65;
328
+ // Type-based matching
329
+ else if (fieldLower.includes('email') && input.type === 'email') score = 60;
330
+ else if (fieldLower.includes('pass') && input.type === 'password') score = 60;
331
+ else if (fieldLower.includes('phone') && input.type === 'tel') score = 60;
332
+
333
+ if (score > bestScore) {
334
+ bestScore = score;
335
+ bestMatch = input;
336
+ }
282
337
  }
283
338
 
284
- notifyProgress('smart_form_filler', 'progress', `✅ Filled ${result.filledFields.length} fields`);
339
+ if (!bestMatch || bestScore < 50) {
340
+ unfilledFields.push(field);
341
+ continue;
342
+ }
285
343
 
286
- // Step 3: Solve CAPTCHA if requested
287
- if (solveCaptcha) {
288
- notifyProgress('smart_form_filler', 'progress', '🔓 Solving CAPTCHA...');
289
- const captchaResult = await handlers.solve_captcha(captchaOptions);
290
-
291
- if (!captchaResult.success) {
292
- return {
293
- success: false,
294
- error: 'CAPTCHA solving failed',
295
- captchaError: captchaResult.error,
296
- filledFields: result.filledFields
297
- };
344
+ try {
345
+ const element = await page.$(bestMatch.selector);
346
+ if (!element) {
347
+ unfilledFields.push(field);
348
+ continue;
298
349
  }
299
350
 
300
- notifyProgress('smart_form_filler', 'progress', `✅ CAPTCHA solved: ${captchaResult.text}`);
351
+ // Focus element first (human-like)
352
+ await element.focus();
353
+ await new Promise(r => setTimeout(r, 100 + Math.random() * 150));
354
+
355
+ if (bestMatch.tag === 'select') {
356
+ // Smart Select
357
+ await page.evaluate((sel, val) => {
358
+ const el = document.querySelector(sel);
359
+ if (!el) return;
360
+ el.value = val;
361
+ if (el.value !== val) {
362
+ for (const opt of el.options) {
363
+ if (opt.text.toLowerCase().includes(val.toLowerCase())) {
364
+ el.value = opt.value;
365
+ break;
366
+ }
367
+ }
368
+ }
369
+ el.dispatchEvent(new Event('change', { bubbles: true }));
370
+ }, bestMatch.selector, String(value));
371
+ } else if (bestMatch.type === 'checkbox' || bestMatch.type === 'radio') {
372
+ if (value) await element.click();
373
+ } else {
374
+ // Text input - clear and type with human behavior
375
+ await element.click({ clickCount: 3 });
376
+ await page.keyboard.press('Backspace');
377
+ await new Promise(r => setTimeout(r, 50));
378
+
379
+ if (humanLike) {
380
+ // Human-like typing with variable delays
381
+ for (let i = 0; i < String(value).length; i++) {
382
+ const char = String(value)[i];
383
+ await page.keyboard.type(char);
384
+ // Variable delay based on character type
385
+ const delay = char === ' ' ? 80 : (30 + Math.random() * 70);
386
+ await new Promise(r => setTimeout(r, delay));
387
+ }
388
+ } else {
389
+ await page.type(bestMatch.selector, String(value));
390
+ }
391
+ }
392
+
393
+ // Tab to next field (human-like navigation)
394
+ if (humanLike) {
395
+ await new Promise(r => setTimeout(r, 100 + Math.random() * 200));
396
+ await page.keyboard.press('Tab');
397
+ await new Promise(r => setTimeout(r, 50));
398
+ }
399
+
400
+ filledCount++;
401
+ filledFields.push({ field, selector: bestMatch.selector, matchScore: bestScore });
402
+ notifyProgress('solve_captcha', 'progress', `📝 Filled: ${field} (score: ${bestScore})`, { field, filledCount });
403
+ } catch (e) {
404
+ unfilledFields.push(field);
301
405
  }
406
+ }
302
407
 
303
- // Step 4: Submit if requested
304
- if (submitAfter) {
305
- notifyProgress('smart_form_filler', 'progress', '📤 Submitting form...');
306
- try {
307
- await page.click(submitSelector);
308
- await new Promise(r => setTimeout(r, 2000)); // Wait for submission
309
- } catch (submitErr) {
310
- notifyProgress('smart_form_filler', 'error', `Submit failed: ${submitErr.message}`);
408
+ return {
409
+ success: filledCount > 0,
410
+ filledCount,
411
+ filledFields,
412
+ unfilledFields,
413
+ totalFields: fields.length,
414
+ pageInfo
415
+ };
416
+ },
417
+
418
+ // ═══════════════════════════════════════════════════════════════
419
+ // HELPER: Pre-Submit Validation - Check all required fields filled
420
+ // ═══════════════════════════════════════════════════════════════
421
+ async _validateBeforeSubmit(page) {
422
+ return await page.evaluate(() => {
423
+ const errors = [];
424
+ const requiredFields = document.querySelectorAll('[required], .required input');
425
+
426
+ requiredFields.forEach(field => {
427
+ if (!field.value || field.value.trim() === '') {
428
+ const label = field.name || field.id || field.placeholder || 'Unknown';
429
+ errors.push({ field: label, error: 'Required field is empty' });
430
+ }
431
+ });
432
+
433
+ // Check for visible error messages
434
+ const errorMsgs = document.querySelectorAll('.error, .error-message, .invalid-feedback, [class*="error"]');
435
+ errorMsgs.forEach(el => {
436
+ if (el.offsetParent !== null && el.textContent.trim()) {
437
+ errors.push({ field: 'form', error: el.textContent.trim() });
311
438
  }
439
+ });
440
+
441
+ return { valid: errors.length === 0, errors };
442
+ });
443
+ },
444
+
445
+ // ═══════════════════════════════════════════════════════════════
446
+ // HELPER: Post-Submit Error Detection
447
+ // ═══════════════════════════════════════════════════════════════
448
+ async _detectPostSubmitErrors(page) {
449
+ await new Promise(r => setTimeout(r, 1500)); // Wait for page response
450
+
451
+ return await page.evaluate(() => {
452
+ const errors = [];
453
+
454
+ // Check for error messages
455
+ const errorSelectors = [
456
+ '.error', '.error-message', '.alert-danger', '.invalid',
457
+ '[class*="error"]', '[class*="invalid"]', '.captcha-error'
458
+ ];
459
+
460
+ for (const sel of errorSelectors) {
461
+ document.querySelectorAll(sel).forEach(el => {
462
+ if (el.offsetParent !== null && el.textContent.trim()) {
463
+ errors.push(el.textContent.trim());
464
+ }
465
+ });
312
466
  }
313
467
 
314
- notifyProgress('smart_form_filler', 'completed', `Form automation completed successfully`);
468
+ // Check if captcha input is still visible (might indicate wrong captcha)
469
+ const captchaInput = document.querySelector('input[name*="captcha"], input[id*="captcha"]');
470
+ if (captchaInput && captchaInput.offsetParent !== null && !captchaInput.value) {
471
+ errors.push('Captcha may have failed - input is empty');
472
+ }
315
473
 
316
474
  return {
317
- success: true,
318
- formsAnalyzed: forms.length,
319
- fieldsDetected: forms[0].fields.length,
320
- fieldsFilled: result.filledFields.length,
321
- filledFields: result.filledFields,
322
- captchaSolved: solveCaptcha,
323
- submitted: submitAfter
475
+ hasErrors: errors.length > 0,
476
+ errors: [...new Set(errors)].slice(0, 5) // Unique errors, max 5
324
477
  };
325
-
326
- } catch (error) {
327
- notifyProgress('smart_form_filler', 'error', error.message);
328
- return { success: false, error: error.message };
329
- }
478
+ });
330
479
  },
331
480
 
332
481
  // 1. Browser Init
@@ -354,6 +503,26 @@ const handlers = {
354
503
  pageInstance = result.page;
355
504
  blockerInstance = result.blocker;
356
505
 
506
+ // ═══════════════════════════════════════════════════════════════
507
+ // GLOBAL DIALOG HANDLER - Auto-accept all dialogs (alerts/confirms/prompts)
508
+ // This prevents browser from getting stuck on JavaScript dialogs
509
+ // ═══════════════════════════════════════════════════════════════
510
+ pageInstance.on('dialog', async (dialog) => {
511
+ const dialogType = dialog.type();
512
+ const dialogMessage = dialog.message();
513
+
514
+ notifyProgress('browser_init', 'progress',
515
+ `🔔 Auto-accepting ${dialogType}: ${dialogMessage.substring(0, 100)}...`);
516
+
517
+ try {
518
+ await dialog.accept();
519
+ notifyProgress('browser_init', 'progress', `✅ Dialog accepted: ${dialogType}`);
520
+ } catch (e) {
521
+ // Dialog might already be handled
522
+ console.error(`Dialog handling error: ${e.message}`);
523
+ }
524
+ });
525
+
357
526
  const pid = browserInstance.process()?.pid;
358
527
 
359
528
  notifyProgress('browser_init', 'completed', `Browser started (PID: ${pid})`, {
@@ -528,26 +697,54 @@ const handlers = {
528
697
  // 5. Click
529
698
  async click(params) {
530
699
  const { page } = requireBrowser();
531
- const { selector, humanLike = true, clickCount = 1, delay = 0 } = params;
700
+ const { selector, humanLike = true, clickCount = 1, delay = 0, autoAcceptDialogs = true } = params;
532
701
 
533
702
  notifyProgress('click', 'started', `Clicking: ${selector}`);
534
703
 
535
- if (humanLike) {
704
+ // Auto-handle dialogs (alerts, confirms, prompts) to prevent blocking
705
+ let dialogHandled = false;
706
+ const dialogHandler = async (dialog) => {
707
+ dialogHandled = true;
708
+ const type = dialog.type();
709
+ const message = dialog.message();
710
+ notifyProgress('click', 'progress', `🔔 Auto-accepting ${type}: ${message.substring(0, 50)}...`);
536
711
  try {
537
- const { createCursor } = require('ghost-cursor');
538
- const cursor = createCursor(page);
539
- await cursor.click(selector);
540
- notifyProgress('click', 'progress', 'Used human-like cursor movement');
712
+ await dialog.accept();
541
713
  } catch (e) {
542
- await page.click(selector, { clickCount, delay });
714
+ // Ignore if already handled by global handler
543
715
  }
544
- } else {
545
- await page.click(selector, { clickCount, delay });
716
+ };
717
+
718
+ if (autoAcceptDialogs) {
719
+ page.on('dialog', dialogHandler);
546
720
  }
547
721
 
548
- notifyProgress('click', 'completed', `Clicked: ${selector}`, { selector, humanLike });
722
+ try {
723
+ if (humanLike) {
724
+ try {
725
+ const { createCursor } = require('ghost-cursor');
726
+ const cursor = createCursor(page);
727
+ await cursor.click(selector);
728
+ notifyProgress('click', 'progress', 'Used human-like cursor movement');
729
+ } catch (e) {
730
+ await page.click(selector, { clickCount, delay });
731
+ }
732
+ } else {
733
+ await page.click(selector, { clickCount, delay });
734
+ }
735
+
736
+ // Small wait to allow dialogs to appear and be handled
737
+ await new Promise(r => setTimeout(r, 300));
549
738
 
550
- return { success: true, selector, clicked: true };
739
+ notifyProgress('click', 'completed', `Clicked: ${selector}${dialogHandled ? ' (dialog auto-accepted)' : ''}`, { selector, humanLike, dialogHandled });
740
+
741
+ return { success: true, selector, clicked: true, dialogHandled };
742
+ } finally {
743
+ // Remove dialog handler to prevent memory leaks
744
+ if (autoAcceptDialogs) {
745
+ page.off('dialog', dialogHandler);
746
+ }
747
+ }
551
748
  },
552
749
 
553
750
  // 6. Type
@@ -596,7 +793,7 @@ const handlers = {
596
793
  return { success: true, message: 'Browser closed' };
597
794
  },
598
795
 
599
- // 8. Solve Captcha (Enhanced with OCR + Page Analysis + 100% Accuracy)
796
+ // 8. Solve Captcha (MERGED with form_automator - handles both CAPTCHA and form automation)
600
797
  async solve_captcha(params = {}) {
601
798
  const { page } = requireBrowser();
602
799
  const {
@@ -614,9 +811,24 @@ const handlers = {
614
811
  analyzeFirst = true, // Analyze page before solving
615
812
  verifyBeforeSubmit = true, // Verify captcha looks correct before typing
616
813
  autoRetry = true, // Auto-retry until success
814
+ // MERGED: Form automation options (from form_automator)
815
+ formData, // Form field data to fill
816
+ formSelector, // Form selector (auto-detect if not provided)
817
+ submit = false, // Auto-submit after filling
818
+ humanLike = true, // Human-like typing delays
819
+ aiMatch = true, // AI matches fields even if names differ
617
820
  } = params;
618
821
 
619
- notifyProgress('solve_captcha', 'started', `🎯 100% Accuracy Mode: Solving ${type} captcha...`);
822
+ // ═══════════════════════════════════════════════════════════════
823
+ // STEP 0: FORM AUTOMATION (if formData is provided)
824
+ // ═══════════════════════════════════════════════════════════════
825
+ let formResult = null;
826
+ if (formData && Object.keys(formData).length > 0) {
827
+ notifyProgress('solve_captcha', 'started', `📋 Smart Form + Captcha Mode: Filling ${Object.keys(formData).length} fields...`);
828
+ formResult = await this._fillFormFields(page, formData, formSelector, humanLike, aiMatch);
829
+ } else {
830
+ notifyProgress('solve_captcha', 'started', `🎯 100% Accuracy Mode: Solving ${type} captcha...`);
831
+ }
620
832
 
621
833
  // ═══════════════════════════════════════════════════════════════
622
834
  // STEP 1: ANALYZE PAGE (if enabled)
@@ -813,6 +1025,26 @@ const handlers = {
813
1025
 
814
1026
  notifyProgress('solve_captcha', 'completed', `✅ 100% VERIFIED: "${finalText}" (${result.confidence.toFixed(0)}%)`);
815
1027
 
1028
+ // MERGED: Handle form submission if requested
1029
+ if (submit) {
1030
+ notifyProgress('solve_captcha', 'progress', '🚀 Submitting form...');
1031
+ const submitResult = await this._submitForm(page);
1032
+ return {
1033
+ success: true,
1034
+ type: 'ocr',
1035
+ text: finalText,
1036
+ originalText: result.text,
1037
+ confidence: result.confidence,
1038
+ attempts,
1039
+ filled: true,
1040
+ pageAnalysis: analyzeFirst ? pageAnalysis : null,
1041
+ verified: true,
1042
+ formResult,
1043
+ submitted: submitResult.success,
1044
+ submitMessage: submitResult.message
1045
+ };
1046
+ }
1047
+
816
1048
  return {
817
1049
  success: true,
818
1050
  type: 'ocr',
@@ -823,6 +1055,7 @@ const handlers = {
823
1055
  filled: true,
824
1056
  pageAnalysis: analyzeFirst ? pageAnalysis : null,
825
1057
  verified: true,
1058
+ formResult,
826
1059
  };
827
1060
  } catch (typeErr) {
828
1061
  notifyProgress('solve_captcha', 'error', `Type error: ${typeErr.message}`);
@@ -839,6 +1072,7 @@ const handlers = {
839
1072
  filled: false,
840
1073
  pageAnalysis: pageAnalysis,
841
1074
  verified: true,
1075
+ formResult,
842
1076
  };
843
1077
  }
844
1078
  }
@@ -850,11 +1084,12 @@ const handlers = {
850
1084
  error: `Failed after ${attempts} attempts`,
851
1085
  lastResult,
852
1086
  pageAnalysis,
1087
+ formResult,
853
1088
  };
854
1089
 
855
1090
  } catch (err) {
856
1091
  notifyProgress('solve_captcha', 'error', `OCR error: ${err.message}`);
857
- return { success: false, error: err.message, type: 'ocr' };
1092
+ return { success: false, error: err.message, type: 'ocr', formResult };
858
1093
  }
859
1094
  }
860
1095
 
@@ -872,7 +1107,22 @@ const handlers = {
872
1107
 
873
1108
  if (turnstileToken) {
874
1109
  notifyProgress('solve_captcha', 'completed', `Captcha solved after ${attempts} checks`, { type: 'turnstile', attempts });
875
- return { success: true, type: 'turnstile', solved: true };
1110
+
1111
+ // MERGED: Handle form submission if requested
1112
+ if (submit) {
1113
+ notifyProgress('solve_captcha', 'progress', '🚀 Submitting form...');
1114
+ const submitResult = await this._submitForm(page);
1115
+ return {
1116
+ success: true,
1117
+ type: 'turnstile',
1118
+ solved: true,
1119
+ formResult,
1120
+ submitted: submitResult.success,
1121
+ submitMessage: submitResult.message
1122
+ };
1123
+ }
1124
+
1125
+ return { success: true, type: 'turnstile', solved: true, formResult };
876
1126
  }
877
1127
 
878
1128
  if (attempts % 10 === 0) {
@@ -883,7 +1133,78 @@ const handlers = {
883
1133
  }
884
1134
 
885
1135
  notifyProgress('solve_captcha', 'error', 'Captcha solving timeout');
886
- return { success: false, error: 'Captcha solving timeout' };
1136
+ return { success: false, error: 'Captcha solving timeout', formResult };
1137
+ },
1138
+
1139
+ // HELPER: Submit form with smart detection, validation, and error handling
1140
+ async _submitForm(page, validateFirst = true, maxRetries = 1) {
1141
+ try {
1142
+ // Pre-submit validation
1143
+ if (validateFirst) {
1144
+ const validation = await this._validateBeforeSubmit(page);
1145
+ if (!validation.valid) {
1146
+ notifyProgress('solve_captcha', 'warn', `⚠️ Validation failed: ${validation.errors.length} issue(s)`);
1147
+ return { success: false, message: 'Pre-submit validation failed', errors: validation.errors };
1148
+ }
1149
+ notifyProgress('solve_captcha', 'progress', '✅ Pre-submit validation passed');
1150
+ }
1151
+
1152
+ const submitSelector = await page.evaluate(() => {
1153
+ const buttons = Array.from(document.querySelectorAll('button, input[type="submit"], input[type="button"], a.btn'));
1154
+ const candidates = buttons.filter(b => {
1155
+ const text = (b.innerText || b.value || '').toLowerCase();
1156
+ return text.includes('submit') || text.includes('go') || text.includes('search') ||
1157
+ text.includes('view') || text.includes('login') || text.includes('sign in') ||
1158
+ text.includes('register') || text.includes('send');
1159
+ });
1160
+ const best = candidates.find(b => b.offsetParent !== null);
1161
+ if (best) {
1162
+ return best.id ? `#${best.id}` : (best.name ? `[name="${best.name}"]` : 'button[type="submit"]');
1163
+ }
1164
+ // Fallback to any submit button
1165
+ const fallback = document.querySelector('button[type="submit"], input[type="submit"]');
1166
+ return fallback ? (fallback.id ? `#${fallback.id}` : 'button[type="submit"]') : null;
1167
+ });
1168
+
1169
+ if (!submitSelector) {
1170
+ notifyProgress('solve_captcha', 'warn', '⚠️ Could not auto-detect submit button');
1171
+ return { success: false, message: 'Could not auto-detect submit button' };
1172
+ }
1173
+
1174
+ // Click submit button with human-like behavior
1175
+ try {
1176
+ const { createCursor } = require('ghost-cursor');
1177
+ const cursor = createCursor(page);
1178
+ await cursor.click(submitSelector);
1179
+ } catch (e) {
1180
+ await page.click(submitSelector);
1181
+ }
1182
+
1183
+ // Wait for response
1184
+ try {
1185
+ await page.waitForNavigation({ timeout: 5000, waitUntil: 'domcontentloaded' });
1186
+ notifyProgress('solve_captcha', 'completed', '✅ Form submitted and navigation complete');
1187
+ return { success: true, message: 'Form submitted and navigation complete', navigated: true };
1188
+ } catch (e) {
1189
+ // No navigation - check for errors on same page
1190
+ const postErrors = await this._detectPostSubmitErrors(page);
1191
+
1192
+ if (postErrors.hasErrors) {
1193
+ notifyProgress('solve_captcha', 'warn', `⚠️ Submit detected errors: ${postErrors.errors[0]}`);
1194
+ return {
1195
+ success: false,
1196
+ message: 'Form submitted but errors detected',
1197
+ errors: postErrors.errors,
1198
+ needsRetry: postErrors.errors.some(e => e.toLowerCase().includes('captcha'))
1199
+ };
1200
+ }
1201
+
1202
+ notifyProgress('solve_captcha', 'completed', '✅ Form submitted (no navigation detected)');
1203
+ return { success: true, message: 'Form submitted (no navigation detected)', navigated: false };
1204
+ }
1205
+ } catch (error) {
1206
+ return { success: false, message: error.message };
1207
+ }
887
1208
  },
888
1209
 
889
1210
 
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * Brave Real Browser MCP Server - Shared Tool Definitions
3
3
  *
4
- * OPTIMIZED: 23 tools (merged from 28)
4
+ * OPTIMIZED: 22 tools (merged from 28)
5
5
  *
6
6
  * Merges Applied:
7
7
  * - iframe_handler + stream_extractor + player_api_hook → media_extractor
8
8
  * - get_content + js_scrape → get_content (enhanced)
9
9
  * - search_regex + extract_json + scrape_meta_tags → extract_data
10
+ * - solve_captcha + form_automator → solve_captcha (enhanced)
10
11
  *
11
12
  * New Features:
12
13
  * - URL/Base64/AES Decoders built into media_extractor
@@ -125,6 +126,7 @@ const TOOLS = [
125
126
  selector: { type: 'string', description: 'CSS selector (AI auto-heals if element not found)' },
126
127
  humanLike: { type: 'boolean', default: true, description: 'Ghost cursor human movement' },
127
128
  aiHeal: { type: 'boolean', default: true, description: 'Auto-find alternative selector if broken' },
129
+ autoAcceptDialogs: { type: 'boolean', default: true, description: 'Auto-accept alerts/confirms to prevent blocking' },
128
130
  retries: { type: 'number', default: 3 },
129
131
  clickCount: { type: 'number', default: 1 },
130
132
  delay: { type: 'number', default: 0 }
@@ -173,34 +175,42 @@ const TOOLS = [
173
175
  }
174
176
  },
175
177
 
176
- // 8. Solve Captcha (Enhanced with OCR for text captchas)
178
+ // 8. Solve Captcha (MERGED with form_automator - Enhanced with OCR + Form Automation)
177
179
  {
178
180
  name: 'solve_captcha',
179
181
  emoji: '🔓',
180
- description: 'Auto-solve CAPTCHA with AI (Turnstile, reCAPTCHA, hCaptcha, Text/Image OCR)',
181
- descriptionHindi: 'CAPTCHA हल करना (AI + OCR powered)',
182
+ description: 'Auto-solve CAPTCHA with AI + Smart Form Automation (Turnstile, reCAPTCHA, hCaptcha, Text/Image OCR)',
183
+ descriptionHindi: 'CAPTCHA हल करना + फॉर्म भरना (AI + OCR powered)',
182
184
  category: 'interaction',
183
185
  requiresBrowser: true,
184
186
  requiresPage: true,
185
187
  inputSchema: {
186
188
  type: 'object',
187
189
  properties: {
188
- type: {
189
- type: 'string',
190
- enum: ['turnstile', 'recaptcha', 'hcaptcha', 'text', 'image', 'auto'],
190
+ // === CAPTCHA OPTIONS ===
191
+ type: {
192
+ type: 'string',
193
+ enum: ['turnstile', 'recaptcha', 'hcaptcha', 'text', 'image', 'auto'],
191
194
  default: 'auto',
192
195
  description: 'Captcha type: turnstile/recaptcha/hcaptcha (JS-based), text/image (OCR-based), auto (detect)'
193
196
  },
194
197
  timeout: { type: 'number', default: 30000 },
195
198
  aiMode: { type: 'boolean', default: true, description: 'Use AI vision for complex CAPTCHAs' },
196
- // OCR-specific options for text/image captchas
197
199
  captchaSelector: { type: 'string', description: 'CSS selector for captcha image (required for text/image type)' },
198
200
  inputSelector: { type: 'string', description: 'CSS selector for input field to fill result' },
199
201
  refreshSelector: { type: 'string', description: 'CSS selector for captcha refresh button' },
200
202
  lang: { type: 'string', default: 'eng', description: 'OCR language: eng, hin, eng+hin' },
201
203
  expectedLength: { type: 'number', description: 'Expected captcha text length' },
202
204
  allowedChars: { type: 'string', description: 'Allowed characters in captcha' },
203
- maxRetries: { type: 'number', default: 3, description: 'Max refresh attempts for OCR' }
205
+ maxRetries: { type: 'number', default: 3, description: 'Max refresh attempts for OCR' },
206
+
207
+ // === FORM AUTOMATION OPTIONS (merged from form_automator) ===
208
+ formData: { type: 'object', description: 'Form field data to fill (AI matches fields automatically)' },
209
+ formSelector: { type: 'string', description: 'Form selector (AI auto-detects if not provided)' },
210
+ submit: { type: 'boolean', default: false, description: 'Auto-submit form after filling and captcha solving' },
211
+ humanLike: { type: 'boolean', default: true, description: 'Human-like typing with random delays' },
212
+ aiMatch: { type: 'boolean', default: true, description: 'AI matches fields even if names differ' },
213
+ aiValidate: { type: 'boolean', default: true, description: 'AI validates form before submission' }
204
214
  }
205
215
  }
206
216
  },
@@ -483,10 +493,10 @@ const TOOLS = [
483
493
  inputSchema: {
484
494
  type: 'object',
485
495
  properties: {
486
- action: {
487
- type: 'string',
488
- enum: ['extract', 'list_iframes', 'switch_iframe', 'player_control', 'decode_url', 'batch_extract'],
489
- default: 'extract'
496
+ action: {
497
+ type: 'string',
498
+ enum: ['extract', 'list_iframes', 'switch_iframe', 'player_control', 'decode_url', 'batch_extract'],
499
+ default: 'extract'
490
500
  },
491
501
  // For extraction
492
502
  types: { type: 'array', items: { type: 'string' }, default: ['all'], description: 'video, audio, hls, dash, download, iframes' },
@@ -530,30 +540,6 @@ const TOOLS = [
530
540
  },
531
541
  required: ['code']
532
542
  }
533
- },
534
-
535
- // 23. Form Automator
536
- {
537
- name: 'form_automator',
538
- emoji: '📋',
539
- description: 'Smart form automation with AI field detection and validation',
540
- descriptionHindi: 'फॉर्म भरना (AI detection)',
541
- category: 'interaction',
542
- requiresBrowser: true,
543
- requiresPage: true,
544
- inputSchema: {
545
- type: 'object',
546
- properties: {
547
- selector: { type: 'string', description: 'Form selector (AI auto-detects if not provided)' },
548
- data: { type: 'object', description: 'Field data (AI matches fields if names differ)' },
549
- submit: { type: 'boolean', default: false },
550
- humanLike: { type: 'boolean', default: true },
551
- aiMatch: { type: 'boolean', default: true, description: 'AI matches fields even if names differ' },
552
- aiValidate: { type: 'boolean', default: true, description: 'AI validates form before submission' },
553
- captcha: { type: 'boolean', default: true, description: 'Auto-solve CAPTCHA if present' }
554
- },
555
- required: ['data']
556
- }
557
543
  }
558
544
  ];
559
545
 
@@ -586,9 +572,9 @@ const TOOL_DISPLAY = TOOLS.map(t => ({
586
572
  category: t.category
587
573
  }));
588
574
 
589
- module.exports = {
590
- TOOLS,
591
- TOOL_DISPLAY,
575
+ module.exports = {
576
+ TOOLS,
577
+ TOOL_DISPLAY,
592
578
  CATEGORIES,
593
579
  getToolByName,
594
580
  getToolsByCategory,