real-browser-mcp-server 1.1.7 → 1.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 (46) hide show
  1. package/dist/lib/cjs/index.js +384 -0
  2. package/{lib → dist/lib}/cjs/module/pageController.js +27 -29
  3. package/{lib → dist/lib}/cjs/module/turnstile.js +23 -12
  4. package/dist/src/ai/action-parser.js +229 -0
  5. package/dist/src/ai/core.js +367 -0
  6. package/dist/src/ai/element-finder.js +409 -0
  7. package/{src → dist/src}/ai/index.js +35 -50
  8. package/dist/src/ai/page-analyzer.js +264 -0
  9. package/dist/src/ai/selector-healer.js +215 -0
  10. package/dist/src/index.js +116 -0
  11. package/dist/src/mcp/handlers/browser.js +230 -0
  12. package/dist/src/mcp/handlers/dom.js +550 -0
  13. package/dist/src/mcp/handlers/extract.js +451 -0
  14. package/dist/src/mcp/handlers/helpers.js +514 -0
  15. package/dist/src/mcp/handlers/index.js +63 -0
  16. package/dist/src/mcp/handlers/misc.js +1224 -0
  17. package/dist/src/mcp/handlers/network.js +1134 -0
  18. package/dist/src/mcp/handlers/state.js +215 -0
  19. package/dist/src/mcp/handlers/vision.js +475 -0
  20. package/dist/src/mcp/index.js +166 -0
  21. package/dist/src/mcp/server.js +117 -0
  22. package/{src → dist/src}/mcp/tools.js +12 -11
  23. package/dist/src/shared/tools.js +598 -0
  24. package/{test → dist/test}/cjs/test.js +119 -169
  25. package/dist/test/mcp/smoke-test.js +131 -0
  26. package/lib/esm/module/pageController.mjs +21 -18
  27. package/lib/esm/module/turnstile.mjs +7 -0
  28. package/package.json +22 -11
  29. package/.github/ISSUE_TEMPLATE/general_issue.yaml +0 -58
  30. package/.github/SETUP.md +0 -111
  31. package/.github/workflows/publish.yml +0 -162
  32. package/Dockerfile +0 -78
  33. package/lib/cjs/adblocker.bin +0 -0
  34. package/lib/cjs/index.js +0 -396
  35. package/src/ai/action-parser.js +0 -269
  36. package/src/ai/core.js +0 -379
  37. package/src/ai/element-finder.js +0 -466
  38. package/src/ai/page-analyzer.js +0 -295
  39. package/src/ai/selector-healer.js +0 -236
  40. package/src/index.js +0 -128
  41. package/src/mcp/handlers.js +0 -5306
  42. package/src/mcp/index.js +0 -190
  43. package/src/mcp/server.js +0 -141
  44. package/src/shared/tools.js +0 -625
  45. package/test/esm/test.mjs +0 -299
  46. package/test/mcp/smoke-test.js +0 -141
@@ -0,0 +1,514 @@
1
+ "use strict";
2
+ // @ts-nocheck
3
+ // Auto-generated helpers handlers
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.helpersHandlers = void 0;
6
+ exports.helpersHandlers = {
7
+ async _handleBlockingModals(page) {
8
+ try {
9
+ const closed = await page.evaluate(() => {
10
+ // Selectors for common modal close buttons
11
+ const closeSelectors = [
12
+ // Bootstrap/Standard Modals
13
+ '.modal.show .btn-close',
14
+ '.modal.show .close',
15
+ '.modal.in .close',
16
+ '.modal-footer .btn-primary', // "OK" button usually
17
+ '.modal-footer .btn-secondary', // "Close" button
18
+ // Custom Overlays
19
+ '#modal-close',
20
+ '.popup-close',
21
+ '.overlay-close',
22
+ // Generic "X" buttons in overlays
23
+ 'div[role="dialog"] button[aria-label="Close"]',
24
+ 'div[role="dialog"] .close',
25
+ // SweetAlert / specific libraries
26
+ '.swal2-confirm',
27
+ '.swal2-cancel',
28
+ '.ui-dialog-titlebar-close',
29
+ // eCourts specific if known (generic fallback)
30
+ '.modal-header .close',
31
+ 'button[data-dismiss="modal"]'
32
+ ];
33
+ let clicked = false;
34
+ // Check if any modal is visible (display block/flex and opacity > 0)
35
+ const modals = document.querySelectorAll('.modal, .popup, .overlay, .dialog, [role="dialog"]');
36
+ for (const modal of modals) {
37
+ const style = window.getComputedStyle(modal);
38
+ if (style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0') {
39
+ // Modal is visible, find a close button inside
40
+ for (const selector of closeSelectors) {
41
+ const btn = modal.querySelector(selector);
42
+ if (btn && btn.offsetParent !== null) { // Visible button
43
+ btn.click();
44
+ clicked = true;
45
+ break; // Clicked one, break inner loop
46
+ }
47
+ }
48
+ if (clicked)
49
+ break; // Handled one modal, break outer loop
50
+ }
51
+ }
52
+ return clicked;
53
+ });
54
+ if (closed) {
55
+ // notifyProgress('helper', 'progress', '🧹 Auto-closed a blocking modal/popup');
56
+ await new Promise(r => setTimeout(r, 500)); // Wait for animation
57
+ }
58
+ return closed;
59
+ }
60
+ catch (e) {
61
+ return false;
62
+ }
63
+ },
64
+ async _analyzeFullPage(page) {
65
+ return await page.evaluate(() => {
66
+ const inputs = [];
67
+ const allInputs = document.querySelectorAll('input, textarea, select');
68
+ allInputs.forEach((el, index) => {
69
+ if (el.type === 'hidden' || el.offsetParent === null)
70
+ return;
71
+ // Find associated label
72
+ let label = '';
73
+ if (el.id) {
74
+ const labelEl = document.querySelector(`label[for="${el.id}"]`);
75
+ if (labelEl)
76
+ label = labelEl.textContent.trim();
77
+ }
78
+ if (!label) {
79
+ const parent = el.closest('label, .form-group, .field');
80
+ if (parent)
81
+ label = parent.textContent?.split('\n')[0]?.trim() || '';
82
+ }
83
+ inputs.push({
84
+ index,
85
+ tag: el.tagName.toLowerCase(),
86
+ type: el.type || 'text',
87
+ name: el.name || '',
88
+ id: el.id || '',
89
+ placeholder: el.placeholder || '',
90
+ label: label,
91
+ required: el.required,
92
+ value: el.value || '',
93
+ selector: el.id ? `#${el.id}` : (el.name ? `[name="${el.name}"]` : `input[type="${el.type}"]:nth-of-type(${index + 1})`)
94
+ });
95
+ });
96
+ // Detect captcha elements
97
+ const captcha = {
98
+ image: document.querySelector('img[src*="captcha"], img[id*="captcha"], .captcha-image')?.src || null,
99
+ input: document.querySelector('input[name*="captcha"], input[id*="captcha"]')?.id || null
100
+ };
101
+ // Detect submit button
102
+ const submitBtn = document.querySelector('button[type="submit"], input[type="submit"], button.submit');
103
+ return {
104
+ inputs,
105
+ captcha,
106
+ submitButton: submitBtn ? (submitBtn.id ? `#${submitBtn.id}` : 'button[type="submit"]') : null,
107
+ totalInputs: inputs.length
108
+ };
109
+ });
110
+ },
111
+ async _fillFormFields(page, formData, formSelector, humanLike = true, aiMatch = true) {
112
+ const targetForm = formSelector || 'form';
113
+ const fields = Object.keys(formData || {});
114
+ let filledCount = 0;
115
+ const filledFields = [];
116
+ const unfilledFields = [];
117
+ // First, analyze the full page
118
+ const pageInfo = await handlers._analyzeFullPage(page);
119
+ notifyProgress('solve_captcha', 'progress', `🔍 Page analyzed: ${pageInfo.totalInputs} inputs found`);
120
+ for (const [field, value] of Object.entries(formData || {})) {
121
+ // Enhanced AI Field Matching - uses pageInfo for better matching
122
+ let bestMatch = null;
123
+ let bestScore = 0;
124
+ for (const input of pageInfo.inputs) {
125
+ let score = 0;
126
+ const fieldLower = field.toLowerCase();
127
+ // Exact matches
128
+ if (input.name.toLowerCase() === fieldLower)
129
+ score = 100;
130
+ else if (input.id.toLowerCase() === fieldLower)
131
+ score = 95;
132
+ // Partial matches
133
+ else if (input.name.toLowerCase().includes(fieldLower))
134
+ score = 80;
135
+ else if (input.id.toLowerCase().includes(fieldLower))
136
+ score = 75;
137
+ else if (input.placeholder.toLowerCase().includes(fieldLower))
138
+ score = 70;
139
+ else if (input.label.toLowerCase().includes(fieldLower))
140
+ score = 65;
141
+ // Type-based matching
142
+ else if (fieldLower.includes('email') && input.type === 'email')
143
+ score = 60;
144
+ else if (fieldLower.includes('pass') && input.type === 'password')
145
+ score = 60;
146
+ else if (fieldLower.includes('phone') && input.type === 'tel')
147
+ score = 60;
148
+ if (score > bestScore) {
149
+ bestScore = score;
150
+ bestMatch = input;
151
+ }
152
+ }
153
+ if (!bestMatch || bestScore < 50) {
154
+ unfilledFields.push(field);
155
+ continue;
156
+ }
157
+ try {
158
+ const element = await page.$(bestMatch.selector);
159
+ if (!element) {
160
+ unfilledFields.push(field);
161
+ continue;
162
+ }
163
+ // Focus element first (human-like)
164
+ await element.focus();
165
+ await new Promise(r => setTimeout(r, 100 + Math.random() * 150));
166
+ if (bestMatch.tag === 'select') {
167
+ // Smart Select
168
+ await page.evaluate((sel, val) => {
169
+ const el = document.querySelector(sel);
170
+ if (!el)
171
+ return;
172
+ el.value = val;
173
+ if (el.value !== val) {
174
+ for (const opt of el.options) {
175
+ if (opt.text.toLowerCase().includes(val.toLowerCase())) {
176
+ el.value = opt.value;
177
+ break;
178
+ }
179
+ }
180
+ }
181
+ el.dispatchEvent(new Event('change', { bubbles: true }));
182
+ }, bestMatch.selector, String(value));
183
+ }
184
+ else if (bestMatch.type === 'checkbox' || bestMatch.type === 'radio') {
185
+ if (value)
186
+ await element.click();
187
+ }
188
+ else {
189
+ // Text input - clear and type with human behavior
190
+ await element.click({ clickCount: 3 });
191
+ await page.keyboard.press('Backspace');
192
+ await new Promise(r => setTimeout(r, 50));
193
+ if (humanLike) {
194
+ // Human-like typing with variable delays
195
+ for (let i = 0; i < String(value).length; i++) {
196
+ const char = String(value)[i];
197
+ await page.keyboard.type(char);
198
+ // Variable delay based on character type
199
+ const delay = char === ' ' ? 80 : (30 + Math.random() * 70);
200
+ await new Promise(r => setTimeout(r, delay));
201
+ }
202
+ }
203
+ else {
204
+ await page.type(bestMatch.selector, String(value));
205
+ }
206
+ }
207
+ // Tab to next field (human-like navigation)
208
+ if (humanLike) {
209
+ await new Promise(r => setTimeout(r, 100 + Math.random() * 200));
210
+ await page.keyboard.press('Tab');
211
+ await new Promise(r => setTimeout(r, 50));
212
+ }
213
+ filledCount++;
214
+ filledFields.push({ field, selector: bestMatch.selector, matchScore: bestScore });
215
+ notifyProgress('solve_captcha', 'progress', `📝 Filled: ${field} (score: ${bestScore})`, { field, filledCount });
216
+ }
217
+ catch (e) {
218
+ unfilledFields.push(field);
219
+ }
220
+ }
221
+ return {
222
+ success: filledCount > 0,
223
+ filledCount,
224
+ filledFields,
225
+ unfilledFields,
226
+ totalFields: fields.length,
227
+ pageInfo
228
+ };
229
+ },
230
+ async _validateBeforeSubmit(page) {
231
+ return await page.evaluate(() => {
232
+ const errors = [];
233
+ const requiredFields = document.querySelectorAll('[required], .required input');
234
+ requiredFields.forEach(field => {
235
+ if (!field.value || field.value.trim() === '') {
236
+ const label = field.name || field.id || field.placeholder || 'Unknown';
237
+ errors.push({ field: label, error: 'Required field is empty' });
238
+ }
239
+ });
240
+ // Check for visible error messages
241
+ const errorMsgs = document.querySelectorAll('.error, .error-message, .invalid-feedback, [class*="error"]');
242
+ errorMsgs.forEach(el => {
243
+ if (el.offsetParent !== null && el.textContent.trim()) {
244
+ errors.push({ field: 'form', error: el.textContent.trim() });
245
+ }
246
+ });
247
+ return { valid: errors.length === 0, errors };
248
+ });
249
+ },
250
+ async _detectPostSubmitErrors(page) {
251
+ await new Promise(r => setTimeout(r, 1500)); // Wait for page response
252
+ return await page.evaluate(() => {
253
+ const errors = [];
254
+ // Check for error messages
255
+ const errorSelectors = [
256
+ '.error', '.error-message', '.alert-danger', '.invalid',
257
+ '[class*="error"]', '[class*="invalid"]', '.captcha-error'
258
+ ];
259
+ for (const sel of errorSelectors) {
260
+ document.querySelectorAll(sel).forEach(el => {
261
+ if (el.offsetParent !== null && el.textContent.trim()) {
262
+ errors.push(el.textContent.trim());
263
+ }
264
+ });
265
+ }
266
+ // Check if captcha input is still visible (might indicate wrong captcha)
267
+ const captchaInput = document.querySelector('input[name*="captcha"], input[id*="captcha"]');
268
+ if (captchaInput && captchaInput.offsetParent !== null && !captchaInput.value) {
269
+ errors.push('Captcha may have failed - input is empty');
270
+ }
271
+ return {
272
+ hasErrors: errors.length > 0,
273
+ errors: [...new Set(errors)].slice(0, 5) // Unique errors, max 5
274
+ };
275
+ });
276
+ },
277
+ async _solveWithVisionAPI(imageBase64, langHint = '') {
278
+ if (process.env.NVIDIA_API_KEY) {
279
+ try {
280
+ return await handlers._solveWithNvidia(imageBase64, langHint);
281
+ }
282
+ catch (e) {
283
+ notifyProgress('solve_captcha', 'progress', `⚠️ NVIDIA API error: ${e.message}`);
284
+ }
285
+ }
286
+ if (process.env.OPENROUTER_API_KEY) {
287
+ try {
288
+ return await handlers._solveWithOpenRouter(imageBase64, langHint);
289
+ }
290
+ catch (e) {
291
+ notifyProgress('solve_captcha', 'progress', `⚠️ OpenRouter API error: ${e.message}`);
292
+ }
293
+ }
294
+ return null; // No API configured — fallback to host LLM
295
+ },
296
+ async _solveWithNvidia(imageBase64, langHint = '') {
297
+ const https = require('https');
298
+ const apiKey = process.env.NVIDIA_API_KEY;
299
+ // NVIDIA vision models sorted by speed & reliability
300
+ const models = [
301
+ 'z-ai/glm-4.7',
302
+ 'deepseek-ai/deepseek-v4-pro',
303
+ 'meta/llama-3.2-11b-vision-instruct',
304
+ 'meta/llama-4-maverick-17b-128e-instruct',
305
+ 'microsoft/phi-4-multimodal-instruct'
306
+ ];
307
+ for (const model of models) {
308
+ notifyProgress('solve_captcha', 'progress', `🟢 NVIDIA: Trying ${model}...`);
309
+ const requestBody = JSON.stringify({
310
+ model,
311
+ messages: [{
312
+ role: 'user',
313
+ content: [
314
+ {
315
+ type: 'text',
316
+ text: 'This is a CAPTCHA image. Read ONLY the text/characters shown. Return ONLY the exact characters, nothing else.' + langHint
317
+ },
318
+ {
319
+ type: 'image_url',
320
+ image_url: { url: `data:image/png;base64,${imageBase64}` }
321
+ }
322
+ ]
323
+ }],
324
+ max_tokens: 30,
325
+ temperature: 0.1
326
+ });
327
+ try {
328
+ const result = await new Promise((resolve, reject) => {
329
+ const options = {
330
+ hostname: 'integrate.api.nvidia.com',
331
+ path: '/v1/chat/completions',
332
+ method: 'POST',
333
+ headers: {
334
+ 'Content-Type': 'application/json',
335
+ 'Authorization': `Bearer ${apiKey}`,
336
+ 'Content-Length': Buffer.byteLength(requestBody)
337
+ }
338
+ };
339
+ const req = https.request(options, (res) => {
340
+ let data = '';
341
+ res.on('data', chunk => data += chunk);
342
+ res.on('end', () => {
343
+ try {
344
+ const json = JSON.parse(data);
345
+ if (json.error) {
346
+ notifyProgress('solve_captcha', 'progress', `⏭️ NVIDIA ${model}: ${(json.error.message || '').substring(0, 60)}`);
347
+ return resolve(null); // Try next model
348
+ }
349
+ const text = json?.choices?.[0]?.message?.content?.trim();
350
+ if (!text)
351
+ return resolve(null);
352
+ const cleaned = text.replace(/[\s"'\n\r`]/g, '');
353
+ notifyProgress('solve_captcha', 'progress', `✨ NVIDIA [${model.split('/')[1]}] extracted: "${cleaned}"`);
354
+ resolve(cleaned || null);
355
+ }
356
+ catch (e) {
357
+ resolve(null);
358
+ }
359
+ });
360
+ });
361
+ req.on('error', () => resolve(null));
362
+ req.setTimeout(20000, () => { req.destroy(); resolve(null); });
363
+ req.write(requestBody);
364
+ req.end();
365
+ });
366
+ if (result)
367
+ return result;
368
+ }
369
+ catch (e) {
370
+ // Try next model
371
+ }
372
+ }
373
+ throw new Error('All NVIDIA models failed');
374
+ },
375
+ async _solveWithOpenRouter(imageBase64, langHint = '') {
376
+ const https = require('https');
377
+ const apiKey = process.env.OPENROUTER_API_KEY;
378
+ // Best free vision models on OpenRouter (auto-fallback)
379
+ const models = [
380
+ 'google/gemini-2.5-pro-free', // Insanely smart & free
381
+ 'meta-llama/llama-3.2-90b-vision-instruct:free',
382
+ 'qwen/qwen-vl-plus:free'
383
+ ];
384
+ for (const model of models) {
385
+ notifyProgress('solve_captcha', 'progress', `🟢 OpenRouter: Trying ${model}...`);
386
+ const requestBody = JSON.stringify({
387
+ model,
388
+ messages: [{
389
+ role: 'user',
390
+ content: [
391
+ { type: 'text', text: 'This is a CAPTCHA image. Read ONLY the text/characters shown. Return ONLY the exact characters, nothing else.' + langHint },
392
+ { type: 'image_url', image_url: { url: `data:image/png;base64,${imageBase64}` } }
393
+ ]
394
+ }],
395
+ max_tokens: 30,
396
+ temperature: 0.1
397
+ });
398
+ try {
399
+ const result = await new Promise((resolve, reject) => {
400
+ const options = {
401
+ hostname: 'openrouter.ai',
402
+ path: '/api/v1/chat/completions',
403
+ method: 'POST',
404
+ headers: {
405
+ 'Content-Type': 'application/json',
406
+ 'Authorization': `Bearer ${apiKey}`,
407
+ 'HTTP-Referer': 'https://github.com/brave-browser',
408
+ 'X-Title': 'Brave MCP',
409
+ 'Content-Length': Buffer.byteLength(requestBody)
410
+ }
411
+ };
412
+ const req = https.request(options, (res) => {
413
+ let data = '';
414
+ res.on('data', chunk => data += chunk);
415
+ res.on('end', () => {
416
+ try {
417
+ const json = JSON.parse(data);
418
+ if (json.error) {
419
+ notifyProgress('solve_captcha', 'progress', `⏭️ OpenRouter ${model}: ${(json.error.message || '').substring(0, 60)}`);
420
+ return resolve(null); // Try next model
421
+ }
422
+ const text = json?.choices?.[0]?.message?.content?.trim();
423
+ if (!text)
424
+ return resolve(null);
425
+ const cleaned = text.replace(/[\s"'\n\r`]/g, '');
426
+ notifyProgress('solve_captcha', 'progress', `✨ OpenRouter [${model.split('/')[1]}] extracted: "${cleaned}"`);
427
+ resolve(cleaned || null);
428
+ }
429
+ catch (e) {
430
+ resolve(null);
431
+ }
432
+ });
433
+ });
434
+ req.on('error', () => resolve(null));
435
+ req.setTimeout(20000, () => { req.destroy(); resolve(null); });
436
+ req.write(requestBody);
437
+ req.end();
438
+ });
439
+ if (result)
440
+ return result;
441
+ }
442
+ catch (e) {
443
+ // Try next model
444
+ }
445
+ }
446
+ throw new Error('All OpenRouter models failed');
447
+ },
448
+ async _submitForm(page, validateFirst = true, maxRetries = 1) {
449
+ try {
450
+ // Pre-submit validation
451
+ if (validateFirst) {
452
+ const validation = await handlers._validateBeforeSubmit(page);
453
+ if (!validation.valid) {
454
+ notifyProgress('solve_captcha', 'warn', `⚠️ Validation failed: ${validation.errors.length} issue(s)`);
455
+ return { success: false, message: 'Pre-submit validation failed', errors: validation.errors };
456
+ }
457
+ notifyProgress('solve_captcha', 'progress', '✅ Pre-submit validation passed');
458
+ }
459
+ const submitSelector = await page.evaluate(() => {
460
+ const buttons = Array.from(document.querySelectorAll('button, input[type="submit"], input[type="button"], a.btn'));
461
+ const candidates = buttons.filter(b => {
462
+ const text = (b.innerText || b.value || '').toLowerCase();
463
+ return text.includes('submit') || text.includes('go') || text.includes('search') ||
464
+ text.includes('view') || text.includes('login') || text.includes('sign in') ||
465
+ text.includes('register') || text.includes('send');
466
+ });
467
+ const best = candidates.find(b => b.offsetParent !== null);
468
+ if (best) {
469
+ return best.id ? `#${best.id}` : (best.name ? `[name="${best.name}"]` : 'button[type="submit"]');
470
+ }
471
+ // Fallback to any submit button
472
+ const fallback = document.querySelector('button[type="submit"], input[type="submit"]');
473
+ return fallback ? (fallback.id ? `#${fallback.id}` : 'button[type="submit"]') : null;
474
+ });
475
+ if (!submitSelector) {
476
+ notifyProgress('solve_captcha', 'warn', '⚠️ Could not auto-detect submit button');
477
+ return { success: false, message: 'Could not auto-detect submit button' };
478
+ }
479
+ // Click submit button with human-like behavior
480
+ try {
481
+ const { createCursor } = require('ghost-cursor-patchright');
482
+ const cursor = createCursor(page);
483
+ await cursor.click(submitSelector);
484
+ }
485
+ catch (e) {
486
+ await page.click(submitSelector);
487
+ }
488
+ // Wait for response
489
+ try {
490
+ await page.waitForNavigation({ timeout: 5000, waitUntil: 'domcontentloaded' });
491
+ notifyProgress('solve_captcha', 'completed', '✅ Form submitted and navigation complete');
492
+ return { success: true, message: 'Form submitted and navigation complete', navigated: true };
493
+ }
494
+ catch (e) {
495
+ // No navigation - check for errors on same page
496
+ const postErrors = await handlers._detectPostSubmitErrors(page);
497
+ if (postErrors.hasErrors) {
498
+ notifyProgress('solve_captcha', 'warn', `⚠️ Submit detected errors: ${postErrors.errors[0]}`);
499
+ return {
500
+ success: false,
501
+ message: 'Form submitted but errors detected',
502
+ errors: postErrors.errors,
503
+ needsRetry: postErrors.errors.some(e => e.toLowerCase().includes('captcha'))
504
+ };
505
+ }
506
+ notifyProgress('solve_captcha', 'completed', '✅ Form submitted (no navigation detected)');
507
+ return { success: true, message: 'Form submitted (no navigation detected)', navigated: false };
508
+ }
509
+ }
510
+ catch (error) {
511
+ return { success: false, message: error.message };
512
+ }
513
+ }
514
+ };
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.aiEnhancedSelector = exports.getAICore = exports.getHeadlessFromEnv = exports.notifyProgress = exports.setProgressCallback = exports.requireBrowser = exports.getState = exports.handlers = void 0;
4
+ exports.executeTool = executeTool;
5
+ exports.cleanup = cleanup;
6
+ // @ts-nocheck
7
+ const browser_1 = require("./browser");
8
+ const dom_1 = require("./dom");
9
+ const network_1 = require("./network");
10
+ const vision_1 = require("./vision");
11
+ const extract_1 = require("./extract");
12
+ const helpers_1 = require("./helpers");
13
+ const misc_1 = require("./misc");
14
+ const state_1 = require("./state");
15
+ Object.defineProperty(exports, "setProgressCallback", { enumerable: true, get: function () { return state_1.setProgressCallback; } });
16
+ Object.defineProperty(exports, "notifyProgress", { enumerable: true, get: function () { return state_1.notifyProgress; } });
17
+ Object.defineProperty(exports, "getHeadlessFromEnv", { enumerable: true, get: function () { return state_1.getHeadlessFromEnv; } });
18
+ Object.defineProperty(exports, "getState", { enumerable: true, get: function () { return state_1.getState; } });
19
+ Object.defineProperty(exports, "requireBrowser", { enumerable: true, get: function () { return state_1.requireBrowser; } });
20
+ const core_1 = require("../../ai/core");
21
+ Object.defineProperty(exports, "getAICore", { enumerable: true, get: function () { return core_1.getAICore; } });
22
+ Object.defineProperty(exports, "aiEnhancedSelector", { enumerable: true, get: function () { return core_1.aiEnhancedSelector; } });
23
+ exports.handlers = {
24
+ ...browser_1.browserHandlers,
25
+ ...dom_1.domHandlers,
26
+ ...network_1.networkHandlers,
27
+ ...vision_1.visionHandlers,
28
+ ...extract_1.extractHandlers,
29
+ ...helpers_1.helpersHandlers,
30
+ ...misc_1.miscHandlers
31
+ };
32
+ async function executeTool(name, args = {}) {
33
+ if (exports.handlers[name]) {
34
+ try {
35
+ const aiCore = core_1.getAICore ? (0, core_1.getAICore)() : null;
36
+ let wrappedHandler = exports.handlers[name];
37
+ if (aiCore && typeof aiCore.wrapHandler === 'function') {
38
+ wrappedHandler = aiCore.wrapHandler(exports.handlers[name].bind(exports.handlers), name);
39
+ }
40
+ return await wrappedHandler(args);
41
+ }
42
+ catch (error) {
43
+ return { success: false, error: error.message };
44
+ }
45
+ }
46
+ return { success: false, error: `Tool ${name} not implemented` };
47
+ }
48
+ async function cleanup() {
49
+ if (state_1.state.browserInstance) {
50
+ try {
51
+ await state_1.state.browserInstance.close();
52
+ }
53
+ catch (e) {
54
+ if (typeof state_1.state.browserInstance.process === 'function') {
55
+ state_1.state.browserInstance.process()?.kill('SIGKILL');
56
+ }
57
+ }
58
+ state_1.state.browserInstance = null;
59
+ state_1.state.pageInstance = null;
60
+ state_1.state.blockerInstance = null;
61
+ state_1.state.setupPageFn = null;
62
+ }
63
+ }