brave-real-browser-mcp-server 2.14.8 → 2.14.9

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.
@@ -1,10 +1,5 @@
1
1
  // @ts-nocheck
2
2
  import { getPageInstance } from '../browser-manager.js';
3
- import natural from 'natural';
4
- import Sentiment from 'sentiment';
5
- import { franc } from 'franc';
6
- const sentiment = new Sentiment();
7
- const tokenizer = new natural.WordTokenizer();
8
3
  /**
9
4
  * Smart Selector Generator - AI-powered CSS selector generation
10
5
  */
@@ -136,201 +131,9 @@ export async function handleContentClassification(args) {
136
131
  /**
137
132
  * Sentiment Analysis - Analyze sentiment of page content
138
133
  */
139
- export async function handleSentimentAnalysis(args) {
140
- const { url, selector, text } = args;
141
- try {
142
- let contentToAnalyze = text;
143
- // If no text provided, get from page
144
- if (!contentToAnalyze) {
145
- const page = getPageInstance();
146
- if (!page) {
147
- throw new Error('Browser not initialized. Call browser_init first.');
148
- }
149
- // Navigate if URL provided and different from current
150
- if (url && page.url() !== url) {
151
- await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
152
- }
153
- // Extract content from selector or entire page
154
- if (selector) {
155
- contentToAnalyze = await page.evaluate((sel) => {
156
- const element = document.querySelector(sel);
157
- return element ? element.textContent : '';
158
- }, selector);
159
- }
160
- else {
161
- contentToAnalyze = await page.evaluate(() => document.body.innerText);
162
- }
163
- }
164
- if (!contentToAnalyze) {
165
- throw new Error('No content to analyze');
166
- }
167
- const result = sentiment.analyze(contentToAnalyze);
168
- // Additional analysis using natural
169
- const tokens = tokenizer.tokenize(contentToAnalyze);
170
- const sentenceTokenizer = new natural.SentenceTokenizer();
171
- const sentences = sentenceTokenizer.tokenize(contentToAnalyze);
172
- // Classify sentiment per sentence
173
- const sentenceSentiments = sentences.map(sentence => {
174
- const s = sentiment.analyze(sentence);
175
- return {
176
- sentence: sentence.substring(0, 100),
177
- score: s.score,
178
- sentiment: s.score > 0 ? 'positive' : s.score < 0 ? 'negative' : 'neutral'
179
- };
180
- });
181
- const stats = {
182
- totalSentences: sentences.length,
183
- positiveSentences: sentenceSentiments.filter(s => s.sentiment === 'positive').length,
184
- negativeSentences: sentenceSentiments.filter(s => s.sentiment === 'negative').length,
185
- neutralSentences: sentenceSentiments.filter(s => s.sentiment === 'neutral').length
186
- };
187
- const resultText = `✅ Sentiment Analysis\n\nOverall Sentiment: ${result.score > 0 ? 'Positive' : result.score < 0 ? 'Negative' : 'Neutral'}\nScore: ${result.score}\nComparative: ${result.comparative.toFixed(4)}\n\nStatistics:\n- Total Sentences: ${stats.totalSentences}\n- Positive: ${stats.positiveSentences}\n- Negative: ${stats.negativeSentences}\n- Neutral: ${stats.neutralSentences}\n\nPositive Words: ${result.positive.join(', ')}\nNegative Words: ${result.negative.join(', ')}`;
188
- return {
189
- content: [{
190
- type: 'text',
191
- text: resultText
192
- }]
193
- };
194
- }
195
- catch (error) {
196
- return { content: [{ type: 'text', text: `? Error: ${error.message}` }], isError: true };
197
- }
198
- }
199
134
  /**
200
135
  * Summary Generator - Generate summary of page content
201
136
  */
202
- export async function handleSummaryGenerator(args) {
203
- const { url, maxSentences = 5, selector } = args;
204
- try {
205
- const page = getPageInstance();
206
- if (!page) {
207
- throw new Error('Browser not initialized. Call browser_init first.');
208
- }
209
- if (url && page.url() !== url) {
210
- await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
211
- }
212
- let content;
213
- if (selector) {
214
- content = await page.evaluate((sel) => {
215
- const element = document.querySelector(sel);
216
- return element ? element.textContent : '';
217
- }, selector);
218
- }
219
- else {
220
- content = await page.evaluate(() => {
221
- // Extract main content
222
- const main = document.querySelector('main, article, .content, .post, #content');
223
- if (main)
224
- return main.textContent || '';
225
- return document.body.innerText;
226
- });
227
- }
228
- if (!content) {
229
- throw new Error('No content found');
230
- }
231
- // Use TF-IDF for extractive summarization
232
- const TfIdf = natural.TfIdf;
233
- const tfidf = new TfIdf();
234
- const sentenceTokenizer = new natural.SentenceTokenizer();
235
- const sentences = sentenceTokenizer.tokenize(content);
236
- if (sentences.length === 0) {
237
- throw new Error('No sentences found to summarize');
238
- }
239
- // Add each sentence as a document
240
- sentences.forEach(sentence => {
241
- tfidf.addDocument(sentence);
242
- });
243
- // Score each sentence
244
- const sentenceScores = sentences.map((sentence, idx) => {
245
- let score = 0;
246
- tfidf.listTerms(idx).forEach(term => {
247
- score += term.tfidf;
248
- });
249
- return { sentence, score, index: idx };
250
- });
251
- // Sort by score and take top N
252
- sentenceScores.sort((a, b) => b.score - a.score);
253
- const topSentences = sentenceScores.slice(0, maxSentences);
254
- // Sort by original order
255
- topSentences.sort((a, b) => a.index - b.index);
256
- const summary = topSentences.map(s => s.sentence).join(' ');
257
- const compressionRatio = (summary.length / content.length * 100).toFixed(2);
258
- const resultText = `✅ Summary Generated\n\nSummary:\n${summary}\n\nStatistics:\n- Original Length: ${content.length} characters\n- Summary Length: ${summary.length} characters\n- Compression Ratio: ${compressionRatio}%\n- Original Sentences: ${sentences.length}\n- Summary Sentences: ${topSentences.length}`;
259
- return {
260
- content: [{
261
- type: 'text',
262
- text: resultText
263
- }]
264
- };
265
- }
266
- catch (error) {
267
- return { content: [{ type: 'text', text: `? Error: ${error.message}` }], isError: true };
268
- }
269
- }
270
137
  /**
271
138
  * Translation Support - Detect language and provide translation info
272
139
  */
273
- export async function handleTranslationSupport(args) {
274
- const { url, selector, text, targetLanguage = 'en' } = args;
275
- try {
276
- let contentToTranslate = text;
277
- if (!contentToTranslate && url) {
278
- const page = getPageInstance();
279
- if (!page) {
280
- throw new Error('Browser not initialized. Call browser_init first.');
281
- }
282
- if (page.url() !== url) {
283
- await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
284
- }
285
- if (selector) {
286
- contentToTranslate = await page.evaluate((sel) => {
287
- const element = document.querySelector(sel);
288
- return element ? element.textContent : '';
289
- }, selector);
290
- }
291
- else {
292
- contentToTranslate = await page.evaluate(() => document.body.innerText);
293
- }
294
- }
295
- if (!contentToTranslate) {
296
- throw new Error('No content to translate');
297
- }
298
- // Detect language using franc
299
- const detectedLang = franc(contentToTranslate, { minLength: 10 });
300
- // Get language name
301
- const langNames = {
302
- 'eng': 'English',
303
- 'spa': 'Spanish',
304
- 'fra': 'French',
305
- 'deu': 'German',
306
- 'ita': 'Italian',
307
- 'por': 'Portuguese',
308
- 'rus': 'Russian',
309
- 'jpn': 'Japanese',
310
- 'kor': 'Korean',
311
- 'cmn': 'Chinese (Mandarin)',
312
- 'ara': 'Arabic',
313
- 'hin': 'Hindi',
314
- 'und': 'Undetermined'
315
- };
316
- const languageName = langNames[detectedLang] || detectedLang;
317
- // Extract key phrases using TF-IDF
318
- const TfIdf = natural.TfIdf;
319
- const tfidf = new TfIdf();
320
- tfidf.addDocument(contentToTranslate);
321
- const keyPhrases = tfidf.listTerms(0)
322
- .slice(0, 10)
323
- .map(term => term.term);
324
- const needsTranslation = detectedLang !== targetLanguage && detectedLang !== 'und';
325
- const resultText = `✅ Translation Support\n\nDetected Language: ${languageName} (${detectedLang})\nTarget Language: ${targetLanguage}\nNeeds Translation: ${needsTranslation ? 'Yes' : 'No'}\n\nContent Length: ${contentToTranslate.length} characters\nContent Preview: ${contentToTranslate.substring(0, 200)}...\n\nKey Phrases: ${keyPhrases.join(', ')}\n\nNote: Use external translation API (Google Translate, DeepL) for actual translation`;
326
- return {
327
- content: [{
328
- type: 'text',
329
- text: resultText
330
- }]
331
- };
332
- }
333
- catch (error) {
334
- return { content: [{ type: 'text', text: `? Error: ${error.message}` }], isError: true };
335
- }
336
- }
@@ -5,104 +5,6 @@
5
5
  import { getCurrentPage } from '../browser-manager.js';
6
6
  import { validateWorkflow } from '../workflow-validation.js';
7
7
  import { withErrorHandling } from '../system-utils.js';
8
- /**
9
- * HTML Tables से structured data extract करता है
10
- */
11
- export async function handleScrapeTable(args) {
12
- return await withErrorHandling(async () => {
13
- // Workflow validation
14
- validateWorkflow('scrape_table', {
15
- requireBrowser: true,
16
- requirePage: true,
17
- });
18
- const page = getCurrentPage();
19
- const selector = args.selector || 'table';
20
- const includeHeaders = args.includeHeaders !== false;
21
- const cleanData = args.cleanData !== false;
22
- const maxRows = args.maxRows || 1000;
23
- // Extract table data from page
24
- const tableData = await page.evaluate(({ selector, includeHeaders, cleanData, maxRows }) => {
25
- const tables = document.querySelectorAll(selector);
26
- const results = [];
27
- tables.forEach((table) => {
28
- const headers = [];
29
- const rows = [];
30
- // Extract headers
31
- if (includeHeaders) {
32
- const headerCells = table.querySelectorAll('thead th, thead td');
33
- headerCells.forEach((cell) => {
34
- const text = cell.textContent?.trim() || '';
35
- headers.push(cleanData ? text.replace(/\s+/g, ' ') : text);
36
- });
37
- }
38
- // If no headers found in thead, try first row
39
- if (headers.length === 0) {
40
- const firstRow = table.querySelector('tr');
41
- if (firstRow) {
42
- const cells = firstRow.querySelectorAll('th, td');
43
- cells.forEach((cell) => {
44
- const text = cell.textContent?.trim() || '';
45
- headers.push(cleanData ? text.replace(/\s+/g, ' ') : text);
46
- });
47
- }
48
- }
49
- // Extract rows
50
- const bodyRows = table.querySelectorAll('tbody tr, tr');
51
- let rowCount = 0;
52
- bodyRows.forEach((row) => {
53
- if (rowCount >= maxRows)
54
- return;
55
- const cells = row.querySelectorAll('td, th');
56
- if (cells.length === 0)
57
- return;
58
- const rowData = {};
59
- cells.forEach((cell, index) => {
60
- const text = cell.textContent?.trim() || '';
61
- const cleanedText = cleanData ? text.replace(/\s+/g, ' ') : text;
62
- const header = headers[index] || `column_${index}`;
63
- // Try to parse as number
64
- const numValue = parseFloat(cleanedText);
65
- rowData[header] = isNaN(numValue) ? cleanedText : numValue;
66
- });
67
- if (Object.keys(rowData).length > 0) {
68
- rows.push(rowData);
69
- rowCount++;
70
- }
71
- });
72
- if (rows.length > 0) {
73
- results.push({
74
- headers,
75
- rows,
76
- summary: {
77
- totalRows: rows.length,
78
- totalColumns: headers.length,
79
- extractedAt: new Date().toISOString(),
80
- },
81
- });
82
- }
83
- });
84
- return results;
85
- }, { selector, includeHeaders, cleanData, maxRows });
86
- if (tableData.length === 0) {
87
- return {
88
- content: [
89
- {
90
- type: 'text',
91
- text: `❌ No tables found with selector "${selector}"`,
92
- },
93
- ],
94
- };
95
- }
96
- return {
97
- content: [
98
- {
99
- type: 'text',
100
- text: `✅ Extracted ${tableData.length} table(s)\n\n${JSON.stringify(tableData, null, 2)}`,
101
- },
102
- ],
103
- };
104
- }, 'Failed to scrape table');
105
- }
106
8
  /**
107
9
  * Bullet lists और numbered lists से data extract करता है
108
10
  */
@@ -2,44 +2,6 @@
2
2
  // Text cleaning, validation, formatting utilities
3
3
  // @ts-nocheck
4
4
  import { withErrorHandling } from '../system-utils.js';
5
- import { parsePhoneNumber } from 'libphonenumber-js';
6
- import Ajv from 'ajv/dist/2020.js';
7
- /**
8
- * Extra whitespace और special characters remove करता है
9
- */
10
- export async function handleSmartTextCleaner(args) {
11
- return await withErrorHandling(async () => {
12
- let text = args.text;
13
- const removeExtraWhitespace = args.removeExtraWhitespace !== false;
14
- const removeSpecialChars = args.removeSpecialChars || false;
15
- const toLowerCase = args.toLowerCase || false;
16
- const trim = args.trim !== false;
17
- // Remove extra whitespace
18
- if (removeExtraWhitespace) {
19
- text = text.replace(/\s+/g, ' ');
20
- }
21
- // Remove special characters
22
- if (removeSpecialChars) {
23
- text = text.replace(/[^\w\s]/g, '');
24
- }
25
- // Convert to lowercase
26
- if (toLowerCase) {
27
- text = text.toLowerCase();
28
- }
29
- // Trim
30
- if (trim) {
31
- text = text.trim();
32
- }
33
- return {
34
- content: [
35
- {
36
- type: 'text',
37
- text: `✅ Text cleaned\n\nOriginal length: ${args.text.length}\nCleaned length: ${text.length}\n\nCleaned text:\n${text}`,
38
- },
39
- ],
40
- };
41
- }, 'Failed to clean text');
42
- }
43
5
  /**
44
6
  * HTML tags intelligently remove करता है
45
7
  */
@@ -84,141 +46,6 @@ export async function handleHTMLToText(args) {
84
46
  };
85
47
  }, 'Failed to convert HTML to text');
86
48
  }
87
- /**
88
- * Contact information automatically detect करता है
89
- */
90
- export async function handleContactExtractor(args) {
91
- return await withErrorHandling(async () => {
92
- const text = args.text;
93
- const types = args.types || ['phone', 'email'];
94
- const defaultCountry = args.defaultCountry || 'US';
95
- const results = {
96
- phones: [],
97
- emails: [],
98
- };
99
- // Extract emails
100
- if (types.includes('email')) {
101
- const emailPattern = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g;
102
- const emails = text.match(emailPattern) || [];
103
- results.emails = [...new Set(emails)].map((email) => ({
104
- value: email,
105
- domain: email.split('@')[1],
106
- username: email.split('@')[0],
107
- }));
108
- }
109
- // Extract phone numbers
110
- if (types.includes('phone')) {
111
- // Common phone patterns
112
- const phonePatterns = [
113
- /\+?\d{1,3}[-.\s]?\(?\d{1,4}\)?[-.\s]?\d{1,4}[-.\s]?\d{1,9}/g,
114
- /\(\d{3}\)\s?\d{3}-?\d{4}/g,
115
- /\d{3}[-.\s]?\d{3}[-.\s]?\d{4}/g,
116
- ];
117
- const potentialPhones = [];
118
- phonePatterns.forEach((pattern) => {
119
- const matches = text.match(pattern) || [];
120
- potentialPhones.push(...matches);
121
- });
122
- // Parse and validate phone numbers
123
- potentialPhones.forEach((phone) => {
124
- try {
125
- const parsed = parsePhoneNumber(phone, defaultCountry);
126
- if (parsed && parsed.isValid()) {
127
- results.phones.push({
128
- original: phone,
129
- international: parsed.formatInternational(),
130
- national: parsed.formatNational(),
131
- country: parsed.country,
132
- type: parsed.getType(),
133
- });
134
- }
135
- }
136
- catch (e) {
137
- // Invalid phone number, skip
138
- results.phones.push({
139
- original: phone,
140
- valid: false,
141
- });
142
- }
143
- });
144
- }
145
- return {
146
- content: [
147
- {
148
- type: 'text',
149
- text: `✅ Extracted contact info\n\nEmails: ${results.emails.length}\nPhones: ${results.phones.length}\n\n${JSON.stringify(results, null, 2)}`,
150
- },
151
- ],
152
- };
153
- }, 'Failed to extract contact info');
154
- }
155
- /**
156
- * Extracted data की structure verify करता है
157
- */
158
- export async function handleSchemaValidator(args) {
159
- return await withErrorHandling(async () => {
160
- const data = args.data;
161
- const schema = args.schema;
162
- const ajv = new Ajv({ allErrors: true });
163
- const validate = ajv.compile(schema);
164
- const valid = validate(data);
165
- if (valid) {
166
- return {
167
- content: [
168
- {
169
- type: 'text',
170
- text: '✅ Data validation passed\n\nAll fields match the schema.',
171
- },
172
- ],
173
- };
174
- }
175
- else {
176
- const errors = validate.errors?.map((err) => ({
177
- field: err.instancePath,
178
- message: err.message,
179
- params: err.params,
180
- }));
181
- return {
182
- content: [
183
- {
184
- type: 'text',
185
- text: `❌ Data validation failed\n\n${JSON.stringify(errors, null, 2)}`,
186
- },
187
- ],
188
- };
189
- }
190
- }, 'Failed to validate schema');
191
- }
192
- /**
193
- * Missing data detect करता है
194
- */
195
- export async function handleRequiredFieldsChecker(args) {
196
- return await withErrorHandling(async () => {
197
- const data = args.data;
198
- const requiredFields = args.requiredFields;
199
- const missing = [];
200
- const present = [];
201
- requiredFields.forEach((field) => {
202
- // Support nested fields with dot notation
203
- const value = field.split('.').reduce((obj, key) => obj?.[key], data);
204
- if (value === undefined || value === null || value === '') {
205
- missing.push(field);
206
- }
207
- else {
208
- present.push(field);
209
- }
210
- });
211
- const isValid = missing.length === 0;
212
- return {
213
- content: [
214
- {
215
- type: 'text',
216
- text: `${isValid ? '✅' : '❌'} Required fields check\n\nPresent: ${present.length}/${requiredFields.length}\nMissing: ${missing.join(', ') || 'None'}\n\n${isValid ? 'All required fields are present!' : 'Some required fields are missing.'}`,
217
- },
218
- ],
219
- };
220
- }, 'Failed to check required fields');
221
- }
222
49
  /**
223
50
  * Repeated data filter करता है
224
51
  */