real-browser-mcp-server 1.2.0 → 1.2.2

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 (120) hide show
  1. package/README.md +96 -9
  2. package/dist/lib/cjs/index.d.ts +2 -0
  3. package/dist/lib/cjs/index.d.ts.map +1 -0
  4. package/dist/lib/cjs/index.js +385 -0
  5. package/dist/lib/cjs/index.js.map +1 -0
  6. package/dist/lib/cjs/module/pageController.d.ts +2 -0
  7. package/dist/lib/cjs/module/pageController.d.ts.map +1 -0
  8. package/{lib → dist/lib}/cjs/module/pageController.js +28 -29
  9. package/dist/lib/cjs/module/pageController.js.map +1 -0
  10. package/dist/lib/cjs/module/turnstile.d.ts +2 -0
  11. package/dist/lib/cjs/module/turnstile.d.ts.map +1 -0
  12. package/{lib → dist/lib}/cjs/module/turnstile.js +24 -12
  13. package/dist/lib/cjs/module/turnstile.js.map +1 -0
  14. package/dist/src/index.d.ts +11 -0
  15. package/dist/src/index.d.ts.map +1 -0
  16. package/dist/src/index.js +118 -0
  17. package/dist/src/index.js.map +1 -0
  18. package/dist/src/mcp/handlers/browser.d.ts +30 -0
  19. package/dist/src/mcp/handlers/browser.d.ts.map +1 -0
  20. package/dist/src/mcp/handlers/browser.js +231 -0
  21. package/dist/src/mcp/handlers/browser.js.map +1 -0
  22. package/dist/src/mcp/handlers/dom.d.ts +134 -0
  23. package/dist/src/mcp/handlers/dom.d.ts.map +1 -0
  24. package/dist/src/mcp/handlers/dom.js +551 -0
  25. package/dist/src/mcp/handlers/dom.js.map +1 -0
  26. package/dist/src/mcp/handlers/extract.d.ts +59 -0
  27. package/dist/src/mcp/handlers/extract.d.ts.map +1 -0
  28. package/dist/src/mcp/handlers/extract.js +455 -0
  29. package/dist/src/mcp/handlers/extract.js.map +1 -0
  30. package/dist/src/mcp/handlers/form-handlers.d.ts +9 -0
  31. package/dist/src/mcp/handlers/form-handlers.d.ts.map +1 -0
  32. package/dist/src/mcp/handlers/form-handlers.js +56 -0
  33. package/dist/src/mcp/handlers/form-handlers.js.map +1 -0
  34. package/dist/src/mcp/handlers/helpers.d.ts +47 -0
  35. package/dist/src/mcp/handlers/helpers.d.ts.map +1 -0
  36. package/dist/src/mcp/handlers/helpers.js +515 -0
  37. package/dist/src/mcp/handlers/helpers.js.map +1 -0
  38. package/dist/src/mcp/handlers/index.d.ts +6 -0
  39. package/dist/src/mcp/handlers/index.d.ts.map +1 -0
  40. package/dist/src/mcp/handlers/index.js +61 -0
  41. package/dist/src/mcp/handlers/index.js.map +1 -0
  42. package/dist/src/mcp/handlers/media-handlers.d.ts +10 -0
  43. package/dist/src/mcp/handlers/media-handlers.d.ts.map +1 -0
  44. package/dist/src/mcp/handlers/media-handlers.js +535 -0
  45. package/dist/src/mcp/handlers/media-handlers.js.map +1 -0
  46. package/dist/src/mcp/handlers/network.d.ts +147 -0
  47. package/dist/src/mcp/handlers/network.d.ts.map +1 -0
  48. package/dist/src/mcp/handlers/network.js +1135 -0
  49. package/dist/src/mcp/handlers/network.js.map +1 -0
  50. package/dist/src/mcp/handlers/state.d.ts +34 -0
  51. package/dist/src/mcp/handlers/state.d.ts.map +1 -0
  52. package/dist/src/mcp/handlers/state.js +225 -0
  53. package/dist/src/mcp/handlers/state.js.map +1 -0
  54. package/dist/src/mcp/handlers/utility-handlers.d.ts +167 -0
  55. package/dist/src/mcp/handlers/utility-handlers.d.ts.map +1 -0
  56. package/dist/src/mcp/handlers/utility-handlers.js +280 -0
  57. package/dist/src/mcp/handlers/utility-handlers.js.map +1 -0
  58. package/dist/src/mcp/handlers/vision.d.ts +127 -0
  59. package/dist/src/mcp/handlers/vision.d.ts.map +1 -0
  60. package/dist/src/mcp/handlers/vision.js +483 -0
  61. package/dist/src/mcp/handlers/vision.js.map +1 -0
  62. package/dist/src/mcp/index.d.ts +3 -0
  63. package/dist/src/mcp/index.d.ts.map +1 -0
  64. package/dist/src/mcp/index.js +166 -0
  65. package/dist/src/mcp/index.js.map +1 -0
  66. package/dist/src/mcp/server.d.ts +2 -0
  67. package/dist/src/mcp/server.d.ts.map +1 -0
  68. package/dist/src/mcp/server.js +117 -0
  69. package/dist/src/mcp/server.js.map +1 -0
  70. package/dist/src/mcp/tools.d.ts +8 -0
  71. package/dist/src/mcp/tools.d.ts.map +1 -0
  72. package/{src → dist/src}/mcp/tools.js +12 -11
  73. package/dist/src/mcp/tools.js.map +1 -0
  74. package/dist/src/shared/cache-manager.d.ts +80 -0
  75. package/dist/src/shared/cache-manager.d.ts.map +1 -0
  76. package/dist/src/shared/cache-manager.js +221 -0
  77. package/dist/src/shared/cache-manager.js.map +1 -0
  78. package/dist/src/shared/tools.d.ts +2 -0
  79. package/dist/src/shared/tools.d.ts.map +1 -0
  80. package/dist/src/shared/tools.js +599 -0
  81. package/dist/src/shared/tools.js.map +1 -0
  82. package/dist/src/types.d.ts +365 -0
  83. package/dist/src/types.d.ts.map +1 -0
  84. package/dist/src/types.js +9 -0
  85. package/dist/src/types.js.map +1 -0
  86. package/dist/test/cjs/test.d.ts +11 -0
  87. package/dist/test/cjs/test.d.ts.map +1 -0
  88. package/dist/test/cjs/test.js +289 -0
  89. package/dist/test/cjs/test.js.map +1 -0
  90. package/dist/test/mcp/smoke-test.d.ts +29 -0
  91. package/dist/test/mcp/smoke-test.d.ts.map +1 -0
  92. package/dist/test/mcp/smoke-test.js +132 -0
  93. package/dist/test/mcp/smoke-test.js.map +1 -0
  94. package/lib/esm/index.mjs +232 -79
  95. package/lib/esm/module/pageController.mjs +21 -18
  96. package/lib/esm/module/turnstile.mjs +7 -0
  97. package/package.json +25 -15
  98. package/typings.d.ts +12 -6
  99. package/.github/ISSUE_TEMPLATE/general_issue.yaml +0 -58
  100. package/.github/SETUP.md +0 -111
  101. package/.github/workflows/publish.yml +0 -135
  102. package/Dockerfile +0 -79
  103. package/lib/cjs/adblocker.bin +0 -0
  104. package/lib/cjs/index.js +0 -249
  105. package/src/ai/action-parser.js +0 -274
  106. package/src/ai/core.js +0 -378
  107. package/src/ai/element-finder.js +0 -466
  108. package/src/ai/index.js +0 -82
  109. package/src/ai/page-analyzer.js +0 -304
  110. package/src/ai/selector-healer.js +0 -236
  111. package/src/index.js +0 -121
  112. package/src/mcp/handlers.js +0 -5071
  113. package/src/mcp/index.js +0 -190
  114. package/src/mcp/server.js +0 -144
  115. package/src/shared/tools.js +0 -618
  116. package/test/cjs/test.js +0 -259
  117. package/test/esm/package.json +0 -13
  118. package/test/esm/test.js +0 -226
  119. package/test/esm/test_option2.js +0 -46
  120. package/test/esm/test_playwright_ghost.js +0 -30
@@ -1,274 +0,0 @@
1
- /**
2
- * AI Action Parser - Parse natural language commands into actions
3
- *
4
- * Converts commands like:
5
- * - "click the login button" -> { type: 'click', target: 'login button' }
6
- * - "type hello in the search box" -> { type: 'type', target: 'search box', text: 'hello' }
7
- * - "scroll down" -> { type: 'scroll', direction: 'down' }
8
- */
9
-
10
- class ActionParser {
11
- constructor() {
12
- // Action patterns
13
- this.actionPatterns = [
14
- // Click patterns
15
- {
16
- pattern: /^(click|tap|press|hit|select)\s+(?:on\s+)?(?:the\s+)?(.+)/i,
17
- type: 'click',
18
- extract: (match) => ({ target: match[2].trim() })
19
- },
20
- // Type patterns
21
- {
22
- pattern: /^(type|enter|write|input)\s+["']?([^"']+)["']?\s+(?:in|into|in the|into the)\s+(.+)/i,
23
- type: 'type',
24
- extract: (match) => ({ text: match[2].trim(), target: match[3].trim() })
25
- },
26
- {
27
- pattern: /^(type|enter|write|input)\s+(.+)\s+(?:in|into)\s+["']?([^"']+)["']?/i,
28
- type: 'type',
29
- extract: (match) => ({ target: match[2].trim(), text: match[3].trim() })
30
- },
31
- {
32
- pattern: /^(fill|fill in|complete)\s+(?:the\s+)?(.+)\s+(?:with|as)\s+["']?([^"']+)["']?/i,
33
- type: 'type',
34
- extract: (match) => ({ target: match[2].trim(), text: match[3].trim() })
35
- },
36
- // Navigate patterns
37
- {
38
- pattern: /^(go to|navigate to|open|visit)\s+(.+)/i,
39
- type: 'navigate',
40
- extract: (match) => {
41
- let url = match[2].trim();
42
- if (!url.startsWith('http')) {
43
- url = 'https://' + url;
44
- }
45
- return { url };
46
- }
47
- },
48
- // Scroll patterns
49
- {
50
- pattern: /^scroll\s+(up|down|left|right)(?:\s+(\d+)\s*(?:px|pixels)?)?/i,
51
- type: 'scroll',
52
- extract: (match) => ({
53
- direction: match[1].toLowerCase(),
54
- amount: parseInt(match[2]) || 300
55
- })
56
- },
57
- {
58
- pattern: /^scroll\s+to\s+(?:the\s+)?(top|bottom|footer|header)/i,
59
- type: 'scroll',
60
- extract: (match) => {
61
- const target = match[1].toLowerCase();
62
- return {
63
- direction: target === 'top' || target === 'header' ? 'up' : 'down',
64
- amount: 10000,
65
- scrollTo: target
66
- };
67
- }
68
- },
69
- // Wait patterns
70
- {
71
- pattern: /^wait\s+(?:for\s+)?(\d+)\s*(?:ms|milliseconds?|s|seconds?)?/i,
72
- type: 'wait',
73
- extract: (match) => {
74
- let duration = parseInt(match[1]);
75
- const unit = match[0].toLowerCase();
76
- if (unit.includes('s') && !unit.includes('ms')) {
77
- duration *= 1000;
78
- }
79
- return { duration };
80
- }
81
- },
82
- {
83
- pattern: /^wait\s+(?:for\s+)?(?:the\s+)?(.+?)(?:\s+to\s+(?:appear|load|show))?$/i,
84
- type: 'waitFor',
85
- extract: (match) => ({ target: match[1].trim() })
86
- },
87
- // Find/Search patterns
88
- {
89
- pattern: /^(find|search|look for|locate)\s+(?:the\s+)?(.+)/i,
90
- type: 'find',
91
- extract: (match) => ({ query: match[2].trim() })
92
- },
93
- // Hover patterns
94
- {
95
- pattern: /^(hover|mouse over|move to)\s+(?:the\s+)?(.+)/i,
96
- type: 'hover',
97
- extract: (match) => ({ target: match[2].trim() })
98
- },
99
- // Clear patterns
100
- {
101
- pattern: /^(clear|empty|delete)\s+(?:the\s+)?(.+)/i,
102
- type: 'clear',
103
- extract: (match) => ({ target: match[2].trim() })
104
- },
105
- // Submit patterns
106
- {
107
- pattern: /^submit\s+(?:the\s+)?(?:form)?(.*)$/i,
108
- type: 'submit',
109
- extract: (match) => ({ target: match[1].trim() || 'form' })
110
- },
111
- // Screenshot patterns
112
- {
113
- pattern: /^(take|capture)\s+(?:a\s+)?screenshot/i,
114
- type: 'screenshot',
115
- extract: () => ({})
116
- },
117
- // Go back/forward patterns
118
- {
119
- pattern: /^go\s+(back|forward)/i,
120
- type: 'navigation',
121
- extract: (match) => ({ direction: match[1].toLowerCase() })
122
- },
123
- // Refresh patterns
124
- {
125
- pattern: /^(refresh|reload)\s*(?:the\s+)?(?:page)?/i,
126
- type: 'refresh',
127
- extract: () => ({})
128
- }
129
- ];
130
-
131
- // Context variable patterns (for substitution)
132
- this.variablePattern = /\{(\w+)\}/g;
133
- }
134
-
135
- /**
136
- * Parse a natural language command
137
- */
138
- async parse(command, context = {}) {
139
- // Substitute context variables
140
- let processedCommand = command.replace(this.variablePattern, (match, varName) => {
141
- return context[varName] !== undefined ? context[varName] : match;
142
- });
143
-
144
- // Trim and normalize
145
- processedCommand = processedCommand.trim();
146
-
147
- // Try each pattern
148
- for (const pattern of this.actionPatterns) {
149
- const match = processedCommand.match(pattern.pattern);
150
- if (match) {
151
- const extracted = pattern.extract(match);
152
- return {
153
- type: pattern.type,
154
- ...extracted,
155
- originalCommand: command,
156
- confidence: 0.9
157
- };
158
- }
159
- }
160
-
161
- // Fallback: Try to guess action from keywords
162
- const fallback = this.guessFallback(processedCommand);
163
- if (fallback) {
164
- return {
165
- ...fallback,
166
- originalCommand: command,
167
- confidence: 0.5
168
- };
169
- }
170
-
171
- // Could not parse
172
- return {
173
- type: 'unknown',
174
- originalCommand: command,
175
- confidence: 0,
176
- error: 'Could not understand command'
177
- };
178
- }
179
-
180
- /**
181
- * Try to guess action from keywords
182
- */
183
- guessFallback(command) {
184
- const lower = command.toLowerCase();
185
-
186
- // Check for action keywords
187
- if (lower.includes('button') || lower.includes('link') || lower.includes('click')) {
188
- return { type: 'click', target: command };
189
- }
190
-
191
- if (lower.includes('type') || lower.includes('enter') || lower.includes('input')) {
192
- return { type: 'find', query: command, suggestedAction: 'type' };
193
- }
194
-
195
- if (lower.includes('search') || lower.includes('find') || lower.includes('look')) {
196
- return { type: 'find', query: command };
197
- }
198
-
199
- if (lower.includes('scroll')) {
200
- return { type: 'scroll', direction: 'down', amount: 300 };
201
- }
202
-
203
- // Default to find
204
- return { type: 'find', query: command };
205
- }
206
-
207
- /**
208
- * Parse multiple commands (separated by 'then', 'and', or newlines)
209
- */
210
- async parseMultiple(commands, context = {}) {
211
- // Split by separators
212
- const parts = commands.split(/\s+(?:then|and)\s+|\n|;/i).filter(p => p.trim());
213
-
214
- const results = [];
215
- for (const part of parts) {
216
- const parsed = await this.parse(part.trim(), context);
217
- results.push(parsed);
218
- }
219
-
220
- return results;
221
- }
222
-
223
- /**
224
- * Get suggestions for incomplete commands
225
- */
226
- getSuggestions(partialCommand) {
227
- const lower = partialCommand.toLowerCase();
228
- const suggestions = [];
229
-
230
- if (lower.startsWith('click')) {
231
- suggestions.push(
232
- 'click the login button',
233
- 'click the submit button',
234
- 'click the link',
235
- 'click the menu'
236
- );
237
- } else if (lower.startsWith('type')) {
238
- suggestions.push(
239
- 'type "text" in the search box',
240
- 'type "username" in the login field',
241
- 'type "hello" in the input'
242
- );
243
- } else if (lower.startsWith('go')) {
244
- suggestions.push(
245
- 'go to google.com',
246
- 'go back',
247
- 'go forward'
248
- );
249
- } else if (lower.startsWith('scroll')) {
250
- suggestions.push(
251
- 'scroll down',
252
- 'scroll up',
253
- 'scroll to the bottom',
254
- 'scroll to the top'
255
- );
256
- } else if (lower.startsWith('wait')) {
257
- suggestions.push(
258
- 'wait 2 seconds',
259
- 'wait for the button to appear',
260
- 'wait 500ms'
261
- );
262
- } else if (lower.startsWith('find')) {
263
- suggestions.push(
264
- 'find the login button',
265
- 'find the search input',
266
- 'find all links'
267
- );
268
- }
269
-
270
- return suggestions;
271
- }
272
- }
273
-
274
- module.exports = ActionParser;
package/src/ai/core.js DELETED
@@ -1,378 +0,0 @@
1
- /**
2
- * AI Core Module - Foundation for all AI-powered features
3
- *
4
- * This module provides AI capabilities that are automatically available
5
- * to ALL tools in the project. Any new tool added will automatically
6
- * benefit from these AI features.
7
- *
8
- * Features:
9
- * - Smart element finding with multiple strategies
10
- * - Auto-healing selectors when they break
11
- * - Page understanding and structure analysis
12
- * - Natural language command parsing
13
- * - Confidence scoring for element matches
14
- * - Fallback strategies when primary method fails
15
- */
16
-
17
- const ElementFinder = require('./element-finder');
18
- const SelectorHealer = require('./selector-healer');
19
- const PageAnalyzer = require('./page-analyzer');
20
- const ActionParser = require('./action-parser');
21
-
22
- /**
23
- * AI Core class - Central AI intelligence for the browser automation
24
- */
25
- class AICore {
26
- constructor() {
27
- this.elementFinder = new ElementFinder();
28
- this.selectorHealer = new SelectorHealer();
29
- this.pageAnalyzer = new PageAnalyzer();
30
- this.actionParser = new ActionParser();
31
-
32
- // Cache for performance
33
- this.pageCache = new Map();
34
- this.selectorCache = new Map();
35
-
36
- // Configuration
37
- this.config = {
38
- defaultConfidence: 0.7,
39
- maxCacheAge: 30000, // 30 seconds
40
- enableAutoHeal: true,
41
- enableSmartFind: true,
42
- logLevel: 'info' // 'debug' | 'info' | 'warn' | 'error'
43
- };
44
- }
45
-
46
- /**
47
- * Configure AI Core settings
48
- */
49
- configure(options = {}) {
50
- this.config = { ...this.config, ...options };
51
- return this;
52
- }
53
-
54
- /**
55
- * AI-Enhanced element finding
56
- * Tries multiple strategies to find an element
57
- */
58
- async smartFind(page, query, options = {}) {
59
- const {
60
- strategy = 'auto',
61
- context = null,
62
- confidence = this.config.defaultConfidence,
63
- returnMultiple = false
64
- } = options;
65
-
66
- this.log('debug', `SmartFind: "${query}" with strategy: ${strategy}`);
67
-
68
- const results = await this.elementFinder.find(page, query, {
69
- strategy,
70
- context,
71
- confidence,
72
- returnMultiple
73
- });
74
-
75
- this.log('info', `SmartFind found ${results.length} elements with confidence >= ${confidence}`);
76
-
77
- return results;
78
- }
79
-
80
- /**
81
- * AI-Enhanced click with auto-healing
82
- * If selector fails, tries to find the element using AI
83
- */
84
- async smartClick(page, selector, options = {}) {
85
- const { humanLike = true, autoHeal = this.config.enableAutoHeal } = options;
86
-
87
- try {
88
- // Try original selector first
89
- const element = await page.$(selector);
90
- if (element) {
91
- if (humanLike) {
92
- try {
93
- const { createCursor } = require('ghost-cursor');
94
- const cursor = createCursor(page);
95
- await cursor.click(selector);
96
- } catch {
97
- await element.click();
98
- }
99
- } else {
100
- await element.click();
101
- }
102
- return { success: true, selector, healed: false };
103
- }
104
- } catch (e) {
105
- this.log('warn', `Original selector failed: ${selector}`);
106
- }
107
-
108
- // Auto-heal if enabled
109
- if (autoHeal) {
110
- this.log('info', `Attempting to heal selector: ${selector}`);
111
- const healed = await this.healAndExecute(page, selector, 'click', options);
112
- if (healed.success) {
113
- return { ...healed, healed: true };
114
- }
115
- }
116
-
117
- return { success: false, error: `Element not found: ${selector}` };
118
- }
119
-
120
- /**
121
- * AI-Enhanced type with auto-healing
122
- */
123
- async smartType(page, selector, text, options = {}) {
124
- const { delay = 50, clear = false, autoHeal = this.config.enableAutoHeal } = options;
125
-
126
- try {
127
- const element = await page.$(selector);
128
- if (element) {
129
- if (clear) {
130
- await element.click({ clickCount: 3 });
131
- await page.keyboard.press('Backspace');
132
- }
133
- await element.type(text, { delay });
134
- return { success: true, selector, healed: false };
135
- }
136
- } catch (e) {
137
- this.log('warn', `Original selector failed: ${selector}`);
138
- }
139
-
140
- if (autoHeal) {
141
- const healed = await this.healAndExecute(page, selector, 'type', { text, ...options });
142
- if (healed.success) {
143
- return { ...healed, healed: true };
144
- }
145
- }
146
-
147
- return { success: false, error: `Element not found: ${selector}` };
148
- }
149
-
150
- /**
151
- * Heal a broken selector and execute action
152
- */
153
- async healAndExecute(page, brokenSelector, action, options = {}) {
154
- const alternatives = await this.selectorHealer.heal(page, brokenSelector, {
155
- maxAlternatives: 5
156
- });
157
-
158
- for (const alt of alternatives) {
159
- try {
160
- const element = await page.$(alt.selector);
161
- if (element) {
162
- this.log('info', `Healed selector: ${brokenSelector} -> ${alt.selector} (confidence: ${alt.confidence})`);
163
-
164
- // Cache the healed selector
165
- this.selectorCache.set(brokenSelector, {
166
- healed: alt.selector,
167
- timestamp: Date.now()
168
- });
169
-
170
- // Execute action
171
- if (action === 'click') {
172
- await element.click();
173
- } else if (action === 'type') {
174
- if (options.clear) {
175
- await element.click({ clickCount: 3 });
176
- await page.keyboard.press('Backspace');
177
- }
178
- await element.type(options.text, { delay: options.delay || 50 });
179
- }
180
-
181
- return { success: true, selector: alt.selector, originalSelector: brokenSelector };
182
- }
183
- } catch (e) {
184
- continue;
185
- }
186
- }
187
-
188
- return { success: false, error: 'Could not heal selector' };
189
- }
190
-
191
- /**
192
- * Understand page structure
193
- */
194
- async understandPage(page, options = {}) {
195
- const cacheKey = page.url();
196
- const cached = this.pageCache.get(cacheKey);
197
-
198
- if (cached && (Date.now() - cached.timestamp) < this.config.maxCacheAge) {
199
- this.log('debug', 'Using cached page analysis');
200
- return cached.analysis;
201
- }
202
-
203
- const analysis = await this.pageAnalyzer.analyze(page, options);
204
-
205
- this.pageCache.set(cacheKey, {
206
- analysis,
207
- timestamp: Date.now()
208
- });
209
-
210
- return analysis;
211
- }
212
-
213
- /**
214
- * Parse natural language command and execute
215
- */
216
- async executeCommand(page, command, options = {}) {
217
- const { context = {}, dryRun = false, humanLike = true } = options;
218
-
219
- this.log('info', `Parsing command: "${command}"`);
220
-
221
- const parsed = await this.actionParser.parse(command, context);
222
-
223
- if (dryRun) {
224
- return { success: true, dryRun: true, parsed };
225
- }
226
-
227
- // Execute parsed action
228
- return await this.executeAction(page, parsed, { humanLike });
229
- }
230
-
231
- /**
232
- * Execute a parsed action
233
- */
234
- async executeAction(page, action, options = {}) {
235
- const { humanLike = true } = options;
236
-
237
- switch (action.type) {
238
- case 'click':
239
- return await this.smartClick(page, action.target, { humanLike });
240
-
241
- case 'type':
242
- return await this.smartType(page, action.target, action.text, { humanLike });
243
-
244
- case 'navigate':
245
- await page.goto(action.url, { waitUntil: 'networkidle2' });
246
- return { success: true, url: action.url };
247
-
248
- case 'scroll':
249
- await page.evaluate((direction, amount) => {
250
- window.scrollBy({ top: direction === 'up' ? -amount : amount, behavior: 'smooth' });
251
- }, action.direction || 'down', action.amount || 300);
252
- return { success: true, direction: action.direction };
253
-
254
- case 'wait':
255
- await new Promise(r => setTimeout(r, action.duration || 1000));
256
- return { success: true, waited: action.duration };
257
-
258
- case 'find':
259
- const results = await this.smartFind(page, action.query);
260
- return { success: true, found: results.length, elements: results };
261
-
262
- default:
263
- return { success: false, error: `Unknown action type: ${action.type}` };
264
- }
265
- }
266
-
267
- /**
268
- * Wrap any handler with AI capabilities
269
- * This allows existing handlers to benefit from AI features
270
- */
271
- wrapHandler(handler, handlerName) {
272
- const aiCore = this;
273
-
274
- return async function aiEnhancedHandler(params = {}) {
275
- const startTime = Date.now();
276
-
277
- // Check if AI features are requested
278
- const useAI = params._useAI !== false;
279
- const autoHeal = params._autoHeal !== false && aiCore.config.enableAutoHeal;
280
-
281
- try {
282
- // Execute original handler
283
- const result = await handler(params);
284
-
285
- // If success, return result
286
- if (result.success) {
287
- return {
288
- ...result,
289
- _ai: { used: false, duration: Date.now() - startTime }
290
- };
291
- }
292
-
293
- // If failed and autoHeal is enabled, try AI recovery
294
- if (autoHeal && result.error?.includes('not found')) {
295
- aiCore.log('info', `AI attempting recovery for ${handlerName}`);
296
-
297
- // Extract selector from params
298
- const selector = params.selector || params.target;
299
- if (selector) {
300
- const healed = await aiCore.selectorHealer.heal(
301
- params._page,
302
- selector,
303
- { maxAlternatives: 3 }
304
- );
305
-
306
- if (healed.length > 0) {
307
- // Retry with healed selector
308
- const retryParams = { ...params, selector: healed[0].selector };
309
- const retryResult = await handler(retryParams);
310
-
311
- return {
312
- ...retryResult,
313
- _ai: {
314
- used: true,
315
- healed: true,
316
- originalSelector: selector,
317
- healedSelector: healed[0].selector,
318
- duration: Date.now() - startTime
319
- }
320
- };
321
- }
322
- }
323
- }
324
-
325
- return {
326
- ...result,
327
- _ai: { used: useAI, duration: Date.now() - startTime }
328
- };
329
-
330
- } catch (error) {
331
- aiCore.log('error', `Handler ${handlerName} failed: ${error.message}`);
332
- return {
333
- success: false,
334
- error: error.message,
335
- _ai: { used: useAI, duration: Date.now() - startTime }
336
- };
337
- }
338
- };
339
- }
340
-
341
- /**
342
- * Clear caches
343
- */
344
- clearCache() {
345
- this.pageCache.clear();
346
- this.selectorCache.clear();
347
- this.log('info', 'AI caches cleared');
348
- }
349
-
350
- /**
351
- * Logging utility
352
- */
353
- log(level, message) {
354
- const levels = ['debug', 'info', 'warn', 'error'];
355
- const configLevel = levels.indexOf(this.config.logLevel);
356
- const msgLevel = levels.indexOf(level);
357
-
358
- if (msgLevel >= configLevel) {
359
- const emoji = { debug: '🔍', info: '🤖', warn: '⚠️', error: '❌' }[level];
360
- console.error(`${emoji} [AI] ${message}`);
361
- }
362
- }
363
- }
364
-
365
- // Singleton instance
366
- const aiCore = new AICore();
367
-
368
- module.exports = {
369
- AICore,
370
- aiCore,
371
- smartFind: (page, query, options) => aiCore.smartFind(page, query, options),
372
- smartClick: (page, selector, options) => aiCore.smartClick(page, selector, options),
373
- smartType: (page, selector, text, options) => aiCore.smartType(page, selector, text, options),
374
- understandPage: (page, options) => aiCore.understandPage(page, options),
375
- executeCommand: (page, command, options) => aiCore.executeCommand(page, command, options),
376
- wrapHandler: (handler, name) => aiCore.wrapHandler(handler, name),
377
- configure: (options) => aiCore.configure(options)
378
- };