@ai-jshook/mcp 0.1.1
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/.env.example +38 -0
- package/CLAUDE.md +170 -0
- package/README.md +564 -0
- package/bun.lock +1484 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/analyzer/AISummarizer.d.ts +41 -0
- package/dist/modules/analyzer/AISummarizer.d.ts.map +1 -0
- package/dist/modules/analyzer/AISummarizer.js +186 -0
- package/dist/modules/analyzer/AISummarizer.js.map +1 -0
- package/dist/modules/analyzer/CodeAnalyzer.d.ts +28 -0
- package/dist/modules/analyzer/CodeAnalyzer.d.ts.map +1 -0
- package/dist/modules/analyzer/CodeAnalyzer.js +1287 -0
- package/dist/modules/analyzer/CodeAnalyzer.js.map +1 -0
- package/dist/modules/analyzer/IntelligentAnalyzer.d.ts +114 -0
- package/dist/modules/analyzer/IntelligentAnalyzer.d.ts.map +1 -0
- package/dist/modules/analyzer/IntelligentAnalyzer.js +1176 -0
- package/dist/modules/analyzer/IntelligentAnalyzer.js.map +1 -0
- package/dist/modules/browser/BrowserModeManager.d.ts +31 -0
- package/dist/modules/browser/BrowserModeManager.d.ts.map +1 -0
- package/dist/modules/browser/BrowserModeManager.js +241 -0
- package/dist/modules/browser/BrowserModeManager.js.map +1 -0
- package/dist/modules/captcha/AICaptchaDetector.d.ts +32 -0
- package/dist/modules/captcha/AICaptchaDetector.d.ts.map +1 -0
- package/dist/modules/captcha/AICaptchaDetector.js +387 -0
- package/dist/modules/captcha/AICaptchaDetector.js.map +1 -0
- package/dist/modules/captcha/CaptchaDetector.d.ts +28 -0
- package/dist/modules/captcha/CaptchaDetector.d.ts.map +1 -0
- package/dist/modules/captcha/CaptchaDetector.js +513 -0
- package/dist/modules/captcha/CaptchaDetector.js.map +1 -0
- package/dist/modules/collector/CodeCache.d.ts +37 -0
- package/dist/modules/collector/CodeCache.d.ts.map +1 -0
- package/dist/modules/collector/CodeCache.js +188 -0
- package/dist/modules/collector/CodeCache.js.map +1 -0
- package/dist/modules/collector/CodeCollector.d.ts +107 -0
- package/dist/modules/collector/CodeCollector.d.ts.map +1 -0
- package/dist/modules/collector/CodeCollector.js +796 -0
- package/dist/modules/collector/CodeCollector.js.map +1 -0
- package/dist/modules/collector/CodeCompressor.d.ts +65 -0
- package/dist/modules/collector/CodeCompressor.d.ts.map +1 -0
- package/dist/modules/collector/CodeCompressor.js +245 -0
- package/dist/modules/collector/CodeCompressor.js.map +1 -0
- package/dist/modules/collector/DOMInspector.d.ts +51 -0
- package/dist/modules/collector/DOMInspector.d.ts.map +1 -0
- package/dist/modules/collector/DOMInspector.js +437 -0
- package/dist/modules/collector/DOMInspector.js.map +1 -0
- package/dist/modules/collector/PageController.d.ts +79 -0
- package/dist/modules/collector/PageController.d.ts.map +1 -0
- package/dist/modules/collector/PageController.js +287 -0
- package/dist/modules/collector/PageController.js.map +1 -0
- package/dist/modules/collector/SmartCodeCollector.d.ts +38 -0
- package/dist/modules/collector/SmartCodeCollector.d.ts.map +1 -0
- package/dist/modules/collector/SmartCodeCollector.js +208 -0
- package/dist/modules/collector/SmartCodeCollector.js.map +1 -0
- package/dist/modules/collector/StreamingCollector.d.ts +46 -0
- package/dist/modules/collector/StreamingCollector.d.ts.map +1 -0
- package/dist/modules/collector/StreamingCollector.js +127 -0
- package/dist/modules/collector/StreamingCollector.js.map +1 -0
- package/dist/modules/crypto/CryptoDetector.d.ts +22 -0
- package/dist/modules/crypto/CryptoDetector.d.ts.map +1 -0
- package/dist/modules/crypto/CryptoDetector.js +168 -0
- package/dist/modules/crypto/CryptoDetector.js.map +1 -0
- package/dist/modules/crypto/CryptoDetectorEnhanced.d.ts +31 -0
- package/dist/modules/crypto/CryptoDetectorEnhanced.d.ts.map +1 -0
- package/dist/modules/crypto/CryptoDetectorEnhanced.js +269 -0
- package/dist/modules/crypto/CryptoDetectorEnhanced.js.map +1 -0
- package/dist/modules/crypto/CryptoRules.d.ts +59 -0
- package/dist/modules/crypto/CryptoRules.d.ts.map +1 -0
- package/dist/modules/crypto/CryptoRules.js +234 -0
- package/dist/modules/crypto/CryptoRules.js.map +1 -0
- package/dist/modules/debugger/BlackboxManager.d.ts +14 -0
- package/dist/modules/debugger/BlackboxManager.d.ts.map +1 -0
- package/dist/modules/debugger/BlackboxManager.js +98 -0
- package/dist/modules/debugger/BlackboxManager.js.map +1 -0
- package/dist/modules/debugger/DebuggerManager.d.ts +138 -0
- package/dist/modules/debugger/DebuggerManager.d.ts.map +1 -0
- package/dist/modules/debugger/DebuggerManager.js +777 -0
- package/dist/modules/debugger/DebuggerManager.js.map +1 -0
- package/dist/modules/debugger/EventBreakpointManager.d.ts +30 -0
- package/dist/modules/debugger/EventBreakpointManager.d.ts.map +1 -0
- package/dist/modules/debugger/EventBreakpointManager.js +125 -0
- package/dist/modules/debugger/EventBreakpointManager.js.map +1 -0
- package/dist/modules/debugger/RuntimeInspector.d.ts +54 -0
- package/dist/modules/debugger/RuntimeInspector.d.ts.map +1 -0
- package/dist/modules/debugger/RuntimeInspector.js +277 -0
- package/dist/modules/debugger/RuntimeInspector.js.map +1 -0
- package/dist/modules/debugger/ScriptManager.d.ts +94 -0
- package/dist/modules/debugger/ScriptManager.d.ts.map +1 -0
- package/dist/modules/debugger/ScriptManager.js +433 -0
- package/dist/modules/debugger/ScriptManager.js.map +1 -0
- package/dist/modules/debugger/WatchExpressionManager.d.ts +52 -0
- package/dist/modules/debugger/WatchExpressionManager.d.ts.map +1 -0
- package/dist/modules/debugger/WatchExpressionManager.js +136 -0
- package/dist/modules/debugger/WatchExpressionManager.js.map +1 -0
- package/dist/modules/debugger/XHRBreakpointManager.d.ts +21 -0
- package/dist/modules/debugger/XHRBreakpointManager.d.ts.map +1 -0
- package/dist/modules/debugger/XHRBreakpointManager.js +81 -0
- package/dist/modules/debugger/XHRBreakpointManager.js.map +1 -0
- package/dist/modules/deobfuscator/ASTOptimizer.d.ts +12 -0
- package/dist/modules/deobfuscator/ASTOptimizer.d.ts.map +1 -0
- package/dist/modules/deobfuscator/ASTOptimizer.js +234 -0
- package/dist/modules/deobfuscator/ASTOptimizer.js.map +1 -0
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.d.ts +52 -0
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.d.ts.map +1 -0
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.js +985 -0
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.js.map +1 -0
- package/dist/modules/deobfuscator/Deobfuscator.d.ts +23 -0
- package/dist/modules/deobfuscator/Deobfuscator.d.ts.map +1 -0
- package/dist/modules/deobfuscator/Deobfuscator.js +487 -0
- package/dist/modules/deobfuscator/Deobfuscator.js.map +1 -0
- package/dist/modules/deobfuscator/JSVMPDeobfuscator.d.ts +19 -0
- package/dist/modules/deobfuscator/JSVMPDeobfuscator.d.ts.map +1 -0
- package/dist/modules/deobfuscator/JSVMPDeobfuscator.js +594 -0
- package/dist/modules/deobfuscator/JSVMPDeobfuscator.js.map +1 -0
- package/dist/modules/deobfuscator/JScramberDeobfuscator.d.ts +28 -0
- package/dist/modules/deobfuscator/JScramberDeobfuscator.d.ts.map +1 -0
- package/dist/modules/deobfuscator/JScramberDeobfuscator.js +239 -0
- package/dist/modules/deobfuscator/JScramberDeobfuscator.js.map +1 -0
- package/dist/modules/deobfuscator/PackerDeobfuscator.d.ts +38 -0
- package/dist/modules/deobfuscator/PackerDeobfuscator.d.ts.map +1 -0
- package/dist/modules/deobfuscator/PackerDeobfuscator.js +191 -0
- package/dist/modules/deobfuscator/PackerDeobfuscator.js.map +1 -0
- package/dist/modules/detector/ObfuscationDetector.d.ts +35 -0
- package/dist/modules/detector/ObfuscationDetector.d.ts.map +1 -0
- package/dist/modules/detector/ObfuscationDetector.js +278 -0
- package/dist/modules/detector/ObfuscationDetector.js.map +1 -0
- package/dist/modules/emulator/AIEnvironmentAnalyzer.d.ts +32 -0
- package/dist/modules/emulator/AIEnvironmentAnalyzer.d.ts.map +1 -0
- package/dist/modules/emulator/AIEnvironmentAnalyzer.js +548 -0
- package/dist/modules/emulator/AIEnvironmentAnalyzer.js.map +1 -0
- package/dist/modules/emulator/BrowserAPIDatabase.d.ts +34 -0
- package/dist/modules/emulator/BrowserAPIDatabase.d.ts.map +1 -0
- package/dist/modules/emulator/BrowserAPIDatabase.js +326 -0
- package/dist/modules/emulator/BrowserAPIDatabase.js.map +1 -0
- package/dist/modules/emulator/BrowserEnvironmentRules.d.ts +47 -0
- package/dist/modules/emulator/BrowserEnvironmentRules.d.ts.map +1 -0
- package/dist/modules/emulator/BrowserEnvironmentRules.js +493 -0
- package/dist/modules/emulator/BrowserEnvironmentRules.js.map +1 -0
- package/dist/modules/emulator/EnvironmentEmulator.d.ts +27 -0
- package/dist/modules/emulator/EnvironmentEmulator.d.ts.map +1 -0
- package/dist/modules/emulator/EnvironmentEmulator.js +1113 -0
- package/dist/modules/emulator/EnvironmentEmulator.js.map +1 -0
- package/dist/modules/emulator/EnvironmentEmulatorEnhanced.d.ts +26 -0
- package/dist/modules/emulator/EnvironmentEmulatorEnhanced.d.ts.map +1 -0
- package/dist/modules/emulator/EnvironmentEmulatorEnhanced.js +493 -0
- package/dist/modules/emulator/EnvironmentEmulatorEnhanced.js.map +1 -0
- package/dist/modules/emulator/templates/chrome-env.d.ts +260 -0
- package/dist/modules/emulator/templates/chrome-env.d.ts.map +1 -0
- package/dist/modules/emulator/templates/chrome-env.js +253 -0
- package/dist/modules/emulator/templates/chrome-env.js.map +1 -0
- package/dist/modules/hook/AIHookGenerator.d.ts +53 -0
- package/dist/modules/hook/AIHookGenerator.d.ts.map +1 -0
- package/dist/modules/hook/AIHookGenerator.js +353 -0
- package/dist/modules/hook/AIHookGenerator.js.map +1 -0
- package/dist/modules/hook/HookManager.d.ts +67 -0
- package/dist/modules/hook/HookManager.d.ts.map +1 -0
- package/dist/modules/hook/HookManager.js +1225 -0
- package/dist/modules/hook/HookManager.js.map +1 -0
- package/dist/modules/monitor/ConsoleMonitor.d.ts +140 -0
- package/dist/modules/monitor/ConsoleMonitor.d.ts.map +1 -0
- package/dist/modules/monitor/ConsoleMonitor.js +834 -0
- package/dist/modules/monitor/ConsoleMonitor.js.map +1 -0
- package/dist/modules/monitor/PerformanceMonitor.d.ts +65 -0
- package/dist/modules/monitor/PerformanceMonitor.d.ts.map +1 -0
- package/dist/modules/monitor/PerformanceMonitor.js +175 -0
- package/dist/modules/monitor/PerformanceMonitor.js.map +1 -0
- package/dist/modules/stealth/StealthScripts2025.d.ts +17 -0
- package/dist/modules/stealth/StealthScripts2025.d.ts.map +1 -0
- package/dist/modules/stealth/StealthScripts2025.js +274 -0
- package/dist/modules/stealth/StealthScripts2025.js.map +1 -0
- package/dist/modules/symbolic/JSVMPSymbolicExecutor.d.ts +69 -0
- package/dist/modules/symbolic/JSVMPSymbolicExecutor.d.ts.map +1 -0
- package/dist/modules/symbolic/JSVMPSymbolicExecutor.js +232 -0
- package/dist/modules/symbolic/JSVMPSymbolicExecutor.js.map +1 -0
- package/dist/modules/symbolic/SymbolicExecutor.d.ts +69 -0
- package/dist/modules/symbolic/SymbolicExecutor.d.ts.map +1 -0
- package/dist/modules/symbolic/SymbolicExecutor.js +346 -0
- package/dist/modules/symbolic/SymbolicExecutor.js.map +1 -0
- package/dist/server/AIHookToolDefinitions.d.ts +3 -0
- package/dist/server/AIHookToolDefinitions.d.ts.map +1 -0
- package/dist/server/AIHookToolDefinitions.js +284 -0
- package/dist/server/AIHookToolDefinitions.js.map +1 -0
- package/dist/server/AIHookToolHandlers.d.ts +50 -0
- package/dist/server/AIHookToolHandlers.d.ts.map +1 -0
- package/dist/server/AIHookToolHandlers.js +311 -0
- package/dist/server/AIHookToolHandlers.js.map +1 -0
- package/dist/server/AdvancedToolDefinitions.d.ts +3 -0
- package/dist/server/AdvancedToolDefinitions.d.ts.map +1 -0
- package/dist/server/AdvancedToolDefinitions.js +218 -0
- package/dist/server/AdvancedToolDefinitions.js.map +1 -0
- package/dist/server/AdvancedToolHandlers.d.ts +85 -0
- package/dist/server/AdvancedToolHandlers.d.ts.map +1 -0
- package/dist/server/AdvancedToolHandlers.js +431 -0
- package/dist/server/AdvancedToolHandlers.js.map +1 -0
- package/dist/server/BrowserToolDefinitions.d.ts +3 -0
- package/dist/server/BrowserToolDefinitions.d.ts.map +1 -0
- package/dist/server/BrowserToolDefinitions.js +841 -0
- package/dist/server/BrowserToolDefinitions.js.map +1 -0
- package/dist/server/BrowserToolHandlers.d.ts +290 -0
- package/dist/server/BrowserToolHandlers.d.ts.map +1 -0
- package/dist/server/BrowserToolHandlers.js +784 -0
- package/dist/server/BrowserToolHandlers.js.map +1 -0
- package/dist/server/CacheToolDefinitions.d.ts +3 -0
- package/dist/server/CacheToolDefinitions.d.ts.map +1 -0
- package/dist/server/CacheToolDefinitions.js +166 -0
- package/dist/server/CacheToolDefinitions.js.map +1 -0
- package/dist/server/DebuggerToolDefinitions.d.ts +3 -0
- package/dist/server/DebuggerToolDefinitions.d.ts.map +1 -0
- package/dist/server/DebuggerToolDefinitions.js +600 -0
- package/dist/server/DebuggerToolDefinitions.js.map +1 -0
- package/dist/server/DebuggerToolHandlers.d.ts +230 -0
- package/dist/server/DebuggerToolHandlers.d.ts.map +1 -0
- package/dist/server/DebuggerToolHandlers.js +935 -0
- package/dist/server/DebuggerToolHandlers.js.map +1 -0
- package/dist/server/MCPServer.d.ts +55 -0
- package/dist/server/MCPServer.d.ts.map +1 -0
- package/dist/server/MCPServer.js +1344 -0
- package/dist/server/MCPServer.js.map +1 -0
- package/dist/server/TokenBudgetToolDefinitions.d.ts +3 -0
- package/dist/server/TokenBudgetToolDefinitions.d.ts.map +1 -0
- package/dist/server/TokenBudgetToolDefinitions.js +114 -0
- package/dist/server/TokenBudgetToolDefinitions.js.map +1 -0
- package/dist/services/LLMService.d.ts +41 -0
- package/dist/services/LLMService.d.ts.map +1 -0
- package/dist/services/LLMService.js +792 -0
- package/dist/services/LLMService.js.map +1 -0
- package/dist/types/index.d.ts +527 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/AdaptiveDataSerializer.d.ts +27 -0
- package/dist/utils/AdaptiveDataSerializer.d.ts.map +1 -0
- package/dist/utils/AdaptiveDataSerializer.js +215 -0
- package/dist/utils/AdaptiveDataSerializer.js.map +1 -0
- package/dist/utils/CacheAdapters.d.ts +30 -0
- package/dist/utils/CacheAdapters.d.ts.map +1 -0
- package/dist/utils/CacheAdapters.js +83 -0
- package/dist/utils/CacheAdapters.js.map +1 -0
- package/dist/utils/TokenBudgetManager.d.ts +52 -0
- package/dist/utils/TokenBudgetManager.d.ts.map +1 -0
- package/dist/utils/TokenBudgetManager.js +190 -0
- package/dist/utils/TokenBudgetManager.js.map +1 -0
- package/dist/utils/UnifiedCacheManager.d.ts +55 -0
- package/dist/utils/UnifiedCacheManager.d.ts.map +1 -0
- package/dist/utils/UnifiedCacheManager.js +207 -0
- package/dist/utils/UnifiedCacheManager.js.map +1 -0
- package/dist/utils/cache.d.ts +13 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +92 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/config.d.ts +7 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +93 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/detailedDataManager.d.ts +60 -0
- package/dist/utils/detailedDataManager.d.ts.map +1 -0
- package/dist/utils/detailedDataManager.js +204 -0
- package/dist/utils/detailedDataManager.js.map +1 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +47 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/parallel.d.ts +40 -0
- package/dist/utils/parallel.d.ts.map +1 -0
- package/dist/utils/parallel.js +148 -0
- package/dist/utils/parallel.js.map +1 -0
- package/package.json +94 -0
- package/server.json +39 -0
- package/tsconfig.dev.json +14 -0
|
@@ -0,0 +1,1113 @@
|
|
|
1
|
+
import * as parser from '@babel/parser';
|
|
2
|
+
import traverse from '@babel/traverse';
|
|
3
|
+
import { logger } from '../../utils/logger.js';
|
|
4
|
+
import { chromeEnvironmentTemplate } from './templates/chrome-env.js';
|
|
5
|
+
import puppeteer from 'puppeteer-extra';
|
|
6
|
+
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
|
|
7
|
+
puppeteer.use(StealthPlugin());
|
|
8
|
+
export class EnvironmentEmulator {
|
|
9
|
+
browser;
|
|
10
|
+
llm;
|
|
11
|
+
constructor(llm) {
|
|
12
|
+
this.llm = llm;
|
|
13
|
+
if (llm) {
|
|
14
|
+
logger.info('✅ LLM服务已启用,将使用AI智能推断环境变量');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async analyze(options) {
|
|
18
|
+
const startTime = Date.now();
|
|
19
|
+
logger.info('🌐 开始环境补全分析...');
|
|
20
|
+
const { code, targetRuntime = 'both', autoFetch = false, browserUrl, browserType = 'chrome', includeComments = true, extractDepth = 3, } = options;
|
|
21
|
+
try {
|
|
22
|
+
logger.info('🔍 正在检测环境变量访问...');
|
|
23
|
+
const detectedVariables = this.detectEnvironmentVariables(code);
|
|
24
|
+
let variableManifest = {};
|
|
25
|
+
if (autoFetch && browserUrl) {
|
|
26
|
+
logger.info('🌐 正在从浏览器提取真实环境变量...');
|
|
27
|
+
variableManifest = await this.fetchRealEnvironment(browserUrl, detectedVariables, extractDepth);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
variableManifest = this.buildManifestFromTemplate(detectedVariables, browserType);
|
|
31
|
+
}
|
|
32
|
+
if (this.llm) {
|
|
33
|
+
logger.info('🤖 使用AI智能推断缺失的环境变量...');
|
|
34
|
+
const aiInferredVars = await this.inferMissingVariablesWithAI(code, detectedVariables, variableManifest, browserType);
|
|
35
|
+
Object.assign(variableManifest, { ...aiInferredVars, ...variableManifest });
|
|
36
|
+
}
|
|
37
|
+
const missingAPIs = this.identifyMissingAPIs(detectedVariables, variableManifest);
|
|
38
|
+
if (this.llm && missingAPIs.length > 0) {
|
|
39
|
+
logger.info(`🤖 使用AI为 ${missingAPIs.length} 个缺失的API生成实现...`);
|
|
40
|
+
await this.generateMissingAPIImplementationsWithAI(missingAPIs, code, variableManifest);
|
|
41
|
+
}
|
|
42
|
+
logger.info('📝 正在生成补环境代码...');
|
|
43
|
+
const emulationCode = this.generateEmulationCode(variableManifest, targetRuntime, includeComments);
|
|
44
|
+
const recommendations = this.generateRecommendations(detectedVariables, missingAPIs);
|
|
45
|
+
const totalVariables = Object.values(detectedVariables).reduce((sum, arr) => sum + arr.length, 0);
|
|
46
|
+
const autoFilledVariables = Object.keys(variableManifest).length;
|
|
47
|
+
const manualRequiredVariables = missingAPIs.length;
|
|
48
|
+
const result = {
|
|
49
|
+
detectedVariables,
|
|
50
|
+
emulationCode,
|
|
51
|
+
missingAPIs,
|
|
52
|
+
variableManifest,
|
|
53
|
+
recommendations,
|
|
54
|
+
stats: {
|
|
55
|
+
totalVariables,
|
|
56
|
+
autoFilledVariables,
|
|
57
|
+
manualRequiredVariables,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
const processingTime = Date.now() - startTime;
|
|
61
|
+
logger.info(`✅ 环境补全分析完成,耗时 ${processingTime}ms`);
|
|
62
|
+
logger.info(`📊 检测到 ${totalVariables} 个环境变量,自动补全 ${autoFilledVariables} 个`);
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
logger.error('环境补全分析失败', error);
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
detectEnvironmentVariables(code) {
|
|
71
|
+
const detected = {
|
|
72
|
+
window: [],
|
|
73
|
+
document: [],
|
|
74
|
+
navigator: [],
|
|
75
|
+
location: [],
|
|
76
|
+
screen: [],
|
|
77
|
+
other: [],
|
|
78
|
+
};
|
|
79
|
+
const accessedPaths = new Set();
|
|
80
|
+
try {
|
|
81
|
+
const ast = parser.parse(code, {
|
|
82
|
+
sourceType: 'unambiguous',
|
|
83
|
+
plugins: ['jsx', 'typescript'],
|
|
84
|
+
});
|
|
85
|
+
const self = this;
|
|
86
|
+
traverse(ast, {
|
|
87
|
+
MemberExpression(path) {
|
|
88
|
+
const fullPath = self.getMemberExpressionPath(path.node);
|
|
89
|
+
if (fullPath) {
|
|
90
|
+
accessedPaths.add(fullPath);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
Identifier(path) {
|
|
94
|
+
const name = path.node.name;
|
|
95
|
+
if (['window', 'document', 'navigator', 'location', 'screen', 'console', 'localStorage', 'sessionStorage'].includes(name)) {
|
|
96
|
+
if (path.scope.hasBinding(name)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
accessedPaths.add(name);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
for (const path of accessedPaths) {
|
|
104
|
+
if (path.startsWith('window.')) {
|
|
105
|
+
detected.window.push(path);
|
|
106
|
+
}
|
|
107
|
+
else if (path.startsWith('document.')) {
|
|
108
|
+
detected.document.push(path);
|
|
109
|
+
}
|
|
110
|
+
else if (path.startsWith('navigator.')) {
|
|
111
|
+
detected.navigator.push(path);
|
|
112
|
+
}
|
|
113
|
+
else if (path.startsWith('location.')) {
|
|
114
|
+
detected.location.push(path);
|
|
115
|
+
}
|
|
116
|
+
else if (path.startsWith('screen.')) {
|
|
117
|
+
detected.screen.push(path);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
detected.other.push(path);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for (const key of Object.keys(detected)) {
|
|
124
|
+
detected[key] = Array.from(new Set(detected[key])).sort();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
logger.warn('AST解析失败,使用正则表达式回退', error);
|
|
129
|
+
this.detectWithRegex(code, detected);
|
|
130
|
+
}
|
|
131
|
+
return detected;
|
|
132
|
+
}
|
|
133
|
+
getMemberExpressionPath(node) {
|
|
134
|
+
const parts = [];
|
|
135
|
+
let current = node;
|
|
136
|
+
while (current) {
|
|
137
|
+
if (current.type === 'MemberExpression') {
|
|
138
|
+
if (current.property.type === 'Identifier') {
|
|
139
|
+
parts.unshift(current.property.name);
|
|
140
|
+
}
|
|
141
|
+
else if (current.property.type === 'StringLiteral') {
|
|
142
|
+
parts.unshift(current.property.value);
|
|
143
|
+
}
|
|
144
|
+
current = current.object;
|
|
145
|
+
}
|
|
146
|
+
else if (current.type === 'Identifier') {
|
|
147
|
+
parts.unshift(current.name);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (parts.length > 0 && parts[0] && ['window', 'document', 'navigator', 'location', 'screen'].includes(parts[0])) {
|
|
155
|
+
return parts.join('.');
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
detectWithRegex(code, detected) {
|
|
160
|
+
const patterns = [
|
|
161
|
+
{ regex: /window\.[a-zA-Z_$][a-zA-Z0-9_$]*/g, category: 'window' },
|
|
162
|
+
{ regex: /document\.[a-zA-Z_$][a-zA-Z0-9_$]*/g, category: 'document' },
|
|
163
|
+
{ regex: /navigator\.[a-zA-Z_$][a-zA-Z0-9_$]*/g, category: 'navigator' },
|
|
164
|
+
{ regex: /location\.[a-zA-Z_$][a-zA-Z0-9_$]*/g, category: 'location' },
|
|
165
|
+
{ regex: /screen\.[a-zA-Z_$][a-zA-Z0-9_$]*/g, category: 'screen' },
|
|
166
|
+
];
|
|
167
|
+
for (const { regex, category } of patterns) {
|
|
168
|
+
const matches = code.match(regex) || [];
|
|
169
|
+
detected[category].push(...matches);
|
|
170
|
+
}
|
|
171
|
+
for (const key of Object.keys(detected)) {
|
|
172
|
+
detected[key] = Array.from(new Set(detected[key])).sort();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
buildManifestFromTemplate(detected, _browserType) {
|
|
176
|
+
const manifest = {};
|
|
177
|
+
const template = chromeEnvironmentTemplate;
|
|
178
|
+
const allPaths = [
|
|
179
|
+
...detected.window,
|
|
180
|
+
...detected.document,
|
|
181
|
+
...detected.navigator,
|
|
182
|
+
...detected.location,
|
|
183
|
+
...detected.screen,
|
|
184
|
+
...detected.other,
|
|
185
|
+
];
|
|
186
|
+
for (const path of allPaths) {
|
|
187
|
+
const value = this.getValueFromTemplate(path, template);
|
|
188
|
+
if (value !== undefined) {
|
|
189
|
+
manifest[path] = value;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return manifest;
|
|
193
|
+
}
|
|
194
|
+
getValueFromTemplate(path, template) {
|
|
195
|
+
const parts = path.split('.');
|
|
196
|
+
let current = template;
|
|
197
|
+
for (const part of parts) {
|
|
198
|
+
if (part === 'window') {
|
|
199
|
+
current = template.window;
|
|
200
|
+
}
|
|
201
|
+
else if (part === 'document') {
|
|
202
|
+
current = template.document;
|
|
203
|
+
}
|
|
204
|
+
else if (part === 'navigator') {
|
|
205
|
+
current = template.navigator;
|
|
206
|
+
}
|
|
207
|
+
else if (part === 'location') {
|
|
208
|
+
current = template.location;
|
|
209
|
+
}
|
|
210
|
+
else if (part === 'screen') {
|
|
211
|
+
current = template.screen;
|
|
212
|
+
}
|
|
213
|
+
else if (current && typeof current === 'object' && part in current) {
|
|
214
|
+
current = current[part];
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return current;
|
|
221
|
+
}
|
|
222
|
+
async fetchRealEnvironment(url, detected, depth) {
|
|
223
|
+
const manifest = {};
|
|
224
|
+
try {
|
|
225
|
+
if (!this.browser) {
|
|
226
|
+
this.browser = await puppeteer.launch({
|
|
227
|
+
headless: true,
|
|
228
|
+
args: [
|
|
229
|
+
'--no-sandbox',
|
|
230
|
+
'--disable-setuid-sandbox',
|
|
231
|
+
'--disable-blink-features=AutomationControlled',
|
|
232
|
+
'--disable-dev-shm-usage',
|
|
233
|
+
'--disable-accelerated-2d-canvas',
|
|
234
|
+
'--no-first-run',
|
|
235
|
+
'--no-zygote',
|
|
236
|
+
'--disable-gpu',
|
|
237
|
+
],
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
const page = await this.browser.newPage();
|
|
241
|
+
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
|
|
242
|
+
await page.evaluateOnNewDocument(() => {
|
|
243
|
+
Object.defineProperty(navigator, 'webdriver', {
|
|
244
|
+
get: () => undefined,
|
|
245
|
+
configurable: true,
|
|
246
|
+
});
|
|
247
|
+
window.chrome = {
|
|
248
|
+
runtime: {
|
|
249
|
+
connect: () => { },
|
|
250
|
+
sendMessage: () => { },
|
|
251
|
+
onMessage: {
|
|
252
|
+
addListener: () => { },
|
|
253
|
+
removeListener: () => { },
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
loadTimes: function () {
|
|
257
|
+
return {
|
|
258
|
+
commitLoadTime: Date.now() / 1000 - Math.random() * 10,
|
|
259
|
+
connectionInfo: 'http/1.1',
|
|
260
|
+
finishDocumentLoadTime: Date.now() / 1000 - Math.random() * 5,
|
|
261
|
+
finishLoadTime: Date.now() / 1000 - Math.random() * 3,
|
|
262
|
+
firstPaintAfterLoadTime: 0,
|
|
263
|
+
firstPaintTime: Date.now() / 1000 - Math.random() * 8,
|
|
264
|
+
navigationType: 'Other',
|
|
265
|
+
npnNegotiatedProtocol: 'http/1.1',
|
|
266
|
+
requestTime: Date.now() / 1000 - Math.random() * 15,
|
|
267
|
+
startLoadTime: Date.now() / 1000 - Math.random() * 12,
|
|
268
|
+
wasAlternateProtocolAvailable: false,
|
|
269
|
+
wasFetchedViaSpdy: false,
|
|
270
|
+
wasNpnNegotiated: true,
|
|
271
|
+
};
|
|
272
|
+
},
|
|
273
|
+
csi: function () {
|
|
274
|
+
return {
|
|
275
|
+
onloadT: Date.now(),
|
|
276
|
+
pageT: Math.random() * 1000,
|
|
277
|
+
startE: Date.now() - Math.random() * 5000,
|
|
278
|
+
tran: 15,
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
app: {
|
|
282
|
+
isInstalled: false,
|
|
283
|
+
InstallState: {
|
|
284
|
+
DISABLED: 'disabled',
|
|
285
|
+
INSTALLED: 'installed',
|
|
286
|
+
NOT_INSTALLED: 'not_installed',
|
|
287
|
+
},
|
|
288
|
+
RunningState: {
|
|
289
|
+
CANNOT_RUN: 'cannot_run',
|
|
290
|
+
READY_TO_RUN: 'ready_to_run',
|
|
291
|
+
RUNNING: 'running',
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
Object.defineProperty(navigator, 'plugins', {
|
|
296
|
+
get: () => {
|
|
297
|
+
const pluginArray = [
|
|
298
|
+
{
|
|
299
|
+
0: {
|
|
300
|
+
type: 'application/x-google-chrome-pdf',
|
|
301
|
+
suffixes: 'pdf',
|
|
302
|
+
description: 'Portable Document Format',
|
|
303
|
+
enabledPlugin: null,
|
|
304
|
+
},
|
|
305
|
+
description: 'Portable Document Format',
|
|
306
|
+
filename: 'internal-pdf-viewer',
|
|
307
|
+
length: 1,
|
|
308
|
+
name: 'Chrome PDF Plugin',
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
0: {
|
|
312
|
+
type: 'application/pdf',
|
|
313
|
+
suffixes: 'pdf',
|
|
314
|
+
description: '',
|
|
315
|
+
enabledPlugin: null,
|
|
316
|
+
},
|
|
317
|
+
description: '',
|
|
318
|
+
filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
|
|
319
|
+
length: 1,
|
|
320
|
+
name: 'Chrome PDF Viewer',
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
0: {
|
|
324
|
+
type: 'application/x-nacl',
|
|
325
|
+
suffixes: '',
|
|
326
|
+
description: 'Native Client Executable',
|
|
327
|
+
enabledPlugin: null,
|
|
328
|
+
},
|
|
329
|
+
1: {
|
|
330
|
+
type: 'application/x-pnacl',
|
|
331
|
+
suffixes: '',
|
|
332
|
+
description: 'Portable Native Client Executable',
|
|
333
|
+
enabledPlugin: null,
|
|
334
|
+
},
|
|
335
|
+
description: '',
|
|
336
|
+
filename: 'internal-nacl-plugin',
|
|
337
|
+
length: 2,
|
|
338
|
+
name: 'Native Client',
|
|
339
|
+
},
|
|
340
|
+
];
|
|
341
|
+
return pluginArray;
|
|
342
|
+
},
|
|
343
|
+
configurable: true,
|
|
344
|
+
});
|
|
345
|
+
Object.defineProperty(navigator, 'languages', {
|
|
346
|
+
get: () => ['zh-CN', 'zh', 'en-US', 'en'],
|
|
347
|
+
configurable: true,
|
|
348
|
+
});
|
|
349
|
+
const originalQuery = window.navigator.permissions.query;
|
|
350
|
+
window.navigator.permissions.query = (parameters) => parameters.name === 'notifications'
|
|
351
|
+
? Promise.resolve({ state: Notification.permission })
|
|
352
|
+
: originalQuery(parameters);
|
|
353
|
+
window.requestAnimationFrame =
|
|
354
|
+
window.requestAnimationFrame ||
|
|
355
|
+
function (callback) {
|
|
356
|
+
return setTimeout(callback, 16);
|
|
357
|
+
};
|
|
358
|
+
window.cancelAnimationFrame =
|
|
359
|
+
window.cancelAnimationFrame ||
|
|
360
|
+
function (id) {
|
|
361
|
+
clearTimeout(id);
|
|
362
|
+
};
|
|
363
|
+
window._sdkGlueVersionMap = window._sdkGlueVersionMap || {};
|
|
364
|
+
});
|
|
365
|
+
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
|
366
|
+
const allPaths = [
|
|
367
|
+
...detected.window,
|
|
368
|
+
...detected.document,
|
|
369
|
+
...detected.navigator,
|
|
370
|
+
...detected.location,
|
|
371
|
+
...detected.screen,
|
|
372
|
+
...detected.other,
|
|
373
|
+
];
|
|
374
|
+
const extractedValues = await page.evaluate((paths, maxDepth) => {
|
|
375
|
+
const result = {};
|
|
376
|
+
const seen = new WeakSet();
|
|
377
|
+
function extractValue(path) {
|
|
378
|
+
try {
|
|
379
|
+
const parts = path.split('.');
|
|
380
|
+
let current = window;
|
|
381
|
+
for (const part of parts) {
|
|
382
|
+
if (current && typeof current === 'object' && part in current) {
|
|
383
|
+
current = current[part];
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
return undefined;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return serializeValue(current, maxDepth, seen);
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
return `[Error: ${error.message}]`;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function serializeValue(value, depth, seenObjects) {
|
|
396
|
+
if (depth <= 0)
|
|
397
|
+
return '[Max Depth]';
|
|
398
|
+
if (value === null)
|
|
399
|
+
return null;
|
|
400
|
+
if (value === undefined)
|
|
401
|
+
return undefined;
|
|
402
|
+
const type = typeof value;
|
|
403
|
+
if (type === 'string' || type === 'number' || type === 'boolean') {
|
|
404
|
+
return value;
|
|
405
|
+
}
|
|
406
|
+
if (type === 'function') {
|
|
407
|
+
try {
|
|
408
|
+
return {
|
|
409
|
+
__type: 'Function',
|
|
410
|
+
name: value.name || 'anonymous',
|
|
411
|
+
toString: value.toString().substring(0, 200),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
catch (e) {
|
|
415
|
+
return '[Function]';
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (type === 'object' && seenObjects.has(value)) {
|
|
419
|
+
return '[Circular Reference]';
|
|
420
|
+
}
|
|
421
|
+
if (Array.isArray(value)) {
|
|
422
|
+
seenObjects.add(value);
|
|
423
|
+
const arr = value.slice(0, 20).map((item) => serializeValue(item, depth - 1, seenObjects));
|
|
424
|
+
if (value.length > 20) {
|
|
425
|
+
arr.push(`[... ${value.length - 20} more items]`);
|
|
426
|
+
}
|
|
427
|
+
return arr;
|
|
428
|
+
}
|
|
429
|
+
if (type === 'object') {
|
|
430
|
+
seenObjects.add(value);
|
|
431
|
+
const serialized = {};
|
|
432
|
+
const allKeys = Object.getOwnPropertyNames(value);
|
|
433
|
+
const limitedKeys = allKeys.slice(0, 100);
|
|
434
|
+
for (const key of limitedKeys) {
|
|
435
|
+
try {
|
|
436
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
437
|
+
if (descriptor) {
|
|
438
|
+
if (descriptor.get) {
|
|
439
|
+
try {
|
|
440
|
+
serialized[key] = serializeValue(value[key], depth - 1, seenObjects);
|
|
441
|
+
}
|
|
442
|
+
catch (e) {
|
|
443
|
+
serialized[key] = '[Getter Error]';
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
else if (descriptor.value !== undefined) {
|
|
447
|
+
serialized[key] = serializeValue(descriptor.value, depth - 1, seenObjects);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
catch (e) {
|
|
452
|
+
serialized[key] = `[Error: ${e.message}]`;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (allKeys.length > 100) {
|
|
456
|
+
serialized['__more'] = `[... ${allKeys.length - 100} more properties]`;
|
|
457
|
+
}
|
|
458
|
+
return serialized;
|
|
459
|
+
}
|
|
460
|
+
try {
|
|
461
|
+
return String(value);
|
|
462
|
+
}
|
|
463
|
+
catch (e) {
|
|
464
|
+
return '[Unserializable]';
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
for (const path of paths) {
|
|
468
|
+
result[path] = extractValue(path);
|
|
469
|
+
}
|
|
470
|
+
const commonAntiCrawlVars = [
|
|
471
|
+
'navigator.userAgent',
|
|
472
|
+
'navigator.platform',
|
|
473
|
+
'navigator.vendor',
|
|
474
|
+
'navigator.hardwareConcurrency',
|
|
475
|
+
'navigator.deviceMemory',
|
|
476
|
+
'navigator.maxTouchPoints',
|
|
477
|
+
'navigator.language',
|
|
478
|
+
'navigator.languages',
|
|
479
|
+
'navigator.onLine',
|
|
480
|
+
'navigator.cookieEnabled',
|
|
481
|
+
'navigator.doNotTrack',
|
|
482
|
+
'screen.width',
|
|
483
|
+
'screen.height',
|
|
484
|
+
'screen.availWidth',
|
|
485
|
+
'screen.availHeight',
|
|
486
|
+
'screen.colorDepth',
|
|
487
|
+
'screen.pixelDepth',
|
|
488
|
+
'screen.orientation.type',
|
|
489
|
+
'window.innerWidth',
|
|
490
|
+
'window.innerHeight',
|
|
491
|
+
'window.outerWidth',
|
|
492
|
+
'window.outerHeight',
|
|
493
|
+
'window.devicePixelRatio',
|
|
494
|
+
'window.screenX',
|
|
495
|
+
'window.screenY',
|
|
496
|
+
'document.referrer',
|
|
497
|
+
'document.cookie',
|
|
498
|
+
'document.title',
|
|
499
|
+
'document.URL',
|
|
500
|
+
'document.documentURI',
|
|
501
|
+
'document.domain',
|
|
502
|
+
'location.href',
|
|
503
|
+
'location.protocol',
|
|
504
|
+
'location.host',
|
|
505
|
+
'location.hostname',
|
|
506
|
+
'location.port',
|
|
507
|
+
'location.pathname',
|
|
508
|
+
'location.search',
|
|
509
|
+
'location.hash',
|
|
510
|
+
'location.origin',
|
|
511
|
+
];
|
|
512
|
+
for (const varPath of commonAntiCrawlVars) {
|
|
513
|
+
if (!result[varPath]) {
|
|
514
|
+
result[varPath] = extractValue(varPath);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return result;
|
|
518
|
+
}, allPaths, depth);
|
|
519
|
+
Object.assign(manifest, extractedValues);
|
|
520
|
+
await page.close();
|
|
521
|
+
logger.info(`✅ 成功从浏览器提取 ${Object.keys(manifest).length} 个环境变量`);
|
|
522
|
+
}
|
|
523
|
+
catch (error) {
|
|
524
|
+
logger.warn('从浏览器提取环境变量失败,使用模板值', error);
|
|
525
|
+
return this.buildManifestFromTemplate(detected, 'chrome');
|
|
526
|
+
}
|
|
527
|
+
return manifest;
|
|
528
|
+
}
|
|
529
|
+
identifyMissingAPIs(detected, manifest) {
|
|
530
|
+
const missing = [];
|
|
531
|
+
const allPaths = [
|
|
532
|
+
...detected.window,
|
|
533
|
+
...detected.document,
|
|
534
|
+
...detected.navigator,
|
|
535
|
+
...detected.location,
|
|
536
|
+
...detected.screen,
|
|
537
|
+
...detected.other,
|
|
538
|
+
];
|
|
539
|
+
for (const path of allPaths) {
|
|
540
|
+
if (!(path in manifest) || manifest[path] === undefined) {
|
|
541
|
+
let type = 'property';
|
|
542
|
+
if (path.includes('()')) {
|
|
543
|
+
type = 'function';
|
|
544
|
+
}
|
|
545
|
+
else if (path.endsWith('Element') || path.endsWith('List')) {
|
|
546
|
+
type = 'object';
|
|
547
|
+
}
|
|
548
|
+
missing.push({
|
|
549
|
+
name: path.split('.').pop() || path,
|
|
550
|
+
type,
|
|
551
|
+
path,
|
|
552
|
+
suggestion: this.getSuggestionForMissingAPI(path, type),
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return missing;
|
|
557
|
+
}
|
|
558
|
+
getSuggestionForMissingAPI(path, type) {
|
|
559
|
+
if (type === 'function') {
|
|
560
|
+
return `补充为空函数: ${path} = function() {}`;
|
|
561
|
+
}
|
|
562
|
+
else if (type === 'object') {
|
|
563
|
+
return `补充为空对象: ${path} = {}`;
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
return `补充为null或合适的值: ${path} = null`;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
generateEmulationCode(manifest, targetRuntime, includeComments) {
|
|
570
|
+
let nodejs = '';
|
|
571
|
+
let python = '';
|
|
572
|
+
if (targetRuntime === 'nodejs' || targetRuntime === 'both') {
|
|
573
|
+
nodejs = this.generateNodeJSCode(manifest, includeComments);
|
|
574
|
+
}
|
|
575
|
+
if (targetRuntime === 'python' || targetRuntime === 'both') {
|
|
576
|
+
python = this.generatePythonCode(manifest, includeComments);
|
|
577
|
+
}
|
|
578
|
+
return { nodejs, python };
|
|
579
|
+
}
|
|
580
|
+
generateNodeJSCode(manifest, includeComments) {
|
|
581
|
+
const lines = [];
|
|
582
|
+
if (includeComments) {
|
|
583
|
+
lines.push('/**');
|
|
584
|
+
lines.push(' * 浏览器环境补全代码 (Node.js)');
|
|
585
|
+
lines.push(' * 自动生成于 ' + new Date().toISOString());
|
|
586
|
+
lines.push(' * 基于真实浏览器环境提取');
|
|
587
|
+
lines.push(' * 适用于抖音、头条等JSVMP混淆的JS代码');
|
|
588
|
+
lines.push(' */');
|
|
589
|
+
lines.push('');
|
|
590
|
+
}
|
|
591
|
+
lines.push('// 1. 初始化全局对象');
|
|
592
|
+
lines.push('const window = global;');
|
|
593
|
+
lines.push('const document = {};');
|
|
594
|
+
lines.push('const navigator = {};');
|
|
595
|
+
lines.push('const location = {};');
|
|
596
|
+
lines.push('const screen = {};');
|
|
597
|
+
lines.push('');
|
|
598
|
+
if (includeComments) {
|
|
599
|
+
lines.push('// 2. 补全window对象');
|
|
600
|
+
}
|
|
601
|
+
lines.push('window.window = window;');
|
|
602
|
+
lines.push('window.self = window;');
|
|
603
|
+
lines.push('window.top = window;');
|
|
604
|
+
lines.push('window.parent = window;');
|
|
605
|
+
lines.push('window.document = document;');
|
|
606
|
+
lines.push('window.navigator = navigator;');
|
|
607
|
+
lines.push('window.location = location;');
|
|
608
|
+
lines.push('window.screen = screen;');
|
|
609
|
+
lines.push('');
|
|
610
|
+
if (includeComments) {
|
|
611
|
+
lines.push('// 3. 补全常见的window方法');
|
|
612
|
+
}
|
|
613
|
+
lines.push('window.requestAnimationFrame = function(callback) {');
|
|
614
|
+
lines.push(' return setTimeout(callback, 16);');
|
|
615
|
+
lines.push('};');
|
|
616
|
+
lines.push('');
|
|
617
|
+
lines.push('window.cancelAnimationFrame = function(id) {');
|
|
618
|
+
lines.push(' clearTimeout(id);');
|
|
619
|
+
lines.push('};');
|
|
620
|
+
lines.push('');
|
|
621
|
+
lines.push('window.setTimeout = setTimeout;');
|
|
622
|
+
lines.push('window.setInterval = setInterval;');
|
|
623
|
+
lines.push('window.clearTimeout = clearTimeout;');
|
|
624
|
+
lines.push('window.clearInterval = clearInterval;');
|
|
625
|
+
lines.push('');
|
|
626
|
+
if (includeComments) {
|
|
627
|
+
lines.push('// 4. 补全XMLHttpRequest');
|
|
628
|
+
}
|
|
629
|
+
lines.push('window.XMLHttpRequest = function() {');
|
|
630
|
+
lines.push(' this.open = function() {};');
|
|
631
|
+
lines.push(' this.send = function() {};');
|
|
632
|
+
lines.push(' this.setRequestHeader = function() {};');
|
|
633
|
+
lines.push('};');
|
|
634
|
+
lines.push('');
|
|
635
|
+
if (includeComments) {
|
|
636
|
+
lines.push('// 5. 补全_sdkGlueVersionMap(头条特有)');
|
|
637
|
+
}
|
|
638
|
+
lines.push('window._sdkGlueVersionMap = {};');
|
|
639
|
+
lines.push('');
|
|
640
|
+
if (includeComments) {
|
|
641
|
+
lines.push('// 6. 补全chrome对象(反检测)');
|
|
642
|
+
}
|
|
643
|
+
lines.push('window.chrome = {');
|
|
644
|
+
lines.push(' runtime: {},');
|
|
645
|
+
lines.push(' loadTimes: function() {},');
|
|
646
|
+
lines.push(' csi: function() {},');
|
|
647
|
+
lines.push(' app: {}');
|
|
648
|
+
lines.push('};');
|
|
649
|
+
lines.push('');
|
|
650
|
+
if (includeComments) {
|
|
651
|
+
lines.push('// 7. 补全从真实浏览器提取的环境变量');
|
|
652
|
+
}
|
|
653
|
+
const categories = this.categorizeManifest(manifest);
|
|
654
|
+
for (const [category, vars] of Object.entries(categories)) {
|
|
655
|
+
if (vars.length === 0)
|
|
656
|
+
continue;
|
|
657
|
+
if (includeComments) {
|
|
658
|
+
lines.push(`// ${category} 对象属性`);
|
|
659
|
+
}
|
|
660
|
+
for (const [path, value] of vars) {
|
|
661
|
+
const parts = path.split('.');
|
|
662
|
+
if (parts.length === 1)
|
|
663
|
+
continue;
|
|
664
|
+
const objName = parts[0];
|
|
665
|
+
const propPath = parts.slice(1).join('.');
|
|
666
|
+
if (parts.length === 2) {
|
|
667
|
+
lines.push(`${objName}.${propPath} = ${this.formatValueForJS(value)};`);
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
const parentPath = parts.slice(0, -1).join('.');
|
|
671
|
+
const lastProp = parts[parts.length - 1];
|
|
672
|
+
lines.push(`if (!${parentPath}) ${parentPath} = {};`);
|
|
673
|
+
lines.push(`${parentPath}.${lastProp} = ${this.formatValueForJS(value)};`);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
lines.push('');
|
|
677
|
+
}
|
|
678
|
+
if (includeComments) {
|
|
679
|
+
lines.push('// 8. 导出环境对象(可选)');
|
|
680
|
+
}
|
|
681
|
+
lines.push('module.exports = { window, document, navigator, location, screen };');
|
|
682
|
+
lines.push('');
|
|
683
|
+
return lines.join('\n');
|
|
684
|
+
}
|
|
685
|
+
generatePythonCode(manifest, includeComments) {
|
|
686
|
+
const lines = [];
|
|
687
|
+
if (includeComments) {
|
|
688
|
+
lines.push('"""');
|
|
689
|
+
lines.push('浏览器环境补全代码 (Python + execjs/PyExecJS)');
|
|
690
|
+
lines.push('自动生成于 ' + new Date().toISOString());
|
|
691
|
+
lines.push('基于真实浏览器环境提取');
|
|
692
|
+
lines.push('适用于抖音、头条等JSVMP混淆的JS代码');
|
|
693
|
+
lines.push('');
|
|
694
|
+
lines.push('使用方法:');
|
|
695
|
+
lines.push('1. pip install PyExecJS');
|
|
696
|
+
lines.push('2. 将混淆的JS代码保存为 obfuscated.js');
|
|
697
|
+
lines.push('3. 运行此脚本调用加密函数');
|
|
698
|
+
lines.push('"""');
|
|
699
|
+
lines.push('');
|
|
700
|
+
}
|
|
701
|
+
lines.push('import execjs');
|
|
702
|
+
lines.push('');
|
|
703
|
+
if (includeComments) {
|
|
704
|
+
lines.push('# ========== 第一部分:补全浏览器环境 ==========');
|
|
705
|
+
}
|
|
706
|
+
lines.push('env_code = """');
|
|
707
|
+
lines.push('// 1. 初始化全局对象');
|
|
708
|
+
lines.push('const window = global;');
|
|
709
|
+
lines.push('const document = {};');
|
|
710
|
+
lines.push('const navigator = {};');
|
|
711
|
+
lines.push('const location = {};');
|
|
712
|
+
lines.push('const screen = {};');
|
|
713
|
+
lines.push('');
|
|
714
|
+
lines.push('// 2. 补全window对象');
|
|
715
|
+
lines.push('window.window = window;');
|
|
716
|
+
lines.push('window.self = window;');
|
|
717
|
+
lines.push('window.top = window;');
|
|
718
|
+
lines.push('window.parent = window;');
|
|
719
|
+
lines.push('window.document = document;');
|
|
720
|
+
lines.push('window.navigator = navigator;');
|
|
721
|
+
lines.push('window.location = location;');
|
|
722
|
+
lines.push('window.screen = screen;');
|
|
723
|
+
lines.push('');
|
|
724
|
+
lines.push('// 3. 补全常见的window方法(抖音/头条会检测)');
|
|
725
|
+
lines.push('window.requestAnimationFrame = function(callback) {');
|
|
726
|
+
lines.push(' return setTimeout(callback, 16);');
|
|
727
|
+
lines.push('};');
|
|
728
|
+
lines.push('');
|
|
729
|
+
lines.push('window.cancelAnimationFrame = function(id) {');
|
|
730
|
+
lines.push(' clearTimeout(id);');
|
|
731
|
+
lines.push('};');
|
|
732
|
+
lines.push('');
|
|
733
|
+
lines.push('window.setTimeout = setTimeout;');
|
|
734
|
+
lines.push('window.setInterval = setInterval;');
|
|
735
|
+
lines.push('window.clearTimeout = clearTimeout;');
|
|
736
|
+
lines.push('window.clearInterval = clearInterval;');
|
|
737
|
+
lines.push('');
|
|
738
|
+
lines.push('// 4. 补全XMLHttpRequest(头条会检测)');
|
|
739
|
+
lines.push('window.XMLHttpRequest = function() {');
|
|
740
|
+
lines.push(' this.open = function() {};');
|
|
741
|
+
lines.push(' this.send = function() {};');
|
|
742
|
+
lines.push(' this.setRequestHeader = function() {};');
|
|
743
|
+
lines.push('};');
|
|
744
|
+
lines.push('');
|
|
745
|
+
lines.push('// 5. 补全_sdkGlueVersionMap(头条特有)');
|
|
746
|
+
lines.push('window._sdkGlueVersionMap = {};');
|
|
747
|
+
lines.push('');
|
|
748
|
+
lines.push('// 6. 补全chrome对象(反检测)');
|
|
749
|
+
lines.push('window.chrome = {');
|
|
750
|
+
lines.push(' runtime: {},');
|
|
751
|
+
lines.push(' loadTimes: function() {},');
|
|
752
|
+
lines.push(' csi: function() {},');
|
|
753
|
+
lines.push(' app: {}');
|
|
754
|
+
lines.push('};');
|
|
755
|
+
lines.push('');
|
|
756
|
+
lines.push('// 7. 补全从真实浏览器提取的环境变量');
|
|
757
|
+
const categories = this.categorizeManifest(manifest);
|
|
758
|
+
for (const [category, vars] of Object.entries(categories)) {
|
|
759
|
+
if (vars.length === 0)
|
|
760
|
+
continue;
|
|
761
|
+
lines.push(`// ${category} 对象属性`);
|
|
762
|
+
for (const [path, value] of vars) {
|
|
763
|
+
const parts = path.split('.');
|
|
764
|
+
if (parts.length === 1)
|
|
765
|
+
continue;
|
|
766
|
+
const objName = parts[0];
|
|
767
|
+
const propPath = parts.slice(1).join('.');
|
|
768
|
+
if (parts.length === 2) {
|
|
769
|
+
lines.push(`${objName}.${propPath} = ${this.formatValueForJS(value)};`);
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
const parentPath = parts.slice(0, -1).join('.');
|
|
773
|
+
const lastProp = parts[parts.length - 1];
|
|
774
|
+
lines.push(`if (!${parentPath}) ${parentPath} = {};`);
|
|
775
|
+
lines.push(`${parentPath}.${lastProp} = ${this.formatValueForJS(value)};`);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
lines.push('');
|
|
779
|
+
}
|
|
780
|
+
lines.push('"""');
|
|
781
|
+
lines.push('');
|
|
782
|
+
if (includeComments) {
|
|
783
|
+
lines.push('# ========== 第二部分:加载混淆的JS代码 ==========');
|
|
784
|
+
}
|
|
785
|
+
lines.push('# 读取混淆的JS文件');
|
|
786
|
+
lines.push('with open("obfuscated.js", "r", encoding="utf-8") as f:');
|
|
787
|
+
lines.push(' obfuscated_code = f.read()');
|
|
788
|
+
lines.push('');
|
|
789
|
+
lines.push('# 合并环境代码和混淆代码');
|
|
790
|
+
lines.push('full_code = env_code + obfuscated_code');
|
|
791
|
+
lines.push('');
|
|
792
|
+
if (includeComments) {
|
|
793
|
+
lines.push('# ========== 第三部分:创建JavaScript执行上下文 ==========');
|
|
794
|
+
}
|
|
795
|
+
lines.push('# 编译JavaScript代码');
|
|
796
|
+
lines.push('ctx = execjs.compile(full_code)');
|
|
797
|
+
lines.push('');
|
|
798
|
+
if (includeComments) {
|
|
799
|
+
lines.push('# ========== 第四部分:调用加密函数 ==========');
|
|
800
|
+
lines.push('# 示例:调用抖音a_bogus加密函数');
|
|
801
|
+
}
|
|
802
|
+
lines.push('def get_a_bogus(url, user_agent):');
|
|
803
|
+
lines.push(' """');
|
|
804
|
+
lines.push(' 调用JS中的sign函数生成a_bogus参数');
|
|
805
|
+
lines.push(' ');
|
|
806
|
+
lines.push(' Args:');
|
|
807
|
+
lines.push(' url: 请求的URL');
|
|
808
|
+
lines.push(' user_agent: User-Agent字符串');
|
|
809
|
+
lines.push(' ');
|
|
810
|
+
lines.push(' Returns:');
|
|
811
|
+
lines.push(' 加密后的a_bogus字符串');
|
|
812
|
+
lines.push(' """');
|
|
813
|
+
lines.push(' try:');
|
|
814
|
+
lines.push(' # 调用window.byted_acrawler.sign方法');
|
|
815
|
+
lines.push(' result = ctx.call("window.byted_acrawler.sign", {');
|
|
816
|
+
lines.push(' "url": url,');
|
|
817
|
+
lines.push(' "user_agent": user_agent');
|
|
818
|
+
lines.push(' })');
|
|
819
|
+
lines.push(' return result');
|
|
820
|
+
lines.push(' except Exception as e:');
|
|
821
|
+
lines.push(' print(f"加密失败: {e}")');
|
|
822
|
+
lines.push(' return None');
|
|
823
|
+
lines.push('');
|
|
824
|
+
if (includeComments) {
|
|
825
|
+
lines.push('# ========== 第五部分:使用示例 ==========');
|
|
826
|
+
}
|
|
827
|
+
lines.push('if __name__ == "__main__":');
|
|
828
|
+
lines.push(' # 测试参数');
|
|
829
|
+
lines.push(' test_url = "https://www.douyin.com/aweme/v1/web/aweme/detail/"');
|
|
830
|
+
lines.push(' test_ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"');
|
|
831
|
+
lines.push(' ');
|
|
832
|
+
lines.push(' # 生成a_bogus');
|
|
833
|
+
lines.push(' a_bogus = get_a_bogus(test_url, test_ua)');
|
|
834
|
+
lines.push(' print(f"a_bogus: {a_bogus}")');
|
|
835
|
+
lines.push('');
|
|
836
|
+
return lines.join('\n');
|
|
837
|
+
}
|
|
838
|
+
categorizeManifest(manifest) {
|
|
839
|
+
const categories = {
|
|
840
|
+
window: [],
|
|
841
|
+
document: [],
|
|
842
|
+
navigator: [],
|
|
843
|
+
location: [],
|
|
844
|
+
screen: [],
|
|
845
|
+
other: [],
|
|
846
|
+
};
|
|
847
|
+
for (const [path, value] of Object.entries(manifest)) {
|
|
848
|
+
if (path.startsWith('window.')) {
|
|
849
|
+
categories.window.push([path, value]);
|
|
850
|
+
}
|
|
851
|
+
else if (path.startsWith('document.')) {
|
|
852
|
+
categories.document.push([path, value]);
|
|
853
|
+
}
|
|
854
|
+
else if (path.startsWith('navigator.')) {
|
|
855
|
+
categories.navigator.push([path, value]);
|
|
856
|
+
}
|
|
857
|
+
else if (path.startsWith('location.')) {
|
|
858
|
+
categories.location.push([path, value]);
|
|
859
|
+
}
|
|
860
|
+
else if (path.startsWith('screen.')) {
|
|
861
|
+
categories.screen.push([path, value]);
|
|
862
|
+
}
|
|
863
|
+
else {
|
|
864
|
+
categories.other.push([path, value]);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return categories;
|
|
868
|
+
}
|
|
869
|
+
formatValue(value) {
|
|
870
|
+
if (value === null)
|
|
871
|
+
return 'null';
|
|
872
|
+
if (value === undefined)
|
|
873
|
+
return 'undefined';
|
|
874
|
+
const type = typeof value;
|
|
875
|
+
if (type === 'string') {
|
|
876
|
+
return `"${value.replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`;
|
|
877
|
+
}
|
|
878
|
+
if (type === 'number' || type === 'boolean') {
|
|
879
|
+
return String(value);
|
|
880
|
+
}
|
|
881
|
+
if (type === 'function' || value === '[Function]') {
|
|
882
|
+
return 'function() {}';
|
|
883
|
+
}
|
|
884
|
+
if (Array.isArray(value)) {
|
|
885
|
+
const items = value.slice(0, 10).map((item) => this.formatValue(item));
|
|
886
|
+
return `[${items.join(', ')}]`;
|
|
887
|
+
}
|
|
888
|
+
if (type === 'object') {
|
|
889
|
+
const entries = Object.entries(value).slice(0, 20);
|
|
890
|
+
const props = entries.map(([k, v]) => `${k}: ${this.formatValue(v)}`);
|
|
891
|
+
return `{${props.join(', ')}}`;
|
|
892
|
+
}
|
|
893
|
+
return 'null';
|
|
894
|
+
}
|
|
895
|
+
formatValueForJS(value, depth = 0) {
|
|
896
|
+
if (depth > 5)
|
|
897
|
+
return 'null';
|
|
898
|
+
if (value === null)
|
|
899
|
+
return 'null';
|
|
900
|
+
if (value === undefined)
|
|
901
|
+
return 'undefined';
|
|
902
|
+
if (typeof value === 'string') {
|
|
903
|
+
if (value === '[Function]' || value.startsWith('[Function:')) {
|
|
904
|
+
return 'function() {}';
|
|
905
|
+
}
|
|
906
|
+
if (value === '[Circular Reference]') {
|
|
907
|
+
return '{}';
|
|
908
|
+
}
|
|
909
|
+
if (value === '[Max Depth]' || value === '[Error]' || value.startsWith('[Error:')) {
|
|
910
|
+
return 'null';
|
|
911
|
+
}
|
|
912
|
+
if (value === '[Getter Error]') {
|
|
913
|
+
return 'undefined';
|
|
914
|
+
}
|
|
915
|
+
return JSON.stringify(value);
|
|
916
|
+
}
|
|
917
|
+
if (typeof value === 'number') {
|
|
918
|
+
return isNaN(value) ? 'NaN' : isFinite(value) ? String(value) : 'null';
|
|
919
|
+
}
|
|
920
|
+
if (typeof value === 'boolean') {
|
|
921
|
+
return String(value);
|
|
922
|
+
}
|
|
923
|
+
if (value && typeof value === 'object' && value.__type === 'Function') {
|
|
924
|
+
return 'function() {}';
|
|
925
|
+
}
|
|
926
|
+
if (Array.isArray(value)) {
|
|
927
|
+
const items = value
|
|
928
|
+
.slice(0, 50)
|
|
929
|
+
.map((item) => this.formatValueForJS(item, depth + 1))
|
|
930
|
+
.filter((item) => item !== 'undefined');
|
|
931
|
+
return `[${items.join(', ')}]`;
|
|
932
|
+
}
|
|
933
|
+
if (typeof value === 'object') {
|
|
934
|
+
const entries = Object.entries(value)
|
|
935
|
+
.filter(([k]) => !k.startsWith('__'))
|
|
936
|
+
.slice(0, 100);
|
|
937
|
+
if (entries.length === 0) {
|
|
938
|
+
return '{}';
|
|
939
|
+
}
|
|
940
|
+
const props = entries
|
|
941
|
+
.map(([k, v]) => {
|
|
942
|
+
const formattedValue = this.formatValueForJS(v, depth + 1);
|
|
943
|
+
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? k : JSON.stringify(k);
|
|
944
|
+
return `${key}: ${formattedValue}`;
|
|
945
|
+
})
|
|
946
|
+
.filter((prop) => !prop.endsWith(': undefined'));
|
|
947
|
+
return `{${props.join(', ')}}`;
|
|
948
|
+
}
|
|
949
|
+
return 'null';
|
|
950
|
+
}
|
|
951
|
+
generateRecommendations(detected, missingAPIs) {
|
|
952
|
+
const recommendations = [];
|
|
953
|
+
const totalVars = Object.values(detected).reduce((sum, arr) => sum + arr.length, 0);
|
|
954
|
+
if (totalVars > 50) {
|
|
955
|
+
recommendations.push('检测到大量环境变量访问,建议使用真实浏览器环境提取功能');
|
|
956
|
+
}
|
|
957
|
+
if (missingAPIs.length > 0) {
|
|
958
|
+
recommendations.push(`有 ${missingAPIs.length} 个API需要手动补充`);
|
|
959
|
+
}
|
|
960
|
+
return recommendations;
|
|
961
|
+
}
|
|
962
|
+
async generateMissingAPIImplementationsWithAI(missingAPIs, code, manifest) {
|
|
963
|
+
if (!this.llm || missingAPIs.length === 0) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
try {
|
|
967
|
+
const apisToGenerate = missingAPIs.slice(0, 10);
|
|
968
|
+
const systemPrompt = `# Role
|
|
969
|
+
You are a browser API implementation expert.
|
|
970
|
+
|
|
971
|
+
# Task
|
|
972
|
+
Generate realistic JavaScript implementations for missing browser APIs.
|
|
973
|
+
|
|
974
|
+
# Requirements
|
|
975
|
+
1. Follow W3C specifications
|
|
976
|
+
2. Match real browser behavior
|
|
977
|
+
3. Handle edge cases
|
|
978
|
+
4. Include proper error handling
|
|
979
|
+
5. Make functions look native (toString returns "[native code]")`;
|
|
980
|
+
const userPrompt = `# Missing APIs
|
|
981
|
+
${JSON.stringify(apisToGenerate.map(api => ({ path: api.path, type: api.type })), null, 2)}
|
|
982
|
+
|
|
983
|
+
# Code Context
|
|
984
|
+
\`\`\`javascript
|
|
985
|
+
${code.substring(0, 1500)}${code.length > 1500 ? '\n// ... (truncated)' : ''}
|
|
986
|
+
\`\`\`
|
|
987
|
+
|
|
988
|
+
# Required Output
|
|
989
|
+
Return ONLY valid JSON object mapping API paths to implementations:
|
|
990
|
+
|
|
991
|
+
\`\`\`json
|
|
992
|
+
{
|
|
993
|
+
"window.requestAnimationFrame": "function(callback) { return setTimeout(callback, 16); }",
|
|
994
|
+
"navigator.getBattery": "function() { return Promise.resolve({ level: 1, charging: true }); }",
|
|
995
|
+
"...": "other implementations"
|
|
996
|
+
}
|
|
997
|
+
\`\`\`
|
|
998
|
+
|
|
999
|
+
Return ONLY the JSON object:`;
|
|
1000
|
+
const response = await this.llm.chat([
|
|
1001
|
+
{ role: 'system', content: systemPrompt },
|
|
1002
|
+
{ role: 'user', content: userPrompt },
|
|
1003
|
+
]);
|
|
1004
|
+
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)\s*```/) ||
|
|
1005
|
+
response.content.match(/\{[\s\S]*\}/);
|
|
1006
|
+
if (jsonMatch) {
|
|
1007
|
+
const jsonStr = jsonMatch[1] || jsonMatch[0];
|
|
1008
|
+
const implementations = JSON.parse(jsonStr);
|
|
1009
|
+
let addedCount = 0;
|
|
1010
|
+
for (const [path, impl] of Object.entries(implementations)) {
|
|
1011
|
+
if (typeof impl === 'string' && impl.trim()) {
|
|
1012
|
+
manifest[path] = impl;
|
|
1013
|
+
addedCount++;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
logger.info(`✅ AI成功生成 ${addedCount} 个API实现`);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
catch (error) {
|
|
1020
|
+
logger.error('AI生成API实现失败', error);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
async inferMissingVariablesWithAI(code, detected, existingManifest, browserType) {
|
|
1024
|
+
if (!this.llm) {
|
|
1025
|
+
return {};
|
|
1026
|
+
}
|
|
1027
|
+
try {
|
|
1028
|
+
const allDetectedPaths = [
|
|
1029
|
+
...detected.window,
|
|
1030
|
+
...detected.document,
|
|
1031
|
+
...detected.navigator,
|
|
1032
|
+
...detected.location,
|
|
1033
|
+
...detected.screen,
|
|
1034
|
+
...detected.other,
|
|
1035
|
+
];
|
|
1036
|
+
const missingPaths = allDetectedPaths.filter(path => !(path in existingManifest));
|
|
1037
|
+
if (missingPaths.length === 0) {
|
|
1038
|
+
logger.info('所有检测到的变量都已填充,无需AI推断');
|
|
1039
|
+
return {};
|
|
1040
|
+
}
|
|
1041
|
+
logger.info(`🤖 AI推断 ${missingPaths.length} 个缺失的环境变量...`);
|
|
1042
|
+
const systemPrompt = `# Role
|
|
1043
|
+
You are a browser environment expert specializing in realistic browser API value generation.
|
|
1044
|
+
|
|
1045
|
+
# Task
|
|
1046
|
+
Generate realistic values for missing browser environment variables based on code analysis.
|
|
1047
|
+
|
|
1048
|
+
# Requirements
|
|
1049
|
+
1. Values must be realistic and match real browser behavior
|
|
1050
|
+
2. Ensure consistency across related variables (e.g., UA matches platform)
|
|
1051
|
+
3. Consider anti-detection (avoid obvious fake values)
|
|
1052
|
+
4. Follow W3C specifications for API return types`;
|
|
1053
|
+
const userPrompt = `# Target Browser
|
|
1054
|
+
${browserType.toUpperCase()}
|
|
1055
|
+
|
|
1056
|
+
# Missing Variables (need values)
|
|
1057
|
+
${JSON.stringify(missingPaths, null, 2)}
|
|
1058
|
+
|
|
1059
|
+
# Code Context (for understanding usage)
|
|
1060
|
+
\`\`\`javascript
|
|
1061
|
+
${code.substring(0, 2000)}${code.length > 2000 ? '\n// ... (truncated)' : ''}
|
|
1062
|
+
\`\`\`
|
|
1063
|
+
|
|
1064
|
+
# Existing Variables (for consistency)
|
|
1065
|
+
${JSON.stringify(existingManifest, null, 2)}
|
|
1066
|
+
|
|
1067
|
+
# Required Output
|
|
1068
|
+
Return ONLY valid JSON object with missing variable paths as keys and realistic values:
|
|
1069
|
+
|
|
1070
|
+
\`\`\`json
|
|
1071
|
+
{
|
|
1072
|
+
"navigator.userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...",
|
|
1073
|
+
"navigator.platform": "Win32",
|
|
1074
|
+
"window.innerWidth": 1920,
|
|
1075
|
+
"...": "other missing variables"
|
|
1076
|
+
}
|
|
1077
|
+
\`\`\`
|
|
1078
|
+
|
|
1079
|
+
# Guidelines
|
|
1080
|
+
- Use realistic values matching target browser
|
|
1081
|
+
- Ensure cross-variable consistency
|
|
1082
|
+
- Consider code usage patterns
|
|
1083
|
+
- Avoid placeholder values like "test" or "example"
|
|
1084
|
+
|
|
1085
|
+
Return ONLY the JSON object:`;
|
|
1086
|
+
const response = await this.llm.chat([
|
|
1087
|
+
{ role: 'system', content: systemPrompt },
|
|
1088
|
+
{ role: 'user', content: userPrompt },
|
|
1089
|
+
]);
|
|
1090
|
+
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)\s*```/) ||
|
|
1091
|
+
response.content.match(/\{[\s\S]*\}/);
|
|
1092
|
+
if (jsonMatch) {
|
|
1093
|
+
const jsonStr = jsonMatch[1] || jsonMatch[0];
|
|
1094
|
+
const inferredVars = JSON.parse(jsonStr);
|
|
1095
|
+
logger.info(`✅ AI成功推断 ${Object.keys(inferredVars).length} 个环境变量`);
|
|
1096
|
+
return inferredVars;
|
|
1097
|
+
}
|
|
1098
|
+
logger.warn('AI响应中未找到有效的JSON');
|
|
1099
|
+
return {};
|
|
1100
|
+
}
|
|
1101
|
+
catch (error) {
|
|
1102
|
+
logger.error('AI推断环境变量失败', error);
|
|
1103
|
+
return {};
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
async cleanup() {
|
|
1107
|
+
if (this.browser) {
|
|
1108
|
+
await this.browser.close();
|
|
1109
|
+
this.browser = undefined;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
//# sourceMappingURL=EnvironmentEmulator.js.map
|