brave-real-browser-mcp-server 2.27.28 → 2.27.30

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.
@@ -1048,6 +1048,319 @@ function decodeRot13(input) {
1048
1048
  function reverseString(input) {
1049
1049
  return input.split('').reverse().join('');
1050
1050
  }
1051
+ // ============================================================
1052
+ // ULTRA ADVANCED DECODE FUNCTIONS
1053
+ // ============================================================
1054
+ /**
1055
+ * Common streaming site encryption keys database
1056
+ * Used for tryCommonKeys feature
1057
+ */
1058
+ const COMMON_STREAMING_KEYS = {
1059
+ hubstream: {
1060
+ keys: ['kiemtienmua911ca', '0123456789abcdef'],
1061
+ ivs: ['1234567890oiuytr', '0123456789abcdef'],
1062
+ },
1063
+ vidstack: {
1064
+ keys: ['streamkey12345ab', 'vidstack1234567'],
1065
+ ivs: ['abcdef0123456789', '1234567890abcdef'],
1066
+ },
1067
+ player: {
1068
+ keys: ['playerkey123456', 'defaultkey12345'],
1069
+ ivs: ['playeriv12345678', 'defaultiv1234567'],
1070
+ },
1071
+ generic: {
1072
+ keys: ['aaaaaaaaaaaaaaaa', '0000000000000000', '1111111111111111'],
1073
+ ivs: ['0000000000000000', '1111111111111111', 'aaaaaaaaaaaaaaaa'],
1074
+ }
1075
+ };
1076
+ /**
1077
+ * XOR Cipher Decryption
1078
+ * XOR each byte with key (repeating if key is shorter)
1079
+ */
1080
+ function decryptXOR(input, key, inputFormat = 'hex') {
1081
+ try {
1082
+ let inputBytes;
1083
+ // Parse input based on format
1084
+ if (inputFormat === 'hex') {
1085
+ inputBytes = Buffer.from(input.replace(/\s/g, ''), 'hex');
1086
+ }
1087
+ else if (inputFormat === 'base64') {
1088
+ inputBytes = Buffer.from(input, 'base64');
1089
+ }
1090
+ else {
1091
+ inputBytes = Buffer.from(input, 'utf-8');
1092
+ }
1093
+ // Parse key
1094
+ let keyBytes;
1095
+ if (typeof key === 'number') {
1096
+ keyBytes = Buffer.from([key & 0xFF]);
1097
+ }
1098
+ else {
1099
+ keyBytes = Buffer.from(key, 'utf-8');
1100
+ }
1101
+ // XOR decrypt
1102
+ const result = Buffer.alloc(inputBytes.length);
1103
+ for (let i = 0; i < inputBytes.length; i++) {
1104
+ result[i] = inputBytes[i] ^ keyBytes[i % keyBytes.length];
1105
+ }
1106
+ return result.toString('utf-8');
1107
+ }
1108
+ catch (e) {
1109
+ return input;
1110
+ }
1111
+ }
1112
+ /**
1113
+ * RC4 Stream Cipher Decryption
1114
+ * Used by some streaming sites for lightweight encryption
1115
+ */
1116
+ function decryptRC4(inputHex, key) {
1117
+ try {
1118
+ const input = Buffer.from(inputHex.replace(/\s/g, ''), 'hex');
1119
+ const keyBytes = Buffer.from(key, 'utf-8');
1120
+ // RC4 Key Scheduling Algorithm (KSA)
1121
+ const S = new Uint8Array(256);
1122
+ for (let i = 0; i < 256; i++)
1123
+ S[i] = i;
1124
+ let j = 0;
1125
+ for (let i = 0; i < 256; i++) {
1126
+ j = (j + S[i] + keyBytes[i % keyBytes.length]) & 0xFF;
1127
+ [S[i], S[j]] = [S[j], S[i]]; // Swap
1128
+ }
1129
+ // RC4 Pseudo-Random Generation Algorithm (PRGA)
1130
+ const result = Buffer.alloc(input.length);
1131
+ let i = 0;
1132
+ j = 0;
1133
+ for (let k = 0; k < input.length; k++) {
1134
+ i = (i + 1) & 0xFF;
1135
+ j = (j + S[i]) & 0xFF;
1136
+ [S[i], S[j]] = [S[j], S[i]]; // Swap
1137
+ const keyByte = S[(S[i] + S[j]) & 0xFF];
1138
+ result[k] = input[k] ^ keyByte;
1139
+ }
1140
+ return result.toString('utf-8');
1141
+ }
1142
+ catch (e) {
1143
+ return inputHex;
1144
+ }
1145
+ }
1146
+ /**
1147
+ * AES-256-CBC Decryption
1148
+ * For sites using stronger 256-bit encryption
1149
+ */
1150
+ function decryptAES256(inputHex, key, iv) {
1151
+ try {
1152
+ const crypto = require('crypto');
1153
+ const inputBuffer = Buffer.from(inputHex, 'hex');
1154
+ const keyBuffer = Buffer.from(key, 'utf-8'); // 32 bytes for AES-256
1155
+ const ivBuffer = Buffer.from(iv, 'utf-8'); // 16 bytes
1156
+ const decipher = crypto.createDecipheriv('aes-256-cbc', keyBuffer, ivBuffer);
1157
+ decipher.setAutoPadding(true);
1158
+ let decrypted = decipher.update(inputBuffer);
1159
+ decrypted = Buffer.concat([decrypted, decipher.final()]);
1160
+ return decrypted.toString('utf-8');
1161
+ }
1162
+ catch (e) {
1163
+ throw new Error(`AES-256 decryption failed: ${e}`);
1164
+ }
1165
+ }
1166
+ /**
1167
+ * AES-GCM Decryption (Authenticated Encryption)
1168
+ * More secure mode used by modern sites
1169
+ */
1170
+ function decryptAESGCM(inputHex, key, iv, authTag) {
1171
+ try {
1172
+ const crypto = require('crypto');
1173
+ const inputBuffer = Buffer.from(inputHex, 'hex');
1174
+ const keyBuffer = Buffer.from(key, 'utf-8');
1175
+ const ivBuffer = Buffer.from(iv, 'utf-8');
1176
+ const decipher = crypto.createDecipheriv('aes-128-gcm', keyBuffer, ivBuffer);
1177
+ if (authTag) {
1178
+ decipher.setAuthTag(Buffer.from(authTag, 'hex'));
1179
+ }
1180
+ let decrypted = decipher.update(inputBuffer);
1181
+ decrypted = Buffer.concat([decrypted, decipher.final()]);
1182
+ return decrypted.toString('utf-8');
1183
+ }
1184
+ catch (e) {
1185
+ throw new Error(`AES-GCM decryption failed: ${e}`);
1186
+ }
1187
+ }
1188
+ /**
1189
+ * Auto-Detect Encryption Type
1190
+ * Analyzes input and detects the most likely encryption/encoding
1191
+ */
1192
+ function autoDetectEncryption(input) {
1193
+ // Check for JSFuck (all symbols)
1194
+ if (/^[\[\]\(\)\+\!\s]+$/.test(input) && input.length > 50) {
1195
+ return { type: 'jsfuck', confidence: 95, details: 'JSFuck obfuscated code' };
1196
+ }
1197
+ // Check for Packed JS
1198
+ if (input.includes('function(p,a,c,k,e,d)') || input.includes("eval(function(p,a,c,k,e")) {
1199
+ return { type: 'packedJs', confidence: 98, details: 'P.A.C.K.E.R. packed JavaScript' };
1200
+ }
1201
+ // Check for Hex (only hex chars and even length)
1202
+ if (/^[0-9a-fA-F]+$/.test(input) && input.length % 2 === 0 && input.length >= 32) {
1203
+ return { type: 'hex', confidence: 85, details: 'Hex-encoded data' };
1204
+ }
1205
+ // Check for Base64
1206
+ if (/^[A-Za-z0-9+/=_-]+$/.test(input) && input.length >= 4) {
1207
+ try {
1208
+ const decoded = decodeBase64(input);
1209
+ if (decoded && /^[\x20-\x7E\s]+$/.test(decoded)) {
1210
+ return { type: 'base64', confidence: 90, details: 'Base64 encoded' };
1211
+ }
1212
+ }
1213
+ catch { }
1214
+ }
1215
+ // Check for ROT13 (common ROT13 patterns)
1216
+ const rot13Decoded = decodeRot13(input);
1217
+ if (rot13Decoded.startsWith('http') || rot13Decoded.includes('://')) {
1218
+ return { type: 'rot13', confidence: 85, details: 'ROT13 encoded URL' };
1219
+ }
1220
+ // Check for AES (hex string of typical AES block sizes)
1221
+ if (/^[0-9a-fA-F]+$/.test(input) && (input.length === 32 || input.length === 64 || input.length % 32 === 0)) {
1222
+ return { type: 'aes', confidence: 70, details: 'Possibly AES encrypted' };
1223
+ }
1224
+ return { type: 'unknown', confidence: 0, details: 'Could not detect encryption type' };
1225
+ }
1226
+ /**
1227
+ * Extract URLs from decoded content
1228
+ * Finds http/https URLs in any text
1229
+ */
1230
+ function extractUrlsFromText(text) {
1231
+ const urlPatterns = [
1232
+ /https?:\/\/[^\s"'<>\\]+\.m3u8[^\s"'<>\\]*/gi,
1233
+ /https?:\/\/[^\s"'<>\\]+\.mp4[^\s"'<>\\]*/gi,
1234
+ /https?:\/\/[^\s"'<>\\]+\.mp3[^\s"'<>\\]*/gi,
1235
+ /https?:\/\/[^\s"'<>\\]+/gi,
1236
+ ];
1237
+ const urls = [];
1238
+ for (const pattern of urlPatterns) {
1239
+ let match;
1240
+ while ((match = pattern.exec(text)) !== null) {
1241
+ const url = match[0].replace(/['",;}\]]+$/, ''); // Clean trailing chars
1242
+ if (!urls.includes(url) && url.length > 10) {
1243
+ urls.push(url);
1244
+ }
1245
+ }
1246
+ }
1247
+ return [...new Set(urls)]; // Remove duplicates
1248
+ }
1249
+ /**
1250
+ * Recursive Decode - Keep decoding until URL found or max depth reached
1251
+ */
1252
+ function recursiveDecode(input, maxDepth = 10) {
1253
+ let current = input;
1254
+ const layers = [];
1255
+ let depth = 0;
1256
+ while (depth < maxDepth) {
1257
+ const detection = autoDetectEncryption(current);
1258
+ if (detection.type === 'unknown' || detection.confidence < 50) {
1259
+ break;
1260
+ }
1261
+ let decoded;
1262
+ try {
1263
+ switch (detection.type) {
1264
+ case 'base64':
1265
+ decoded = decodeBase64(current);
1266
+ break;
1267
+ case 'hex':
1268
+ decoded = decodeHex(current);
1269
+ break;
1270
+ case 'rot13':
1271
+ decoded = decodeRot13(current);
1272
+ break;
1273
+ case 'packedJs':
1274
+ decoded = unpackJs(current);
1275
+ break;
1276
+ default:
1277
+ decoded = current;
1278
+ }
1279
+ }
1280
+ catch {
1281
+ break;
1282
+ }
1283
+ if (decoded === current || !decoded) {
1284
+ break;
1285
+ }
1286
+ layers.push(detection.type);
1287
+ current = decoded;
1288
+ depth++;
1289
+ // Check if we found URLs
1290
+ const urls = extractUrlsFromText(current);
1291
+ if (urls.some(u => u.includes('.m3u8') || u.includes('.mp4') || u.includes('stream'))) {
1292
+ return { decoded: current, layers, depth, foundUrls: urls };
1293
+ }
1294
+ }
1295
+ return { decoded: current, layers, depth, foundUrls: extractUrlsFromText(current) };
1296
+ }
1297
+ /**
1298
+ * JSFuck Decoder
1299
+ * Decodes JSFuck obfuscated code (uses only []()!+)
1300
+ */
1301
+ function decodeJSFuck(input) {
1302
+ try {
1303
+ // JSFuck can only be safely decoded by evaluating it
1304
+ // We use a sandboxed approach
1305
+ if (!/^[\[\]\(\)\+\!\s]+$/.test(input)) {
1306
+ return input; // Not JSFuck
1307
+ }
1308
+ // Common JSFuck patterns that decode to strings
1309
+ const patterns = {
1310
+ '[]': '',
1311
+ '![]': 'false',
1312
+ '!![]': 'true',
1313
+ '+[]': '0',
1314
+ '+!![]': '1',
1315
+ };
1316
+ // This is a simplified decoder - full JSFuck requires eval
1317
+ // Return indication that it's JSFuck for browser-side decoding
1318
+ return `[JSFuck detected - length: ${input.length} - use execute_js with eval() for full decode]`;
1319
+ }
1320
+ catch {
1321
+ return input;
1322
+ }
1323
+ }
1324
+ /**
1325
+ * Try decryption with common streaming site keys
1326
+ */
1327
+ async function tryCommonKeys(inputHex, page) {
1328
+ for (const [site, config] of Object.entries(COMMON_STREAMING_KEYS)) {
1329
+ for (const key of config.keys) {
1330
+ for (const iv of config.ivs) {
1331
+ try {
1332
+ const result = await page.evaluate(async (params) => {
1333
+ try {
1334
+ const hexToBytes = (hex) => {
1335
+ const bytes = new Uint8Array(hex.length / 2);
1336
+ for (let i = 0; i < hex.length; i += 2) {
1337
+ bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
1338
+ }
1339
+ return bytes;
1340
+ };
1341
+ const keyData = new TextEncoder().encode(params.key);
1342
+ const ivData = new TextEncoder().encode(params.iv);
1343
+ const encryptedData = hexToBytes(params.input);
1344
+ const cryptoKey = await crypto.subtle.importKey("raw", keyData, "AES-CBC", false, ["decrypt"]);
1345
+ const decrypted = await crypto.subtle.decrypt({ name: "AES-CBC", iv: ivData }, cryptoKey, encryptedData);
1346
+ return new TextDecoder().decode(decrypted);
1347
+ }
1348
+ catch {
1349
+ return null;
1350
+ }
1351
+ }, { input: inputHex, key, iv });
1352
+ if (result && (result.includes('http') || result.includes('source'))) {
1353
+ return { success: true, key, iv, decrypted: result, site };
1354
+ }
1355
+ }
1356
+ catch {
1357
+ continue;
1358
+ }
1359
+ }
1360
+ }
1361
+ }
1362
+ return { success: false };
1363
+ }
1051
1364
  /**
1052
1365
  * AES-CBC decryption for encrypted streaming responses
1053
1366
  * Used by sites like hubstream.art that encrypt video URLs
@@ -1249,6 +1562,9 @@ function applyDecodeLayer(input, layer) {
1249
1562
  case 'rot13': return decodeRot13(input);
1250
1563
  case 'reverse': return reverseString(input);
1251
1564
  case 'unpackJs': return unpackJs(input);
1565
+ case 'xor': return decryptXOR(input, 0xFF, 'text'); // Default XOR with 0xFF
1566
+ case 'rc4': return input; // RC4 requires key - use decryptRC4 directly
1567
+ case 'jsfuck': return decodeJSFuck(input);
1252
1568
  default: return input;
1253
1569
  }
1254
1570
  }
@@ -1531,7 +1847,220 @@ export async function handleExtractJson(page, args) {
1531
1847
  }
1532
1848
  }
1533
1849
  // ============================================================
1534
- // 6. ORIGINAL JSON EXTRACTION (preserved)
1850
+ // 6. ULTRA ADVANCED DECODE (NEW!)
1851
+ // ============================================================
1852
+ if (args.ultraDecode) {
1853
+ try {
1854
+ let input = args.ultraDecode.input || '';
1855
+ // Extract from page if requested
1856
+ if (args.ultraDecode.extractFromPage) {
1857
+ input = await page.evaluate((selector) => {
1858
+ if (selector) {
1859
+ const el = document.querySelector(selector);
1860
+ return el?.textContent || '';
1861
+ }
1862
+ return document.body?.textContent || '';
1863
+ }, args.ultraDecode.pageSelector);
1864
+ }
1865
+ if (input) {
1866
+ // Auto-detect mode
1867
+ if (args.ultraDecode.autoDetect) {
1868
+ const detection = autoDetectEncryption(input);
1869
+ decoded = {
1870
+ source: 'ultraDecode-autoDetect',
1871
+ detectedType: detection.type,
1872
+ confidence: detection.confidence,
1873
+ details: detection.details,
1874
+ };
1875
+ // Try to decode based on detection
1876
+ if (detection.confidence >= 70) {
1877
+ const recursiveResult = recursiveDecode(input, args.ultraDecode.maxDepth || 10);
1878
+ decoded = {
1879
+ ...decoded,
1880
+ decoded: recursiveResult.decoded,
1881
+ layers: recursiveResult.layers,
1882
+ depth: recursiveResult.depth,
1883
+ foundUrls: recursiveResult.foundUrls,
1884
+ streamUrls: recursiveResult.foundUrls.filter(u => u.includes('.m3u8') || u.includes('.mp4') || u.includes('stream')),
1885
+ };
1886
+ }
1887
+ data.push(decoded);
1888
+ }
1889
+ // Recursive decode mode
1890
+ else if (args.ultraDecode.recursive) {
1891
+ const result = recursiveDecode(input, args.ultraDecode.maxDepth || 10);
1892
+ decoded = {
1893
+ source: 'ultraDecode-recursive',
1894
+ decoded: result.decoded,
1895
+ layers: result.layers,
1896
+ depth: result.depth,
1897
+ foundUrls: result.foundUrls,
1898
+ streamUrls: result.foundUrls.filter(u => u.includes('.m3u8') || u.includes('.mp4') || u.includes('stream')),
1899
+ };
1900
+ data.push(decoded);
1901
+ }
1902
+ // Try common keys mode
1903
+ else if (args.ultraDecode.tryCommonKeys) {
1904
+ const result = await tryCommonKeys(input, page);
1905
+ if (result.success) {
1906
+ decoded = {
1907
+ source: 'ultraDecode-commonKeys',
1908
+ success: true,
1909
+ matchedSite: result.site,
1910
+ key: result.key,
1911
+ iv: result.iv,
1912
+ decrypted: result.decrypted,
1913
+ extractedUrls: extractUrlsFromText(result.decrypted || ''),
1914
+ };
1915
+ }
1916
+ else {
1917
+ decoded = {
1918
+ source: 'ultraDecode-commonKeys',
1919
+ success: false,
1920
+ message: 'No matching key found in common keys database',
1921
+ triedSites: Object.keys(COMMON_STREAMING_KEYS),
1922
+ };
1923
+ }
1924
+ data.push(decoded);
1925
+ }
1926
+ }
1927
+ }
1928
+ catch (e) {
1929
+ decoded = { source: 'ultraDecode', error: String(e) };
1930
+ }
1931
+ }
1932
+ // ============================================================
1933
+ // 7. XOR CIPHER DECRYPTION
1934
+ // ============================================================
1935
+ if (args.decryptXOR) {
1936
+ try {
1937
+ const result = decryptXOR(args.decryptXOR.input, args.decryptXOR.key, args.decryptXOR.inputFormat || 'hex');
1938
+ decoded = {
1939
+ source: 'decryptXOR',
1940
+ input: args.decryptXOR.input.substring(0, 50) + '...',
1941
+ key: String(args.decryptXOR.key),
1942
+ format: args.decryptXOR.inputFormat || 'hex',
1943
+ decrypted: result,
1944
+ foundUrls: extractUrlsFromText(result),
1945
+ };
1946
+ data.push(decoded);
1947
+ }
1948
+ catch (e) {
1949
+ decoded = { source: 'decryptXOR', error: String(e) };
1950
+ }
1951
+ }
1952
+ // ============================================================
1953
+ // 8. RC4 STREAM CIPHER DECRYPTION
1954
+ // ============================================================
1955
+ if (args.decryptRC4) {
1956
+ try {
1957
+ const result = decryptRC4(args.decryptRC4.input, args.decryptRC4.key);
1958
+ decoded = {
1959
+ source: 'decryptRC4',
1960
+ input: args.decryptRC4.input.substring(0, 50) + '...',
1961
+ key: args.decryptRC4.key,
1962
+ decrypted: result,
1963
+ foundUrls: extractUrlsFromText(result),
1964
+ };
1965
+ data.push(decoded);
1966
+ }
1967
+ catch (e) {
1968
+ decoded = { source: 'decryptRC4', error: String(e) };
1969
+ }
1970
+ }
1971
+ // ============================================================
1972
+ // 9. OBFUSCATED JS DECODER (eval interceptor)
1973
+ // ============================================================
1974
+ if (args.decodeObfuscatedJs) {
1975
+ try {
1976
+ // Intercept eval calls if requested
1977
+ const obfuscatedResults = await page.evaluate((options) => {
1978
+ const results = [];
1979
+ // Find JSFuck or heavily obfuscated scripts
1980
+ const scripts = options.selector
1981
+ ? document.querySelectorAll(options.selector)
1982
+ : document.querySelectorAll('script');
1983
+ scripts.forEach((script) => {
1984
+ const content = script.textContent || '';
1985
+ // Detect JSFuck
1986
+ if (/^[\[\]\(\)\+\!\s]+$/.test(content.trim()) && content.length > 50) {
1987
+ results.push({ type: 'jsfuck', content: content.substring(0, 500) });
1988
+ }
1989
+ // Detect eval-based obfuscation
1990
+ if (content.includes('eval(') || content.includes('Function(')) {
1991
+ results.push({ type: 'eval-obfuscated', content: content.substring(0, 1000) });
1992
+ }
1993
+ // Detect hex-encoded strings
1994
+ const hexMatches = content.match(/\\x[0-9a-fA-F]{2}/g);
1995
+ if (hexMatches && hexMatches.length > 10) {
1996
+ results.push({ type: 'hex-encoded', content: content.substring(0, 1000) });
1997
+ }
1998
+ });
1999
+ return results;
2000
+ }, { selector: args.decodeObfuscatedJs.selector, evalIntercept: args.decodeObfuscatedJs.evalIntercept });
2001
+ decoded = {
2002
+ source: 'decodeObfuscatedJs',
2003
+ foundObfuscated: obfuscatedResults.length,
2004
+ results: obfuscatedResults,
2005
+ hint: 'Use execute_js with eval() to decode JSFuck, or use ultraDecode with recursive:true',
2006
+ };
2007
+ data.push(decoded);
2008
+ }
2009
+ catch (e) {
2010
+ decoded = { source: 'decodeObfuscatedJs', error: String(e) };
2011
+ }
2012
+ }
2013
+ // ============================================================
2014
+ // 10. CUSTOM CIPHER FUNCTION (user-provided decode logic)
2015
+ // ============================================================
2016
+ if (args.customCipher) {
2017
+ try {
2018
+ let input = args.customCipher.input;
2019
+ // Convert input format if needed
2020
+ if (args.customCipher.inputFormat === 'hex') {
2021
+ input = Buffer.from(input.replace(/\s/g, ''), 'hex').toString('utf-8');
2022
+ }
2023
+ else if (args.customCipher.inputFormat === 'base64') {
2024
+ input = decodeBase64(input);
2025
+ }
2026
+ // Execute custom decode function in browser context (safer)
2027
+ const result = await page.evaluate((params) => {
2028
+ try {
2029
+ // Create function from string and execute
2030
+ const decodeFn = eval(`(${params.fn})`);
2031
+ if (typeof decodeFn === 'function') {
2032
+ return { success: true, result: decodeFn(params.input) };
2033
+ }
2034
+ return { success: false, error: 'Not a function' };
2035
+ }
2036
+ catch (e) {
2037
+ return { success: false, error: e.message };
2038
+ }
2039
+ }, { input, fn: args.customCipher.decodeFunction });
2040
+ if (result.success) {
2041
+ decoded = {
2042
+ source: 'customCipher',
2043
+ input: args.customCipher.input.substring(0, 50) + '...',
2044
+ function: args.customCipher.decodeFunction.substring(0, 100),
2045
+ decrypted: result.result,
2046
+ foundUrls: extractUrlsFromText(String(result.result)),
2047
+ };
2048
+ }
2049
+ else {
2050
+ decoded = {
2051
+ source: 'customCipher',
2052
+ error: result.error,
2053
+ hint: 'Function must be a valid JS arrow function like: input => input.split("").reverse().join("")',
2054
+ };
2055
+ }
2056
+ data.push(decoded);
2057
+ }
2058
+ catch (e) {
2059
+ decoded = { source: 'customCipher', error: String(e) };
2060
+ }
2061
+ }
2062
+ // ============================================================
2063
+ // 11. ORIGINAL JSON EXTRACTION (preserved)
1535
2064
  // ============================================================
1536
2065
  // Extract from script tags with type="application/json" or type="application/ld+json"
1537
2066
  const scriptData = await page.evaluate((selector) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.27.28",
3
+ "version": "2.27.30",
4
4
  "description": "🦁 MCP server for Brave Real Browser - NPM Workspaces Monorepo with anti-detection features, SSE streaming, and LSP compatibility",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -50,7 +50,7 @@
50
50
  "dependencies": {
51
51
  "@modelcontextprotocol/sdk": "latest",
52
52
  "@types/turndown": "latest",
53
- "brave-real-browser": "^2.8.28",
53
+ "brave-real-browser": "^2.8.30",
54
54
  "puppeteer-core": "^24.35.0",
55
55
  "turndown": "latest",
56
56
  "vscode-languageserver": "^9.0.1",