real-browser-mcp-server 1.3.3 → 1.4.0

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 (118) 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 +386 -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 +232 -0
  21. package/dist/src/mcp/handlers/browser.js.map +1 -0
  22. package/dist/src/mcp/handlers/dom.d.ts +149 -0
  23. package/dist/src/mcp/handlers/dom.d.ts.map +1 -0
  24. package/dist/src/mcp/handlers/dom.js +577 -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 +226 -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 +549 -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 +606 -0
  81. package/dist/src/shared/tools.js.map +1 -0
  82. package/dist/src/types.d.ts +376 -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 +230 -154
  95. package/lib/esm/module/pageController.mjs +21 -18
  96. package/lib/esm/module/turnstile.mjs +7 -0
  97. package/package.json +25 -16
  98. package/typings.d.ts +7 -1
  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 -321
  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
@@ -1,304 +0,0 @@
1
- /**
2
- * AI Page Analyzer - Understand page structure and content
3
- *
4
- * Analyzes page to identify:
5
- * - Interactive elements (buttons, links, inputs)
6
- * - Forms and form fields
7
- * - Navigation structure
8
- * - Main content areas
9
- * - Media elements
10
- */
11
-
12
- class PageAnalyzer {
13
- constructor() {
14
- this.analysisTypes = ['full', 'interactive', 'forms', 'navigation', 'content', 'media'];
15
- }
16
-
17
- /**
18
- * Analyze page structure
19
- */
20
- async analyze(page, options = {}) {
21
- const {
22
- analysisType = 'full',
23
- includeSelectors = true,
24
- includeScreenshot = false,
25
- maxDepth = 10
26
- } = options;
27
-
28
- const analysis = await page.evaluate(({ analysisType, includeSelectors, maxDepth }) => {
29
- const result = {
30
- url: window.location.href,
31
- title: document.title,
32
- timestamp: new Date().toISOString(),
33
- viewport: {
34
- width: window.innerWidth,
35
- height: window.innerHeight
36
- }
37
- };
38
-
39
- // Helper to generate selector
40
- const getSelector = (el) => {
41
- if (!includeSelectors) return null;
42
- if (el.id) return `#${el.id}`;
43
- if (el.name) return `[name="${el.name}"]`;
44
- if (el.className && typeof el.className === 'string') {
45
- const cls = el.className.split(' ').filter(c => c)[0];
46
- if (cls) return `${el.tagName.toLowerCase()}.${cls}`;
47
- }
48
- return el.tagName.toLowerCase();
49
- };
50
-
51
- // Analyze interactive elements
52
- if (analysisType === 'full' || analysisType === 'interactive') {
53
- result.interactive = {
54
- buttons: [],
55
- links: [],
56
- inputs: []
57
- };
58
-
59
- // Buttons
60
- document.querySelectorAll('button, [role="button"], input[type="button"], input[type="submit"]').forEach(el => {
61
- const rect = el.getBoundingClientRect();
62
- if (rect.width > 0 && rect.height > 0) {
63
- result.interactive.buttons.push({
64
- selector: getSelector(el),
65
- text: el.textContent?.trim().substring(0, 50),
66
- type: el.type || 'button',
67
- disabled: el.disabled,
68
- visible: true
69
- });
70
- }
71
- });
72
-
73
- // Links
74
- document.querySelectorAll('a[href]').forEach(el => {
75
- const rect = el.getBoundingClientRect();
76
- if (rect.width > 0 && rect.height > 0) {
77
- result.interactive.links.push({
78
- selector: getSelector(el),
79
- text: el.textContent?.trim().substring(0, 50),
80
- href: el.href,
81
- target: el.target || '_self',
82
- visible: true
83
- });
84
- }
85
- });
86
-
87
- // Inputs
88
- document.querySelectorAll('input, textarea, select').forEach(el => {
89
- const rect = el.getBoundingClientRect();
90
- if (rect.width > 0 && rect.height > 0) {
91
- result.interactive.inputs.push({
92
- selector: getSelector(el),
93
- type: el.type || 'text',
94
- name: el.name,
95
- placeholder: el.placeholder,
96
- required: el.required,
97
- disabled: el.disabled,
98
- value: el.type === 'password' ? '***' : el.value?.substring(0, 20),
99
- visible: true
100
- });
101
- }
102
- });
103
- }
104
-
105
- // Analyze forms
106
- if (analysisType === 'full' || analysisType === 'forms') {
107
- result.forms = [];
108
-
109
- document.querySelectorAll('form').forEach(form => {
110
- const formData = {
111
- selector: getSelector(form),
112
- action: form.action,
113
- method: form.method,
114
- fields: []
115
- };
116
-
117
- form.querySelectorAll('input, textarea, select').forEach(field => {
118
- formData.fields.push({
119
- selector: getSelector(field),
120
- type: field.type || field.tagName.toLowerCase(),
121
- name: field.name,
122
- label: document.querySelector(`label[for="${field.id}"]`)?.textContent?.trim(),
123
- required: field.required,
124
- placeholder: field.placeholder
125
- });
126
- });
127
-
128
- // Find submit button
129
- const submitBtn = form.querySelector('button[type="submit"], input[type="submit"], button:not([type])');
130
- if (submitBtn) {
131
- formData.submitButton = {
132
- selector: getSelector(submitBtn),
133
- text: submitBtn.textContent?.trim() || submitBtn.value
134
- };
135
- }
136
-
137
- result.forms.push(formData);
138
- });
139
- }
140
-
141
- // Analyze navigation
142
- if (analysisType === 'full' || analysisType === 'navigation') {
143
- result.navigation = {
144
- menus: [],
145
- breadcrumbs: null,
146
- pagination: null
147
- };
148
-
149
- // Find menus
150
- document.querySelectorAll('nav, [role="navigation"], .nav, .menu, header ul').forEach(nav => {
151
- const links = [];
152
- nav.querySelectorAll('a').forEach(a => {
153
- links.push({
154
- text: a.textContent?.trim().substring(0, 30),
155
- href: a.href,
156
- selector: getSelector(a)
157
- });
158
- });
159
-
160
- if (links.length > 0) {
161
- result.navigation.menus.push({
162
- selector: getSelector(nav),
163
- type: nav.tagName.toLowerCase(),
164
- links: links.slice(0, 20)
165
- });
166
- }
167
- });
168
-
169
- // Find breadcrumbs
170
- const breadcrumb = document.querySelector('[aria-label*="breadcrumb"], .breadcrumb, .breadcrumbs');
171
- if (breadcrumb) {
172
- result.navigation.breadcrumbs = {
173
- selector: getSelector(breadcrumb),
174
- items: Array.from(breadcrumb.querySelectorAll('a, span')).map(el => el.textContent?.trim()).filter(Boolean)
175
- };
176
- }
177
-
178
- // Find pagination
179
- const pagination = document.querySelector('.pagination, [aria-label*="pagination"], nav[role="navigation"] a[href*="page"]');
180
- if (pagination) {
181
- result.navigation.pagination = {
182
- selector: getSelector(pagination.closest('nav, .pagination, div')),
183
- found: true
184
- };
185
- }
186
- }
187
-
188
- // Analyze main content
189
- if (analysisType === 'full' || analysisType === 'content') {
190
- result.content = {
191
- headings: [],
192
- paragraphs: 0,
193
- mainContent: null
194
- };
195
-
196
- // Headings
197
- document.querySelectorAll('h1, h2, h3').forEach(h => {
198
- result.content.headings.push({
199
- level: parseInt(h.tagName[1]),
200
- text: h.textContent?.trim().substring(0, 100),
201
- selector: getSelector(h)
202
- });
203
- });
204
-
205
- // Count paragraphs
206
- result.content.paragraphs = document.querySelectorAll('p').length;
207
-
208
- // Find main content area
209
- const main = document.querySelector('main, [role="main"], article, .content, #content');
210
- if (main) {
211
- result.content.mainContent = {
212
- selector: getSelector(main),
213
- textLength: main.textContent?.length || 0
214
- };
215
- }
216
- }
217
-
218
- // Analyze media
219
- if (analysisType === 'full' || analysisType === 'media') {
220
- result.media = {
221
- images: [],
222
- videos: [],
223
- iframes: []
224
- };
225
-
226
- // Images
227
- document.querySelectorAll('img').forEach(img => {
228
- const rect = img.getBoundingClientRect();
229
- if (rect.width > 50 && rect.height > 50) {
230
- result.media.images.push({
231
- selector: getSelector(img),
232
- src: img.src?.substring(0, 100),
233
- alt: img.alt,
234
- width: Math.round(rect.width),
235
- height: Math.round(rect.height)
236
- });
237
- }
238
- });
239
-
240
- // Videos
241
- document.querySelectorAll('video').forEach(video => {
242
- result.media.videos.push({
243
- selector: getSelector(video),
244
- src: video.src || video.querySelector('source')?.src,
245
- duration: video.duration,
246
- controls: video.controls
247
- });
248
- });
249
-
250
- // Iframes (potential embedded content)
251
- document.querySelectorAll('iframe').forEach(iframe => {
252
- result.media.iframes.push({
253
- selector: getSelector(iframe),
254
- src: iframe.src?.substring(0, 100),
255
- title: iframe.title
256
- });
257
- });
258
- }
259
-
260
- // Summary stats
261
- result.summary = {
262
- totalButtons: result.interactive?.buttons?.length || 0,
263
- totalLinks: result.interactive?.links?.length || 0,
264
- totalInputs: result.interactive?.inputs?.length || 0,
265
- totalForms: result.forms?.length || 0,
266
- totalImages: result.media?.images?.length || 0,
267
- hasNavigation: (result.navigation?.menus?.length || 0) > 0,
268
- isLoginPage: !!(result.interactive?.inputs?.find(i => i.type === 'password')),
269
- isSearchPage: !!(result.interactive?.inputs?.find(i => i.type === 'search' || i.name?.includes('search') || i.placeholder?.toLowerCase().includes('search')))
270
- };
271
-
272
- return result;
273
- }, { analysisType, includeSelectors, maxDepth });
274
-
275
- // Add screenshot if requested
276
- if (includeScreenshot) {
277
- const screenshot = await page.screenshot({ encoding: 'base64', type: 'jpeg', quality: 50 });
278
- analysis.screenshot = screenshot;
279
- }
280
-
281
- return analysis;
282
- }
283
-
284
- /**
285
- * Get quick summary of page
286
- */
287
- async quickSummary(page) {
288
- return await page.evaluate(() => {
289
- return {
290
- url: window.location.href,
291
- title: document.title,
292
- buttons: document.querySelectorAll('button, [role="button"]').length,
293
- links: document.querySelectorAll('a[href]').length,
294
- inputs: document.querySelectorAll('input, textarea').length,
295
- forms: document.querySelectorAll('form').length,
296
- images: document.querySelectorAll('img').length,
297
- hasLogin: !!document.querySelector('input[type="password"]'),
298
- hasSearch: !!document.querySelector('input[type="search"], [name*="search"], [placeholder*="earch"]')
299
- };
300
- });
301
- }
302
- }
303
-
304
- module.exports = PageAnalyzer;
@@ -1,236 +0,0 @@
1
- /**
2
- * AI Selector Healer - Auto-fix broken CSS selectors
3
- *
4
- * When a selector stops working (page updated, dynamic content, etc.),
5
- * this module tries to find alternative selectors that match the same element.
6
- */
7
-
8
- class SelectorHealer {
9
- constructor() {
10
- this.healingStrategies = [
11
- 'byId',
12
- 'byName',
13
- 'byAriaLabel',
14
- 'byText',
15
- 'byClasses',
16
- 'byAttributes',
17
- 'byStructure',
18
- 'byPosition'
19
- ];
20
- }
21
-
22
- /**
23
- * Heal a broken selector by finding alternatives
24
- */
25
- async heal(page, brokenSelector, options = {}) {
26
- const {
27
- lastKnownText = null,
28
- lastKnownAttributes = {},
29
- elementType = null,
30
- maxAlternatives = 5
31
- } = options;
32
-
33
- const context = {
34
- brokenSelector,
35
- lastKnownText,
36
- lastKnownAttributes,
37
- elementType
38
- };
39
-
40
- // Try to extract info from broken selector
41
- const selectorInfo = this.parseSelector(brokenSelector);
42
-
43
- // Find alternatives using multiple strategies
44
- const alternatives = await page.evaluate(({ context, selectorInfo }) => {
45
- const results = [];
46
-
47
- // Strategy 1: Find by similar ID
48
- if (selectorInfo.id) {
49
- const elements = document.querySelectorAll(`[id*="${selectorInfo.id}"]`);
50
- for (const el of elements) {
51
- results.push({
52
- selector: `#${el.id}`,
53
- confidence: 0.9,
54
- strategy: 'byId',
55
- reason: 'Similar ID found'
56
- });
57
- }
58
- }
59
-
60
- // Strategy 2: Find by name attribute
61
- if (selectorInfo.name) {
62
- const elements = document.querySelectorAll(`[name*="${selectorInfo.name}"]`);
63
- for (const el of elements) {
64
- results.push({
65
- selector: `[name="${el.name}"]`,
66
- confidence: 0.85,
67
- strategy: 'byName',
68
- reason: 'Similar name attribute found'
69
- });
70
- }
71
- }
72
-
73
- // Strategy 3: Find by last known text
74
- if (context.lastKnownText) {
75
- const allElements = document.querySelectorAll('button, a, input, label, span, div, [role]');
76
- for (const el of allElements) {
77
- const text = el.textContent?.trim();
78
- if (text && text.toLowerCase().includes(context.lastKnownText.toLowerCase())) {
79
- let selector = '';
80
- if (el.id) {
81
- selector = `#${el.id}`;
82
- } else if (el.className) {
83
- selector = `${el.tagName.toLowerCase()}.${el.className.split(' ')[0]}`;
84
- } else {
85
- selector = `${el.tagName.toLowerCase()}`;
86
- }
87
-
88
- results.push({
89
- selector,
90
- confidence: 0.8,
91
- strategy: 'byText',
92
- reason: `Contains text: "${text.substring(0, 30)}"`
93
- });
94
- }
95
- }
96
- }
97
-
98
- // Strategy 4: Find by similar classes
99
- if (selectorInfo.classes.length > 0) {
100
- for (const cls of selectorInfo.classes) {
101
- const elements = document.querySelectorAll(`[class*="${cls}"]`);
102
- for (const el of elements) {
103
- const rect = el.getBoundingClientRect();
104
- if (rect.width > 0 && rect.height > 0) {
105
- results.push({
106
- selector: `.${cls}`,
107
- confidence: 0.7,
108
- strategy: 'byClasses',
109
- reason: `Similar class: ${cls}`
110
- });
111
- }
112
- }
113
- }
114
- }
115
-
116
- // Strategy 5: Find by tag and type
117
- if (selectorInfo.tag) {
118
- let selector = selectorInfo.tag;
119
- if (selectorInfo.type) {
120
- selector += `[type="${selectorInfo.type}"]`;
121
- }
122
-
123
- const elements = document.querySelectorAll(selector);
124
- for (const el of elements) {
125
- const rect = el.getBoundingClientRect();
126
- if (rect.width > 0 && rect.height > 0) {
127
- let uniqueSelector = selector;
128
- if (el.placeholder) {
129
- uniqueSelector += `[placeholder="${el.placeholder}"]`;
130
- } else if (el.value) {
131
- uniqueSelector += `[value="${el.value}"]`;
132
- }
133
-
134
- results.push({
135
- selector: uniqueSelector,
136
- confidence: 0.6,
137
- strategy: 'byAttributes',
138
- reason: `Similar element structure`
139
- });
140
- }
141
- }
142
- }
143
-
144
- // Strategy 6: Find by aria-label
145
- if (context.lastKnownAttributes?.['aria-label']) {
146
- const label = context.lastKnownAttributes['aria-label'];
147
- const elements = document.querySelectorAll(`[aria-label*="${label}"]`);
148
- for (const el of elements) {
149
- results.push({
150
- selector: `[aria-label="${el.getAttribute('aria-label')}"]`,
151
- confidence: 0.85,
152
- strategy: 'byAriaLabel',
153
- reason: 'Similar aria-label found'
154
- });
155
- }
156
- }
157
-
158
- // Deduplicate
159
- const seen = new Set();
160
- return results.filter(r => {
161
- if (seen.has(r.selector)) return false;
162
- seen.add(r.selector);
163
- return true;
164
- });
165
-
166
- }, { context, selectorInfo });
167
-
168
- // Sort by confidence and return top alternatives
169
- return alternatives
170
- .sort((a, b) => b.confidence - a.confidence)
171
- .slice(0, maxAlternatives);
172
- }
173
-
174
- /**
175
- * Parse a CSS selector to extract components
176
- */
177
- parseSelector(selector) {
178
- const info = {
179
- tag: null,
180
- id: null,
181
- classes: [],
182
- name: null,
183
- type: null,
184
- attributes: {}
185
- };
186
-
187
- // Extract ID
188
- const idMatch = selector.match(/#([a-zA-Z0-9_-]+)/);
189
- if (idMatch) info.id = idMatch[1];
190
-
191
- // Extract classes
192
- const classMatches = selector.matchAll(/\.([a-zA-Z0-9_-]+)/g);
193
- for (const match of classMatches) {
194
- info.classes.push(match[1]);
195
- }
196
-
197
- // Extract tag
198
- const tagMatch = selector.match(/^([a-zA-Z]+)/);
199
- if (tagMatch) info.tag = tagMatch[1];
200
-
201
- // Extract attributes
202
- const attrMatches = selector.matchAll(/\[([a-zA-Z-]+)(?:=["']?([^"'\]]+)["']?)?\]/g);
203
- for (const match of attrMatches) {
204
- const key = match[1];
205
- const value = match[2] || true;
206
- info.attributes[key] = value;
207
-
208
- if (key === 'name') info.name = value;
209
- if (key === 'type') info.type = value;
210
- }
211
-
212
- return info;
213
- }
214
-
215
- /**
216
- * Test if a selector works
217
- */
218
- async testSelector(page, selector) {
219
- try {
220
- const element = await page.$(selector);
221
- if (!element) return { valid: false };
222
-
223
- const info = await element.evaluate(el => ({
224
- visible: el.offsetWidth > 0 && el.offsetHeight > 0,
225
- tag: el.tagName.toLowerCase(),
226
- text: el.textContent?.substring(0, 50)
227
- }));
228
-
229
- return { valid: true, ...info };
230
- } catch {
231
- return { valid: false };
232
- }
233
- }
234
- }
235
-
236
- module.exports = SelectorHealer;
package/src/index.js DELETED
@@ -1,121 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Real Browser - Unified Entry Point
4
- *
5
- * Usage:
6
- * node src/index.js - Start MCP Server (default)
7
- * node src/index.js --help - Show help
8
- * node src/index.js --list - List all available tools
9
- */
10
-
11
- const { TOOLS, TOOL_DISPLAY, CATEGORIES } = require('./shared/tools.js');
12
-
13
- // ANSI colors for terminal
14
- const colors = {
15
- reset: '\x1b[0m',
16
- bright: '\x1b[1m',
17
- dim: '\x1b[2m',
18
- green: '\x1b[32m',
19
- yellow: '\x1b[33m',
20
- blue: '\x1b[34m',
21
- magenta: '\x1b[35m',
22
- cyan: '\x1b[36m',
23
- red: '\x1b[31m',
24
- };
25
-
26
- /**
27
- * Display help message
28
- */
29
- function showHelp() {
30
- console.log(`
31
- ${colors.bright}${colors.cyan}🦁 Real Browser - Unified Server${colors.reset}
32
-
33
- ${colors.bright}USAGE:${colors.reset}
34
- node src/index.js [options]
35
-
36
- ${colors.bright}OPTIONS:${colors.reset}
37
- ${colors.yellow}--help, -h${colors.reset} Show this help message
38
- ${colors.yellow}--verbose, -v${colors.reset} Show detailed tool information
39
- ${colors.yellow}--list${colors.reset} List all available tools
40
-
41
- ${colors.bright}EXAMPLES:${colors.reset}
42
- node src/index.js # Start MCP server
43
- node src/index.js --list # List all tools
44
-
45
- ${colors.bright}NPM SCRIPTS:${colors.reset}
46
- npm run dev # Start MCP server
47
- npm run mcp # Start MCP server only
48
-
49
- ${colors.bright}ARCHITECTURE:${colors.reset}
50
- ${colors.cyan}MCP Server${colors.reset} → STDIO transport → AI Agents (Claude, Cursor, Copilot)
51
-
52
- ${colors.bright}TOOL CATEGORIES (${TOOLS.length} tools):${colors.reset}
53
- ${Object.entries(CATEGORIES).map(([key, cat]) => {
54
- const count = TOOLS.filter(t => t.category === key).length;
55
- return ` ${cat.emoji} ${colors.yellow}${cat.name.padEnd(15)}${colors.reset} ${colors.dim}(${count} tools)${colors.reset}`;
56
- }).join('\n')}
57
- `);
58
- }
59
-
60
- /**
61
- * List all available tools
62
- */
63
- function listTools() {
64
- console.log(`\n${colors.bright}${colors.cyan}🦁 Available Tools (${TOOLS.length}):${colors.reset}\n`);
65
-
66
- for (const [key, cat] of Object.entries(CATEGORIES)) {
67
- const tools = TOOLS.filter(t => t.category === key);
68
- if (tools.length === 0) continue;
69
-
70
- console.log(`${colors.bright}${cat.emoji} ${cat.name}${colors.reset} ${colors.dim}(${tools.length})${colors.reset}`);
71
- console.log(`${colors.dim}${'─'.repeat(50)}${colors.reset}`);
72
-
73
- for (const tool of tools) {
74
- console.log(` ${tool.emoji} ${colors.yellow}${tool.name.padEnd(25)}${colors.reset} ${colors.dim}${tool.description.substring(0, 40)}${colors.reset}`);
75
- }
76
- console.log('');
77
- }
78
- }
79
-
80
- /**
81
- * Main entry point
82
- */
83
- async function main() {
84
- const args = process.argv.slice(2);
85
-
86
- // Parse arguments
87
- const hasHelp = args.includes('--help') || args.includes('-h');
88
- const hasList = args.includes('--list');
89
-
90
- if (hasHelp) {
91
- showHelp();
92
- process.exit(0);
93
- }
94
-
95
- if (hasList) {
96
- listTools();
97
- process.exit(0);
98
- }
99
-
100
- console.error('🚀 Real Browser MCP Server starting...');
101
-
102
- // Import and run MCP server
103
- require('./mcp/index.js');
104
- }
105
-
106
- // Export for programmatic use
107
- module.exports = {
108
- TOOLS,
109
- TOOL_DISPLAY,
110
- CATEGORIES,
111
- startMCP: () => require('./mcp/index.js'),
112
- startBoth: () => require('./mcp/index.js'),
113
- };
114
-
115
- // Run if called directly
116
- if (require.main === module) {
117
- main().catch(error => {
118
- console.error(`${colors.red}❌ Fatal error:${colors.reset}`, error.message);
119
- process.exit(1);
120
- });
121
- }