brave-real-browser-mcp-server 2.11.0 → 2.11.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.
- package/dist/handlers/advanced-extraction-handlers.js +6 -2
- package/dist/handlers/api-integration-handlers.js +1 -1
- package/dist/handlers/data-extraction-handlers.js +12 -5
- package/dist/handlers/smart-data-extractors.js +22 -6
- package/dist/optimization-utils.js +479 -0
- package/dist/tool-definitions.js +8 -6
- package/package.json +1 -1
|
@@ -14,7 +14,7 @@ export async function handleAdvancedVideoExtraction(args) {
|
|
|
14
14
|
requirePage: true,
|
|
15
15
|
});
|
|
16
16
|
const page = getCurrentPage();
|
|
17
|
-
const waitTime = args.waitTime ||
|
|
17
|
+
const waitTime = args.waitTime || 20000;
|
|
18
18
|
// Collect all video-related data
|
|
19
19
|
const videoData = {
|
|
20
20
|
directVideoUrls: [],
|
|
@@ -183,7 +183,11 @@ export async function handleAdvancedVideoExtraction(args) {
|
|
|
183
183
|
'button[data-download]',
|
|
184
184
|
'a[href*=".mp4"]',
|
|
185
185
|
'a[href*=".mkv"]',
|
|
186
|
-
'a[class*="download"]'
|
|
186
|
+
'a[class*="download"]',
|
|
187
|
+
'a[href*=".webm"]',
|
|
188
|
+
'.download-btn',
|
|
189
|
+
'.btn-download',
|
|
190
|
+
'[class*="download"]'
|
|
187
191
|
];
|
|
188
192
|
downloadSelectors.forEach(selector => {
|
|
189
193
|
document.querySelectorAll(selector).forEach((el) => {
|
|
@@ -6,7 +6,7 @@ import { sleep } from '../system-utils.js';
|
|
|
6
6
|
* REST API Endpoint Finder - Discover REST API endpoints
|
|
7
7
|
*/
|
|
8
8
|
export async function handleRESTAPIEndpointFinder(args) {
|
|
9
|
-
const { url, analyzeNetworkRequests = true, scanDuration =
|
|
9
|
+
const { url, analyzeNetworkRequests = true, scanDuration = 10000 } = args;
|
|
10
10
|
try {
|
|
11
11
|
const page = getPageInstance();
|
|
12
12
|
if (!page) {
|
|
@@ -195,7 +195,8 @@ export async function handleExtractJSON(args) {
|
|
|
195
195
|
const results = [];
|
|
196
196
|
// Extract JSON from script tags
|
|
197
197
|
if (source === 'script' || source === 'all') {
|
|
198
|
-
const
|
|
198
|
+
const defaultSelector = selector || 'script[type="application/json"], script[type="application/ld+json"], script';
|
|
199
|
+
const scripts = document.querySelectorAll(defaultSelector);
|
|
199
200
|
scripts.forEach((script, index) => {
|
|
200
201
|
try {
|
|
201
202
|
const content = script.textContent || '';
|
|
@@ -346,7 +347,7 @@ export async function handleExtractSchema(args) {
|
|
|
346
347
|
});
|
|
347
348
|
const page = getCurrentPage();
|
|
348
349
|
const format = args.format || 'all';
|
|
349
|
-
const schemaType = args.schemaType;
|
|
350
|
+
const schemaType = args.schemaType || ['WebPage', 'Organization', 'Product', 'BreadcrumbList'];
|
|
350
351
|
const schemaData = await page.evaluate(({ format, schemaType }) => {
|
|
351
352
|
const results = [];
|
|
352
353
|
// Extract JSON-LD
|
|
@@ -358,7 +359,9 @@ export async function handleExtractSchema(args) {
|
|
|
358
359
|
// Filter by schema type if specified
|
|
359
360
|
if (schemaType) {
|
|
360
361
|
const type = data['@type'] || '';
|
|
361
|
-
|
|
362
|
+
const types = Array.isArray(schemaType) ? schemaType : [schemaType];
|
|
363
|
+
const typeMatch = types.some(t => type.toLowerCase().includes(t.toLowerCase()));
|
|
364
|
+
if (!typeMatch) {
|
|
362
365
|
return;
|
|
363
366
|
}
|
|
364
367
|
}
|
|
@@ -378,8 +381,12 @@ export async function handleExtractSchema(args) {
|
|
|
378
381
|
const items = document.querySelectorAll('[itemscope]');
|
|
379
382
|
items.forEach((item) => {
|
|
380
383
|
const itemType = item.getAttribute('itemtype') || '';
|
|
381
|
-
if (schemaType
|
|
382
|
-
|
|
384
|
+
if (schemaType) {
|
|
385
|
+
const types = Array.isArray(schemaType) ? schemaType : [schemaType];
|
|
386
|
+
const typeMatch = types.some(t => itemType.toLowerCase().includes(t.toLowerCase()));
|
|
387
|
+
if (!typeMatch) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
383
390
|
}
|
|
384
391
|
const properties = {};
|
|
385
392
|
const props = item.querySelectorAll('[itemprop]');
|
|
@@ -184,7 +184,7 @@ export async function handleAjaxExtractor(args) {
|
|
|
184
184
|
requirePage: true,
|
|
185
185
|
});
|
|
186
186
|
const page = getCurrentPage();
|
|
187
|
-
const duration = args.duration ||
|
|
187
|
+
const duration = args.duration || 15000;
|
|
188
188
|
const url = args.url;
|
|
189
189
|
const requests = [];
|
|
190
190
|
const requestHandler = (request) => {
|
|
@@ -223,7 +223,7 @@ export async function handleFetchXHR(args) {
|
|
|
223
223
|
requirePage: true,
|
|
224
224
|
});
|
|
225
225
|
const page = getCurrentPage();
|
|
226
|
-
const duration = args.duration ||
|
|
226
|
+
const duration = args.duration || 15000;
|
|
227
227
|
const xhrData = [];
|
|
228
228
|
const responseHandler = async (response) => {
|
|
229
229
|
const request = response.request();
|
|
@@ -267,8 +267,8 @@ export async function handleNetworkRecorder(args) {
|
|
|
267
267
|
requirePage: true,
|
|
268
268
|
});
|
|
269
269
|
const page = getCurrentPage();
|
|
270
|
-
const duration = args.duration ||
|
|
271
|
-
const filterTypes = args.filterTypes || ['
|
|
270
|
+
const duration = args.duration || 20000;
|
|
271
|
+
const filterTypes = args.filterTypes || ['video', 'xhr', 'fetch', 'media'];
|
|
272
272
|
const networkActivity = [];
|
|
273
273
|
const requestHandler = (request) => {
|
|
274
274
|
const resourceType = request.resourceType();
|
|
@@ -317,13 +317,16 @@ export async function handleApiFinder(args) {
|
|
|
317
317
|
requirePage: true,
|
|
318
318
|
});
|
|
319
319
|
const page = getCurrentPage();
|
|
320
|
-
const
|
|
320
|
+
const deepScan = args.deepScan !== false;
|
|
321
|
+
const apis = await page.evaluate(({ deepScan }) => {
|
|
321
322
|
const results = [];
|
|
322
323
|
const scripts = Array.from(document.querySelectorAll('script'));
|
|
323
324
|
const apiPatterns = [
|
|
324
325
|
/https?:\/\/[^"'\s]+\/api\/[^"'\s]*/gi,
|
|
325
326
|
/https?:\/\/api\.[^"'\s]+/gi,
|
|
326
327
|
/\/api\/v?\d*\/[^"'\s]*/gi,
|
|
328
|
+
/graphql/gi,
|
|
329
|
+
/rest\/v?\d*/gi,
|
|
327
330
|
];
|
|
328
331
|
scripts.forEach(script => {
|
|
329
332
|
const content = script.textContent || '';
|
|
@@ -337,8 +340,21 @@ export async function handleApiFinder(args) {
|
|
|
337
340
|
}
|
|
338
341
|
});
|
|
339
342
|
});
|
|
343
|
+
// Deep scan: also check HTML content for API references
|
|
344
|
+
if (deepScan) {
|
|
345
|
+
const htmlContent = document.body.innerHTML;
|
|
346
|
+
apiPatterns.forEach(pattern => {
|
|
347
|
+
const matches = htmlContent.match(pattern);
|
|
348
|
+
if (matches) {
|
|
349
|
+
matches.forEach(match => results.push({
|
|
350
|
+
url: match,
|
|
351
|
+
source: 'html_content',
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
340
356
|
return [...new Set(results.map(r => r.url))].map(url => ({ url, source: 'script' }));
|
|
341
|
-
});
|
|
357
|
+
}, { deepScan });
|
|
342
358
|
return {
|
|
343
359
|
content: [{
|
|
344
360
|
type: 'text',
|
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
// Optimization Utilities for All MCP Tools
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { sleep } from './system-utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* Tool Optimization Configuration
|
|
6
|
+
* Centralized defaults for all 113 tools
|
|
7
|
+
*/
|
|
8
|
+
export const TOOL_OPTIMIZATION_CONFIG = {
|
|
9
|
+
// Network Monitoring Tools (9 tools)
|
|
10
|
+
networkMonitoring: {
|
|
11
|
+
defaultDuration: 10000,
|
|
12
|
+
maxDuration: 30000,
|
|
13
|
+
monitoringTimeout: 15000,
|
|
14
|
+
retryAttempts: 3,
|
|
15
|
+
retryDelay: 2000,
|
|
16
|
+
},
|
|
17
|
+
// Video Extraction Tools (30+ tools)
|
|
18
|
+
videoExtraction: {
|
|
19
|
+
iframeDepthLimit: 5,
|
|
20
|
+
defaultTimeout: 12000,
|
|
21
|
+
hosterPatterns: [
|
|
22
|
+
'vidcloud',
|
|
23
|
+
'vidsrc',
|
|
24
|
+
'filemoon',
|
|
25
|
+
'streamtape',
|
|
26
|
+
'doodstream',
|
|
27
|
+
'mixdrop',
|
|
28
|
+
'upstream',
|
|
29
|
+
'streamwish',
|
|
30
|
+
'vidmoly',
|
|
31
|
+
'vidlox',
|
|
32
|
+
'mystream',
|
|
33
|
+
'cloudemb',
|
|
34
|
+
'embedsrc',
|
|
35
|
+
'upns.online',
|
|
36
|
+
'voe.sx',
|
|
37
|
+
'streamlare',
|
|
38
|
+
'dailymotion',
|
|
39
|
+
'youtube',
|
|
40
|
+
'vimeo',
|
|
41
|
+
'twitch',
|
|
42
|
+
],
|
|
43
|
+
videoSelectors: [
|
|
44
|
+
'video',
|
|
45
|
+
'iframe[src*="player"]',
|
|
46
|
+
'iframe[src*="embed"]',
|
|
47
|
+
'div[class*="player"]',
|
|
48
|
+
'div[id*="player"]',
|
|
49
|
+
'[data-video-url]',
|
|
50
|
+
'[data-src*=".mp4"]',
|
|
51
|
+
'[data-src*=".m3u8"]',
|
|
52
|
+
'[data-src*=".mkv"]',
|
|
53
|
+
'[data-src*=".webm"]',
|
|
54
|
+
],
|
|
55
|
+
downloadButtonSelectors: [
|
|
56
|
+
'a[download]',
|
|
57
|
+
'button[download]',
|
|
58
|
+
'a[href*="download"]',
|
|
59
|
+
'button[data-download]',
|
|
60
|
+
'a[href*=".mp4"]',
|
|
61
|
+
'a[href*=".mkv"]',
|
|
62
|
+
'a[href*=".webm"]',
|
|
63
|
+
'a[class*="download"]',
|
|
64
|
+
'.download-btn',
|
|
65
|
+
'.btn-download',
|
|
66
|
+
'[class*="download"]',
|
|
67
|
+
'[id*="download"]',
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
// Data Extraction Tools (40+ tools)
|
|
71
|
+
dataExtraction: {
|
|
72
|
+
deepScanEnabled: true,
|
|
73
|
+
recursiveSearchDepth: 5,
|
|
74
|
+
deduplicationEnabled: true,
|
|
75
|
+
cachingEnabled: true,
|
|
76
|
+
cacheTTL: 300000, // 5 minutes
|
|
77
|
+
defaultTimeout: 10000,
|
|
78
|
+
},
|
|
79
|
+
// Redirect Tracing
|
|
80
|
+
redirectTracing: {
|
|
81
|
+
maxRedirects: 15,
|
|
82
|
+
redirectTimeout: 30000,
|
|
83
|
+
followIframes: true,
|
|
84
|
+
maxDepth: 5,
|
|
85
|
+
},
|
|
86
|
+
// Global Settings
|
|
87
|
+
global: {
|
|
88
|
+
defaultTimeout: 10000,
|
|
89
|
+
retryAttempts: 3,
|
|
90
|
+
retryDelay: 2000,
|
|
91
|
+
enableErrorLogging: true,
|
|
92
|
+
enableCaching: true,
|
|
93
|
+
enableDeduplication: true,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Result Deduplication - Remove duplicate entries
|
|
98
|
+
*/
|
|
99
|
+
export function deduplicateResults(results, uniqueKey = 'url') {
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
return results.filter((item) => {
|
|
102
|
+
const key = item[uniqueKey] || JSON.stringify(item);
|
|
103
|
+
if (seen.has(key))
|
|
104
|
+
return false;
|
|
105
|
+
seen.add(key);
|
|
106
|
+
return true;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Deep Deduplication - For complex objects
|
|
111
|
+
*/
|
|
112
|
+
export function deepDeduplicateResults(results) {
|
|
113
|
+
const seen = new Set();
|
|
114
|
+
return results.filter((item) => {
|
|
115
|
+
const key = JSON.stringify(item);
|
|
116
|
+
if (seen.has(key))
|
|
117
|
+
return false;
|
|
118
|
+
seen.add(key);
|
|
119
|
+
return true;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Result Caching with TTL
|
|
124
|
+
*/
|
|
125
|
+
export class ResultCache {
|
|
126
|
+
cache = new Map();
|
|
127
|
+
set(key, data, ttl = 300000) {
|
|
128
|
+
this.cache.set(key, {
|
|
129
|
+
data,
|
|
130
|
+
timestamp: Date.now(),
|
|
131
|
+
ttl,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
get(key) {
|
|
135
|
+
const cached = this.cache.get(key);
|
|
136
|
+
if (!cached)
|
|
137
|
+
return null;
|
|
138
|
+
const age = Date.now() - cached.timestamp;
|
|
139
|
+
if (age > cached.ttl) {
|
|
140
|
+
this.cache.delete(key);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
return cached.data;
|
|
144
|
+
}
|
|
145
|
+
clear() {
|
|
146
|
+
this.cache.clear();
|
|
147
|
+
}
|
|
148
|
+
getStats() {
|
|
149
|
+
return {
|
|
150
|
+
size: this.cache.size,
|
|
151
|
+
entries: Array.from(this.cache.keys()),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Global cache instance for all tools
|
|
157
|
+
*/
|
|
158
|
+
export const globalCache = new ResultCache();
|
|
159
|
+
/**
|
|
160
|
+
* Retry with Exponential Backoff
|
|
161
|
+
*/
|
|
162
|
+
export async function retryWithBackoff(fn, maxAttempts = 3, initialDelay = 1000) {
|
|
163
|
+
let lastError;
|
|
164
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
165
|
+
try {
|
|
166
|
+
return await fn();
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
170
|
+
console.error(`Attempt ${attempt}/${maxAttempts} failed:`, lastError.message);
|
|
171
|
+
if (attempt < maxAttempts) {
|
|
172
|
+
const delay = initialDelay * Math.pow(2, attempt - 1);
|
|
173
|
+
await sleep(delay);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
throw lastError;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Enhanced Error Handler for Tools
|
|
181
|
+
*/
|
|
182
|
+
export function createErrorHandler(toolName) {
|
|
183
|
+
return {
|
|
184
|
+
handle(error, context = '') {
|
|
185
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
186
|
+
console.error(`[${toolName}] Error in ${context}: ${message}`);
|
|
187
|
+
return {
|
|
188
|
+
content: [
|
|
189
|
+
{
|
|
190
|
+
type: 'text',
|
|
191
|
+
text: `❌ ${toolName} Error: ${message}`,
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
isError: true,
|
|
195
|
+
toolName,
|
|
196
|
+
context,
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Tool Execution Wrapper with Timeout and Retry
|
|
203
|
+
*/
|
|
204
|
+
export async function executeToolWithOptimizations(toolName, toolFn, options = {}) {
|
|
205
|
+
const { timeout = TOOL_OPTIMIZATION_CONFIG.global.defaultTimeout, retryAttempts = TOOL_OPTIMIZATION_CONFIG.global.retryAttempts, retryDelay = TOOL_OPTIMIZATION_CONFIG.global.retryDelay, cacheKey, shouldCache = TOOL_OPTIMIZATION_CONFIG.global.enableCaching, } = options;
|
|
206
|
+
// Check cache first
|
|
207
|
+
if (cacheKey && shouldCache) {
|
|
208
|
+
const cached = globalCache.get(cacheKey);
|
|
209
|
+
if (cached) {
|
|
210
|
+
console.log(`[${toolName}] Cache hit for key: ${cacheKey}`);
|
|
211
|
+
return cached;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
// Execute with timeout
|
|
216
|
+
const result = await Promise.race([
|
|
217
|
+
toolFn(),
|
|
218
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`${toolName} timeout after ${timeout}ms`)), timeout)),
|
|
219
|
+
]);
|
|
220
|
+
// Cache result if enabled
|
|
221
|
+
if (cacheKey && shouldCache) {
|
|
222
|
+
globalCache.set(cacheKey, result);
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
// Retry on failure for network tools
|
|
228
|
+
if (retryAttempts > 0) {
|
|
229
|
+
console.log(`[${toolName}] Retrying after error: ${error instanceof Error ? error.message : String(error)}`);
|
|
230
|
+
return retryWithBackoff(toolFn, retryAttempts, retryDelay);
|
|
231
|
+
}
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Resource Cleanup for Tools
|
|
237
|
+
*/
|
|
238
|
+
export async function cleanupToolResources(page, context) {
|
|
239
|
+
try {
|
|
240
|
+
// Remove all event listeners
|
|
241
|
+
if (page) {
|
|
242
|
+
await page.removeAllListeners?.();
|
|
243
|
+
}
|
|
244
|
+
// Close unnecessary tabs
|
|
245
|
+
if (context) {
|
|
246
|
+
const pages = await context.pages?.();
|
|
247
|
+
if (pages && pages.length > 1) {
|
|
248
|
+
for (const p of pages.slice(1)) {
|
|
249
|
+
await p.close?.();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// Clear cache periodically
|
|
254
|
+
const cacheStats = globalCache.getStats();
|
|
255
|
+
if (cacheStats.size > 100) {
|
|
256
|
+
console.log('[Cleanup] Clearing cache - size exceeded 100 items');
|
|
257
|
+
globalCache.clear();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
console.error('[Cleanup] Error during cleanup:', error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Video Hoster Detection Database
|
|
266
|
+
*/
|
|
267
|
+
export const VIDEO_HOSTERS_DB = {
|
|
268
|
+
primary: [
|
|
269
|
+
{ name: 'vidcloud', patterns: ['vidcloud', 'vidnode'], embedPattern: '/embed/' },
|
|
270
|
+
{ name: 'vidsrc', patterns: ['vidsrc.to', 'vidsrc.me', 'vidsrc.xyz'] },
|
|
271
|
+
{ name: 'filemoon', patterns: ['filemoon.sx', 'filemoon.to'] },
|
|
272
|
+
{ name: 'streamtape', patterns: ['streamtape.com', 'streamtape.to'] },
|
|
273
|
+
{ name: 'doodstream', patterns: ['dood.', 'doodstream.'] },
|
|
274
|
+
{ name: 'mixdrop', patterns: ['mixdrop.co', 'mixdrop.to'] },
|
|
275
|
+
{ name: 'upstream', patterns: ['upstream.to', 'upstreamcdn'] },
|
|
276
|
+
{ name: 'streamwish', patterns: ['streamwish.to', 'streamwish.com'] },
|
|
277
|
+
],
|
|
278
|
+
methods: {
|
|
279
|
+
iframe: { depth: 5, timeout: 12000 },
|
|
280
|
+
directLink: { patterns: ['.m3u8', '.mp4', '.mkv', '/stream/', '.webm'] },
|
|
281
|
+
apiEndpoint: { patterns: ['/api/source', '/api/video', '/get-source', '/embed'] },
|
|
282
|
+
},
|
|
283
|
+
isKnownHoster(url) {
|
|
284
|
+
const lowerUrl = url.toLowerCase();
|
|
285
|
+
for (const hoster of this.primary) {
|
|
286
|
+
if (hoster.patterns.some((p) => lowerUrl.includes(p))) {
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
},
|
|
292
|
+
getHosterName(url) {
|
|
293
|
+
const lowerUrl = url.toLowerCase();
|
|
294
|
+
for (const hoster of this.primary) {
|
|
295
|
+
if (hoster.patterns.some((p) => lowerUrl.includes(p))) {
|
|
296
|
+
return hoster.name;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return null;
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
/**
|
|
303
|
+
* Common Selector Utilities
|
|
304
|
+
*/
|
|
305
|
+
export const SELECTOR_UTILS = {
|
|
306
|
+
// Link selectors
|
|
307
|
+
linkSelectors: [
|
|
308
|
+
'a[href]',
|
|
309
|
+
'a[href^="http"]',
|
|
310
|
+
'a[href^="/"]',
|
|
311
|
+
'[data-href]',
|
|
312
|
+
'button[onclick*="http"]',
|
|
313
|
+
],
|
|
314
|
+
// Image selectors
|
|
315
|
+
imageSelectors: [
|
|
316
|
+
'img',
|
|
317
|
+
'img[src]',
|
|
318
|
+
'picture img',
|
|
319
|
+
'[style*="background-image"]',
|
|
320
|
+
'div[data-src*="image"]',
|
|
321
|
+
'div[data-background*="image"]',
|
|
322
|
+
],
|
|
323
|
+
// Video selectors: Already defined above
|
|
324
|
+
videoSelectors: TOOL_OPTIMIZATION_CONFIG.videoExtraction.videoSelectors,
|
|
325
|
+
// Download button selectors: Already defined above
|
|
326
|
+
downloadSelectors: TOOL_OPTIMIZATION_CONFIG.videoExtraction.downloadButtonSelectors,
|
|
327
|
+
// Form selectors
|
|
328
|
+
formSelectors: ['form', 'input', 'textarea', 'select', 'button[type="submit"]'],
|
|
329
|
+
// Table selectors
|
|
330
|
+
tableSelectors: ['table', 'tbody tr', 'thead tr', '.table-row', '[role="table"]'],
|
|
331
|
+
// Pagination selectors
|
|
332
|
+
paginationSelectors: [
|
|
333
|
+
'a[rel="next"]',
|
|
334
|
+
'button[aria-label*="next"]',
|
|
335
|
+
'.pagination a',
|
|
336
|
+
'.next',
|
|
337
|
+
'a[href*="page"]',
|
|
338
|
+
'a[href*="offset"]',
|
|
339
|
+
],
|
|
340
|
+
// Hidden element detection
|
|
341
|
+
hiddenSelectors: [
|
|
342
|
+
'[style*="display: none"]',
|
|
343
|
+
'[style*="visibility: hidden"]',
|
|
344
|
+
'[hidden]',
|
|
345
|
+
'.hidden',
|
|
346
|
+
'.d-none',
|
|
347
|
+
],
|
|
348
|
+
// All selectors
|
|
349
|
+
getAllSelectors() {
|
|
350
|
+
return {
|
|
351
|
+
links: this.linkSelectors,
|
|
352
|
+
images: this.imageSelectors,
|
|
353
|
+
videos: this.videoSelectors,
|
|
354
|
+
downloads: this.downloadSelectors,
|
|
355
|
+
forms: this.formSelectors,
|
|
356
|
+
tables: this.tableSelectors,
|
|
357
|
+
pagination: this.paginationSelectors,
|
|
358
|
+
hidden: this.hiddenSelectors,
|
|
359
|
+
};
|
|
360
|
+
},
|
|
361
|
+
};
|
|
362
|
+
/**
|
|
363
|
+
* Performance Metrics Collection
|
|
364
|
+
*/
|
|
365
|
+
export class PerformanceMetrics {
|
|
366
|
+
metrics = new Map();
|
|
367
|
+
start(toolName) {
|
|
368
|
+
this.metrics.set(toolName, { start: Date.now() });
|
|
369
|
+
}
|
|
370
|
+
end(toolName) {
|
|
371
|
+
const metric = this.metrics.get(toolName);
|
|
372
|
+
if (metric) {
|
|
373
|
+
metric.end = Date.now();
|
|
374
|
+
metric.duration = metric.end - metric.start;
|
|
375
|
+
return metric.duration;
|
|
376
|
+
}
|
|
377
|
+
return 0;
|
|
378
|
+
}
|
|
379
|
+
getMetrics(toolName) {
|
|
380
|
+
return this.metrics.get(toolName) || null;
|
|
381
|
+
}
|
|
382
|
+
getAllMetrics() {
|
|
383
|
+
return this.metrics;
|
|
384
|
+
}
|
|
385
|
+
getAverageDuration() {
|
|
386
|
+
let totalDuration = 0;
|
|
387
|
+
let count = 0;
|
|
388
|
+
for (const metric of this.metrics.values()) {
|
|
389
|
+
if (metric.duration) {
|
|
390
|
+
totalDuration += metric.duration;
|
|
391
|
+
count++;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return count > 0 ? totalDuration / count : 0;
|
|
395
|
+
}
|
|
396
|
+
reset() {
|
|
397
|
+
this.metrics.clear();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Global metrics instance
|
|
402
|
+
*/
|
|
403
|
+
export const globalMetrics = new PerformanceMetrics();
|
|
404
|
+
export class ToolStatusTracker {
|
|
405
|
+
tools = new Map();
|
|
406
|
+
register(toolName, category, optimizations = []) {
|
|
407
|
+
this.tools.set(toolName, {
|
|
408
|
+
name: toolName,
|
|
409
|
+
category,
|
|
410
|
+
status: 'pending',
|
|
411
|
+
optimizations,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
updateStatus(toolName, status, performanceGain) {
|
|
415
|
+
const tool = this.tools.get(toolName);
|
|
416
|
+
if (tool) {
|
|
417
|
+
tool.status = status;
|
|
418
|
+
if (performanceGain !== undefined) {
|
|
419
|
+
tool.performanceGain = performanceGain;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
recordExecution(toolName, duration) {
|
|
424
|
+
const tool = this.tools.get(toolName);
|
|
425
|
+
if (tool) {
|
|
426
|
+
tool.lastExecutionTime = duration;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
getStatus(toolName) {
|
|
430
|
+
return this.tools.get(toolName) || null;
|
|
431
|
+
}
|
|
432
|
+
getAllStatus() {
|
|
433
|
+
return Array.from(this.tools.values());
|
|
434
|
+
}
|
|
435
|
+
getSummary() {
|
|
436
|
+
let optimized = 0;
|
|
437
|
+
let pending = 0;
|
|
438
|
+
let failed = 0;
|
|
439
|
+
const byCategory = new Map();
|
|
440
|
+
for (const tool of this.tools.values()) {
|
|
441
|
+
if (tool.status === 'optimized')
|
|
442
|
+
optimized++;
|
|
443
|
+
else if (tool.status === 'pending')
|
|
444
|
+
pending++;
|
|
445
|
+
else if (tool.status === 'failed')
|
|
446
|
+
failed++;
|
|
447
|
+
const count = byCategory.get(tool.category) || 0;
|
|
448
|
+
byCategory.set(tool.category, count + 1);
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
total: this.tools.size,
|
|
452
|
+
optimized,
|
|
453
|
+
pending,
|
|
454
|
+
failed,
|
|
455
|
+
byCategory,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Global tool status tracker
|
|
461
|
+
*/
|
|
462
|
+
export const globalToolStatus = new ToolStatusTracker();
|
|
463
|
+
export default {
|
|
464
|
+
TOOL_OPTIMIZATION_CONFIG,
|
|
465
|
+
deduplicateResults,
|
|
466
|
+
deepDeduplicateResults,
|
|
467
|
+
ResultCache,
|
|
468
|
+
globalCache,
|
|
469
|
+
retryWithBackoff,
|
|
470
|
+
createErrorHandler,
|
|
471
|
+
executeToolWithOptimizations,
|
|
472
|
+
cleanupToolResources,
|
|
473
|
+
VIDEO_HOSTERS_DB,
|
|
474
|
+
SELECTOR_UTILS,
|
|
475
|
+
PerformanceMetrics,
|
|
476
|
+
globalMetrics,
|
|
477
|
+
ToolStatusTracker,
|
|
478
|
+
globalToolStatus,
|
|
479
|
+
};
|
package/dist/tool-definitions.js
CHANGED
|
@@ -942,7 +942,7 @@ export const TOOLS = [
|
|
|
942
942
|
properties: {
|
|
943
943
|
url: { type: 'string' },
|
|
944
944
|
analyzeNetworkRequests: { type: 'boolean', default: true },
|
|
945
|
-
scanDuration: { type: 'number', default:
|
|
945
|
+
scanDuration: { type: 'number', default: 10000 },
|
|
946
946
|
},
|
|
947
947
|
},
|
|
948
948
|
},
|
|
@@ -1024,7 +1024,7 @@ export const TOOLS = [
|
|
|
1024
1024
|
inputSchema: {
|
|
1025
1025
|
type: 'object',
|
|
1026
1026
|
properties: {
|
|
1027
|
-
duration: { type: 'number', default:
|
|
1027
|
+
duration: { type: 'number', default: 15000 },
|
|
1028
1028
|
url: { type: 'string' },
|
|
1029
1029
|
},
|
|
1030
1030
|
},
|
|
@@ -1035,7 +1035,7 @@ export const TOOLS = [
|
|
|
1035
1035
|
inputSchema: {
|
|
1036
1036
|
type: 'object',
|
|
1037
1037
|
properties: {
|
|
1038
|
-
duration: { type: 'number', default:
|
|
1038
|
+
duration: { type: 'number', default: 15000 },
|
|
1039
1039
|
},
|
|
1040
1040
|
},
|
|
1041
1041
|
},
|
|
@@ -1045,8 +1045,8 @@ export const TOOLS = [
|
|
|
1045
1045
|
inputSchema: {
|
|
1046
1046
|
type: 'object',
|
|
1047
1047
|
properties: {
|
|
1048
|
-
duration: { type: 'number', default:
|
|
1049
|
-
filterTypes: { type: 'array', items: { type: 'string' }, default: ['
|
|
1048
|
+
duration: { type: 'number', default: 20000 },
|
|
1049
|
+
filterTypes: { type: 'array', items: { type: 'string' }, default: ['video', 'xhr', 'fetch', 'media'] },
|
|
1050
1050
|
},
|
|
1051
1051
|
},
|
|
1052
1052
|
},
|
|
@@ -1055,7 +1055,9 @@ export const TOOLS = [
|
|
|
1055
1055
|
description: 'Discover API endpoints on page',
|
|
1056
1056
|
inputSchema: {
|
|
1057
1057
|
type: 'object',
|
|
1058
|
-
properties: {
|
|
1058
|
+
properties: {
|
|
1059
|
+
deepScan: { type: 'boolean', default: true },
|
|
1060
|
+
},
|
|
1059
1061
|
},
|
|
1060
1062
|
},
|
|
1061
1063
|
{
|