code-auditor-mcp 1.20.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/GoAdapter.d.ts +53 -0
- package/dist/adapters/GoAdapter.d.ts.map +1 -0
- package/dist/adapters/GoAdapter.js +602 -0
- package/dist/adapters/GoAdapter.js.map +1 -0
- package/dist/adapters/GoConfig.d.ts +35 -0
- package/dist/adapters/GoConfig.d.ts.map +1 -0
- package/dist/adapters/GoConfig.js +155 -0
- package/dist/adapters/GoConfig.js.map +1 -0
- package/dist/adapters/LanguageAdapter.d.ts +135 -0
- package/dist/adapters/LanguageAdapter.d.ts.map +1 -0
- package/dist/adapters/LanguageAdapter.js +47 -0
- package/dist/adapters/LanguageAdapter.js.map +1 -0
- package/dist/adapters/TypeScriptAdapter.d.ts +43 -0
- package/dist/adapters/TypeScriptAdapter.d.ts.map +1 -0
- package/dist/adapters/TypeScriptAdapter.js +386 -0
- package/dist/adapters/TypeScriptAdapter.js.map +1 -0
- package/dist/adapters/UniversalAnalyzer.d.ts +42 -0
- package/dist/adapters/UniversalAnalyzer.d.ts.map +1 -0
- package/dist/adapters/UniversalAnalyzer.js +103 -0
- package/dist/adapters/UniversalAnalyzer.js.map +1 -0
- package/dist/adapters/UniversalSOLIDAnalyzer.d.ts +48 -0
- package/dist/adapters/UniversalSOLIDAnalyzer.d.ts.map +1 -0
- package/dist/adapters/UniversalSOLIDAnalyzer.js +178 -0
- package/dist/adapters/UniversalSOLIDAnalyzer.js.map +1 -0
- package/dist/adapters/solidAnalyzerCompat.d.ts +21 -0
- package/dist/adapters/solidAnalyzerCompat.d.ts.map +1 -0
- package/dist/adapters/solidAnalyzerCompat.js +78 -0
- package/dist/adapters/solidAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +103 -0
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +425 -0
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -0
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts +117 -0
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js +618 -0
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -0
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +129 -0
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -0
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +473 -0
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -0
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +126 -0
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -0
- package/dist/analyzers/cross-language/SchemaValidator.js +522 -0
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -0
- package/dist/analyzers/dataAccessAnalyzerCompat.d.ts +20 -0
- package/dist/analyzers/dataAccessAnalyzerCompat.d.ts.map +1 -0
- package/dist/analyzers/dataAccessAnalyzerCompat.js +59 -0
- package/dist/analyzers/dataAccessAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/documentationAnalyzerCompat.d.ts +20 -0
- package/dist/analyzers/documentationAnalyzerCompat.d.ts.map +1 -0
- package/dist/analyzers/documentationAnalyzerCompat.js +52 -0
- package/dist/analyzers/documentationAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/dryAnalyzerCompat.d.ts +20 -0
- package/dist/analyzers/dryAnalyzerCompat.d.ts.map +1 -0
- package/dist/analyzers/dryAnalyzerCompat.js +44 -0
- package/dist/analyzers/dryAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/schemaAnalyzer.d.ts +21 -0
- package/dist/analyzers/schemaAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/schemaAnalyzer.js +405 -0
- package/dist/analyzers/schemaAnalyzer.js.map +1 -0
- package/dist/analyzers/schemaAnalyzerCompat.d.ts +20 -0
- package/dist/analyzers/schemaAnalyzerCompat.d.ts.map +1 -0
- package/dist/analyzers/schemaAnalyzerCompat.js +69 -0
- package/dist/analyzers/schemaAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/solidAnalyzerCompat.d.ts +20 -0
- package/dist/analyzers/solidAnalyzerCompat.d.ts.map +1 -0
- package/dist/analyzers/solidAnalyzerCompat.js +78 -0
- package/dist/analyzers/solidAnalyzerCompat.js.map +1 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +71 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +276 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +82 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +489 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts +48 -0
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +175 -0
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +64 -0
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +286 -0
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +119 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +812 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -0
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +23 -7
- package/dist/auditRunner.js.map +1 -1
- package/dist/codeIndexDB-enhanced.d.ts +80 -0
- package/dist/codeIndexDB-enhanced.d.ts.map +1 -0
- package/dist/codeIndexDB-enhanced.js +488 -0
- package/dist/codeIndexDB-enhanced.js.map +1 -0
- package/dist/codeIndexDb.d.ts +70 -1
- package/dist/codeIndexDb.d.ts.map +1 -1
- package/dist/codeIndexDb.js +199 -2
- package/dist/codeIndexDb.js.map +1 -1
- package/dist/config/defaults.js +1 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/languages/LanguageOrchestrator.d.ts +181 -0
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -0
- package/dist/languages/LanguageOrchestrator.js +365 -0
- package/dist/languages/LanguageOrchestrator.js.map +1 -0
- package/dist/languages/LanguageRegistry.d.ts +43 -0
- package/dist/languages/LanguageRegistry.d.ts.map +1 -0
- package/dist/languages/LanguageRegistry.js +86 -0
- package/dist/languages/LanguageRegistry.js.map +1 -0
- package/dist/languages/RuntimeManager.d.ts +181 -0
- package/dist/languages/RuntimeManager.d.ts.map +1 -0
- package/dist/languages/RuntimeManager.js +841 -0
- package/dist/languages/RuntimeManager.js.map +1 -0
- package/dist/languages/UniversalAnalyzer.d.ts +37 -0
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -0
- package/dist/languages/UniversalAnalyzer.js +101 -0
- package/dist/languages/UniversalAnalyzer.js.map +1 -0
- package/dist/languages/go/GoAdapter.d.ts +57 -0
- package/dist/languages/go/GoAdapter.d.ts.map +1 -0
- package/dist/languages/go/GoAdapter.js +360 -0
- package/dist/languages/go/GoAdapter.js.map +1 -0
- package/dist/languages/index.d.ts +13 -0
- package/dist/languages/index.d.ts.map +1 -0
- package/dist/languages/index.js +23 -0
- package/dist/languages/index.js.map +1 -0
- package/dist/languages/types.d.ts +182 -0
- package/dist/languages/types.d.ts.map +1 -0
- package/dist/languages/types.js +5 -0
- package/dist/languages/types.js.map +1 -0
- package/dist/languages/typescript/TypeScriptAdapter.d.ts +108 -0
- package/dist/languages/typescript/TypeScriptAdapter.d.ts.map +1 -0
- package/dist/languages/typescript/TypeScriptAdapter.js +567 -0
- package/dist/languages/typescript/TypeScriptAdapter.js.map +1 -0
- package/dist/languages/typescript/test-extraction.d.ts +2 -0
- package/dist/languages/typescript/test-extraction.d.ts.map +1 -0
- package/dist/languages/typescript/test-extraction.js +36 -0
- package/dist/languages/typescript/test-extraction.js.map +1 -0
- package/dist/mcp-hybrid.d.ts +10 -0
- package/dist/mcp-hybrid.d.ts.map +1 -0
- package/dist/mcp-hybrid.js +41 -0
- package/dist/mcp-hybrid.js.map +1 -0
- package/dist/mcp-index.d.ts +11 -0
- package/dist/mcp-index.d.ts.map +1 -0
- package/dist/mcp-index.js +101 -0
- package/dist/mcp-index.js.map +1 -0
- package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +67 -10
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts +49 -0
- package/dist/mcp-tools-shared.d.ts.map +1 -0
- package/dist/mcp-tools-shared.js +1282 -0
- package/dist/mcp-tools-shared.js.map +1 -0
- package/dist/mcp-ui-server.d.ts +27 -0
- package/dist/mcp-ui-server.d.ts.map +1 -0
- package/dist/mcp-ui-server.js +484 -0
- package/dist/mcp-ui-server.js.map +1 -0
- package/dist/mcp-ui-simple.d.ts +27 -0
- package/dist/mcp-ui-simple.d.ts.map +1 -0
- package/dist/mcp-ui-simple.js +425 -0
- package/dist/mcp-ui-simple.js.map +1 -0
- package/dist/mcp.js +7 -4
- package/dist/mcp.js.map +1 -1
- package/dist/services/SchemaParser.d.ts +89 -0
- package/dist/services/SchemaParser.d.ts.map +1 -0
- package/dist/services/SchemaParser.js +434 -0
- package/dist/services/SchemaParser.js.map +1 -0
- package/dist/types/crossLanguage.d.ts +267 -0
- package/dist/types/crossLanguage.d.ts.map +1 -0
- package/dist/types/crossLanguage.js +6 -0
- package/dist/types/crossLanguage.js.map +1 -0
- package/dist/types.d.ts +128 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/fileDiscovery.d.ts +6 -0
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js +12 -1
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/examples/schema-example.json +364 -0
- package/examples/schema-example.yaml +137 -0
- package/package.json +10 -3
|
@@ -0,0 +1,841 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Manager
|
|
3
|
+
* Detects available language runtimes and manages analyzer processes
|
|
4
|
+
*/
|
|
5
|
+
import { exec, spawn } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
export class RuntimeManager {
|
|
11
|
+
runtimes = new Map();
|
|
12
|
+
initialized = false;
|
|
13
|
+
config = new Map();
|
|
14
|
+
constructor(config) {
|
|
15
|
+
if (config) {
|
|
16
|
+
for (const [runtime, cfg] of Object.entries(config)) {
|
|
17
|
+
this.config.set(runtime, cfg);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Initialize runtime detection
|
|
23
|
+
*/
|
|
24
|
+
async initialize() {
|
|
25
|
+
console.error('[RuntimeManager] Initializing runtime detection...');
|
|
26
|
+
await Promise.all([
|
|
27
|
+
this.detectNodeRuntime(),
|
|
28
|
+
this.detectGoRuntime(),
|
|
29
|
+
this.detectPythonRuntime(),
|
|
30
|
+
this.detectRustRuntime(),
|
|
31
|
+
this.detectDenoRuntime(),
|
|
32
|
+
this.detectBunRuntime()
|
|
33
|
+
]);
|
|
34
|
+
this.initialized = true;
|
|
35
|
+
console.error('[RuntimeManager] Runtime detection complete');
|
|
36
|
+
this.logRuntimeStatus();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if a specific runtime is available
|
|
40
|
+
*/
|
|
41
|
+
hasRuntime(name) {
|
|
42
|
+
const runtime = this.runtimes.get(name);
|
|
43
|
+
const config = this.config.get(name);
|
|
44
|
+
return runtime?.available === true &&
|
|
45
|
+
(config?.enabled !== false) &&
|
|
46
|
+
this.checkVersionCompatibility(runtime, config);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get fallback runtime for a language if the primary is unavailable
|
|
50
|
+
*/
|
|
51
|
+
getFallbackRuntime(language) {
|
|
52
|
+
const fallbackMap = {
|
|
53
|
+
'typescript': ['node', 'deno', 'bun'],
|
|
54
|
+
'javascript': ['node', 'deno', 'bun'],
|
|
55
|
+
'go': ['go'],
|
|
56
|
+
'python': ['python'],
|
|
57
|
+
'rust': ['rust']
|
|
58
|
+
};
|
|
59
|
+
const fallbacks = fallbackMap[language] || [];
|
|
60
|
+
for (const fallback of fallbacks) {
|
|
61
|
+
if (this.hasRuntime(fallback)) {
|
|
62
|
+
const runtime = this.runtimes.get(fallback);
|
|
63
|
+
if (runtime?.analyzer?.supports.some(ext => this.getLanguageExtensions(language).includes(ext))) {
|
|
64
|
+
return runtime;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get file extensions for a language
|
|
72
|
+
*/
|
|
73
|
+
getLanguageExtensions(language) {
|
|
74
|
+
const extensionMap = {
|
|
75
|
+
'typescript': ['.ts', '.tsx'],
|
|
76
|
+
'javascript': ['.js', '.jsx'],
|
|
77
|
+
'go': ['.go'],
|
|
78
|
+
'python': ['.py'],
|
|
79
|
+
'rust': ['.rs']
|
|
80
|
+
};
|
|
81
|
+
return extensionMap[language] || [];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Attempt to analyze with fallback runtime if primary fails
|
|
85
|
+
*/
|
|
86
|
+
async analyzeWithFallback(language, files, options) {
|
|
87
|
+
// Try primary runtime first
|
|
88
|
+
let result = await this.spawnAnalyzer(language, files, options);
|
|
89
|
+
if (result && result.errors?.length === 0) {
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
// If primary failed, try fallback
|
|
93
|
+
const fallbackRuntime = this.getFallbackRuntime(language);
|
|
94
|
+
if (fallbackRuntime) {
|
|
95
|
+
console.log(`[RuntimeManager] Primary ${language} runtime failed, trying fallback: ${fallbackRuntime.name}`);
|
|
96
|
+
try {
|
|
97
|
+
const fallbackResult = await fallbackRuntime.analyzer.analyze(files, {
|
|
98
|
+
...options,
|
|
99
|
+
fallbackMode: true,
|
|
100
|
+
originalLanguage: language
|
|
101
|
+
});
|
|
102
|
+
// Add warning about fallback usage
|
|
103
|
+
fallbackResult.errors = fallbackResult.errors || [];
|
|
104
|
+
fallbackResult.errors.push({
|
|
105
|
+
message: `Analysis completed using fallback runtime ${fallbackRuntime.name} instead of native ${language} runtime`,
|
|
106
|
+
type: 'fallback_warning',
|
|
107
|
+
language
|
|
108
|
+
});
|
|
109
|
+
return fallbackResult;
|
|
110
|
+
}
|
|
111
|
+
catch (fallbackError) {
|
|
112
|
+
console.error(`[RuntimeManager] Fallback runtime also failed:`, fallbackError);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// No fallback available or fallback also failed
|
|
116
|
+
return {
|
|
117
|
+
violations: [],
|
|
118
|
+
indexEntries: [],
|
|
119
|
+
metrics: { filesAnalyzed: 0, executionTime: 0 },
|
|
120
|
+
errors: [{
|
|
121
|
+
message: `No working runtime available for ${language}. Install ${language} runtime or enable fallback options.`,
|
|
122
|
+
type: 'no_runtime',
|
|
123
|
+
language,
|
|
124
|
+
suggestions: this.getRuntimeInstallationSuggestions(language)
|
|
125
|
+
}]
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get installation suggestions for missing runtimes
|
|
130
|
+
*/
|
|
131
|
+
getRuntimeInstallationSuggestions(language) {
|
|
132
|
+
const suggestions = {
|
|
133
|
+
'go': [
|
|
134
|
+
'Install Go: https://golang.org/dl/',
|
|
135
|
+
'Or use package manager: brew install go (macOS), apt install golang (Ubuntu)'
|
|
136
|
+
],
|
|
137
|
+
'python': [
|
|
138
|
+
'Install Python: https://python.org/downloads/',
|
|
139
|
+
'Or use package manager: brew install python (macOS), apt install python3 (Ubuntu)'
|
|
140
|
+
],
|
|
141
|
+
'rust': [
|
|
142
|
+
'Install Rust: https://rustup.rs/',
|
|
143
|
+
'Run: curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh'
|
|
144
|
+
],
|
|
145
|
+
'node': [
|
|
146
|
+
'Install Node.js: https://nodejs.org/',
|
|
147
|
+
'Or use nvm: nvm install node'
|
|
148
|
+
]
|
|
149
|
+
};
|
|
150
|
+
return suggestions[language] || ['Check the official documentation for installation instructions'];
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get runtime information
|
|
154
|
+
*/
|
|
155
|
+
getRuntime(name) {
|
|
156
|
+
return this.runtimes.get(name);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get all available runtimes
|
|
160
|
+
*/
|
|
161
|
+
getAvailableRuntimes() {
|
|
162
|
+
return Array.from(this.runtimes.values()).filter(r => r.available);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Spawn analyzer for a specific language
|
|
166
|
+
*/
|
|
167
|
+
async spawnAnalyzer(language, files, options) {
|
|
168
|
+
if (!this.initialized) {
|
|
169
|
+
await this.initialize();
|
|
170
|
+
}
|
|
171
|
+
const runtime = this.runtimes.get(language);
|
|
172
|
+
if (!runtime?.available || !runtime.analyzer) {
|
|
173
|
+
console.warn(`[RuntimeManager] No analyzer available for ${language}`);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
console.log(`[RuntimeManager] Spawning ${language} analyzer for ${files.length} files`);
|
|
177
|
+
const startTime = Date.now();
|
|
178
|
+
const processTimeout = options?.timeout || 300000; // 5 minutes default
|
|
179
|
+
try {
|
|
180
|
+
const result = await this.runWithTimeout(() => runtime.analyzer.analyze(files, options), processTimeout);
|
|
181
|
+
const executionTime = Date.now() - startTime;
|
|
182
|
+
result.metrics.executionTime = executionTime;
|
|
183
|
+
console.log(`[RuntimeManager] ${language} analysis complete in ${executionTime}ms: ${result.violations.length} violations`);
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
const executionTime = Date.now() - startTime;
|
|
188
|
+
console.error(`[RuntimeManager] Error in ${language} analyzer after ${executionTime}ms:`, error);
|
|
189
|
+
return {
|
|
190
|
+
violations: [],
|
|
191
|
+
indexEntries: [],
|
|
192
|
+
metrics: { filesAnalyzed: 0, executionTime },
|
|
193
|
+
errors: [{
|
|
194
|
+
message: error instanceof Error ? error.message : String(error),
|
|
195
|
+
language,
|
|
196
|
+
type: error instanceof Error && error.message.includes('timeout') ? 'timeout' : 'runtime_error'
|
|
197
|
+
}]
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Run a function with timeout
|
|
203
|
+
*/
|
|
204
|
+
async runWithTimeout(fn, timeoutMs) {
|
|
205
|
+
return new Promise((resolve, reject) => {
|
|
206
|
+
const timeoutId = setTimeout(() => {
|
|
207
|
+
reject(new Error(`Analysis timed out after ${timeoutMs}ms`));
|
|
208
|
+
}, timeoutMs);
|
|
209
|
+
fn()
|
|
210
|
+
.then(result => {
|
|
211
|
+
clearTimeout(timeoutId);
|
|
212
|
+
resolve(result);
|
|
213
|
+
})
|
|
214
|
+
.catch(error => {
|
|
215
|
+
clearTimeout(timeoutId);
|
|
216
|
+
reject(error);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Spawn multiple analyzers in parallel with concurrency control
|
|
222
|
+
*/
|
|
223
|
+
async spawnMultipleAnalyzers(languageFilePairs, options) {
|
|
224
|
+
const maxConcurrency = options?.maxConcurrency || 3;
|
|
225
|
+
const results = [];
|
|
226
|
+
console.log(`[RuntimeManager] Spawning ${languageFilePairs.length} analyzers with max concurrency ${maxConcurrency}`);
|
|
227
|
+
// Process in batches to control concurrency
|
|
228
|
+
for (let i = 0; i < languageFilePairs.length; i += maxConcurrency) {
|
|
229
|
+
const batch = languageFilePairs.slice(i, i + maxConcurrency);
|
|
230
|
+
const batchPromises = batch.map(async ({ language, files }) => {
|
|
231
|
+
const result = await this.spawnAnalyzer(language, files, options);
|
|
232
|
+
return { language, result };
|
|
233
|
+
});
|
|
234
|
+
const batchResults = await Promise.all(batchPromises);
|
|
235
|
+
results.push(...batchResults);
|
|
236
|
+
}
|
|
237
|
+
return results;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Kill all running analyzer processes (for cleanup)
|
|
241
|
+
*/
|
|
242
|
+
async killAllAnalyzers() {
|
|
243
|
+
console.log('[RuntimeManager] Killing all analyzer processes...');
|
|
244
|
+
// This is a placeholder for process management
|
|
245
|
+
// In future implementations, we'll track child processes and kill them here
|
|
246
|
+
// For now, we just log the action since our analyzers run in-process
|
|
247
|
+
console.log('[RuntimeManager] All analyzer processes terminated');
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Detect Node.js runtime
|
|
251
|
+
*/
|
|
252
|
+
async detectNodeRuntime() {
|
|
253
|
+
try {
|
|
254
|
+
const { stdout } = await execAsync('node --version');
|
|
255
|
+
const version = stdout.trim();
|
|
256
|
+
this.runtimes.set('node', {
|
|
257
|
+
name: 'Node.js',
|
|
258
|
+
command: 'node',
|
|
259
|
+
version,
|
|
260
|
+
available: true,
|
|
261
|
+
minVersion: '16.0.0',
|
|
262
|
+
analyzer: new TypeScriptAnalyzer()
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
this.runtimes.set('node', {
|
|
267
|
+
name: 'Node.js',
|
|
268
|
+
command: 'node',
|
|
269
|
+
version: 'unknown',
|
|
270
|
+
available: false
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Detect Go runtime
|
|
276
|
+
*/
|
|
277
|
+
async detectGoRuntime() {
|
|
278
|
+
try {
|
|
279
|
+
console.error('[RuntimeManager] Detecting Go runtime...');
|
|
280
|
+
const { stdout } = await execAsync('go version');
|
|
281
|
+
const versionMatch = stdout.match(/go(\d+\.\d+\.\d+)/);
|
|
282
|
+
const version = versionMatch ? versionMatch[1] : stdout.trim();
|
|
283
|
+
console.error('[RuntimeManager] Go version detected:', version);
|
|
284
|
+
// Check if our Go analyzer exists
|
|
285
|
+
const analyzerPath = path.join(__dirname, 'go', 'analyzer');
|
|
286
|
+
console.error('[RuntimeManager] Looking for Go analyzer at:', analyzerPath);
|
|
287
|
+
const analyzerExists = await this.fileExists(analyzerPath) || await this.fileExists(analyzerPath + '.exe');
|
|
288
|
+
console.error('[RuntimeManager] Go analyzer exists:', analyzerExists);
|
|
289
|
+
this.runtimes.set('go', {
|
|
290
|
+
name: 'Go',
|
|
291
|
+
command: 'go',
|
|
292
|
+
version,
|
|
293
|
+
available: true,
|
|
294
|
+
minVersion: '1.18.0',
|
|
295
|
+
executablePath: analyzerExists ? analyzerPath : undefined,
|
|
296
|
+
analyzer: new GoAnalyzer(analyzerPath)
|
|
297
|
+
});
|
|
298
|
+
console.error('[RuntimeManager] Go runtime configured successfully');
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
this.runtimes.set('go', {
|
|
302
|
+
name: 'Go',
|
|
303
|
+
command: 'go',
|
|
304
|
+
version: 'unknown',
|
|
305
|
+
available: false
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Detect Python runtime
|
|
311
|
+
*/
|
|
312
|
+
async detectPythonRuntime() {
|
|
313
|
+
const pythonCommands = ['python3', 'python'];
|
|
314
|
+
for (const cmd of pythonCommands) {
|
|
315
|
+
try {
|
|
316
|
+
const { stdout } = await execAsync(`${cmd} --version`);
|
|
317
|
+
const versionMatch = stdout.match(/Python (\d+\.\d+\.\d+)/);
|
|
318
|
+
const version = versionMatch ? versionMatch[1] : stdout.trim();
|
|
319
|
+
this.runtimes.set('python', {
|
|
320
|
+
name: 'Python',
|
|
321
|
+
command: cmd,
|
|
322
|
+
version,
|
|
323
|
+
available: true,
|
|
324
|
+
minVersion: '3.8.0',
|
|
325
|
+
analyzer: new PythonAnalyzer(cmd)
|
|
326
|
+
});
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
// Try next command
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
this.runtimes.set('python', {
|
|
334
|
+
name: 'Python',
|
|
335
|
+
command: 'python',
|
|
336
|
+
version: 'unknown',
|
|
337
|
+
available: false
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Detect Rust runtime
|
|
342
|
+
*/
|
|
343
|
+
async detectRustRuntime() {
|
|
344
|
+
try {
|
|
345
|
+
const { stdout } = await execAsync('rustc --version');
|
|
346
|
+
const versionMatch = stdout.match(/rustc (\d+\.\d+\.\d+)/);
|
|
347
|
+
const version = versionMatch ? versionMatch[1] : stdout.trim();
|
|
348
|
+
this.runtimes.set('rust', {
|
|
349
|
+
name: 'Rust',
|
|
350
|
+
command: 'rustc',
|
|
351
|
+
version,
|
|
352
|
+
available: true,
|
|
353
|
+
minVersion: '1.60.0'
|
|
354
|
+
// Rust analyzer will be implemented in future phases
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
this.runtimes.set('rust', {
|
|
359
|
+
name: 'Rust',
|
|
360
|
+
command: 'rustc',
|
|
361
|
+
version: 'unknown',
|
|
362
|
+
available: false
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Detect Deno runtime
|
|
368
|
+
*/
|
|
369
|
+
async detectDenoRuntime() {
|
|
370
|
+
try {
|
|
371
|
+
const { stdout } = await execAsync('deno --version');
|
|
372
|
+
const versionMatch = stdout.match(/deno (\d+\.\d+\.\d+)/);
|
|
373
|
+
const version = versionMatch ? versionMatch[1] : stdout.trim();
|
|
374
|
+
this.runtimes.set('deno', {
|
|
375
|
+
name: 'Deno',
|
|
376
|
+
command: 'deno',
|
|
377
|
+
version,
|
|
378
|
+
available: true,
|
|
379
|
+
minVersion: '1.20.0'
|
|
380
|
+
// Could reuse TypeScript analyzer with Deno-specific adaptations
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
this.runtimes.set('deno', {
|
|
385
|
+
name: 'Deno',
|
|
386
|
+
command: 'deno',
|
|
387
|
+
version: 'unknown',
|
|
388
|
+
available: false
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Detect Bun runtime
|
|
394
|
+
*/
|
|
395
|
+
async detectBunRuntime() {
|
|
396
|
+
try {
|
|
397
|
+
const { stdout } = await execAsync('bun --version');
|
|
398
|
+
const version = stdout.trim();
|
|
399
|
+
this.runtimes.set('bun', {
|
|
400
|
+
name: 'Bun',
|
|
401
|
+
command: 'bun',
|
|
402
|
+
version,
|
|
403
|
+
available: true,
|
|
404
|
+
minVersion: '0.5.0'
|
|
405
|
+
// Could reuse TypeScript analyzer with Bun-specific adaptations
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
this.runtimes.set('bun', {
|
|
410
|
+
name: 'Bun',
|
|
411
|
+
command: 'bun',
|
|
412
|
+
version: 'unknown',
|
|
413
|
+
available: false
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Check if a file exists
|
|
419
|
+
*/
|
|
420
|
+
async fileExists(filePath) {
|
|
421
|
+
try {
|
|
422
|
+
await fs.access(filePath);
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Check version compatibility with detailed reporting
|
|
431
|
+
*/
|
|
432
|
+
checkVersionCompatibility(runtime, config) {
|
|
433
|
+
const minVersion = config?.minVersion || runtime.minVersion;
|
|
434
|
+
if (!minVersion || !runtime.version || runtime.version === 'unknown') {
|
|
435
|
+
console.log(`[RuntimeManager] Version compatibility check skipped for ${runtime.name} (version: ${runtime.version})`);
|
|
436
|
+
return true; // If we can't determine version, assume compatible
|
|
437
|
+
}
|
|
438
|
+
const isCompatible = this.compareVersions(runtime.version, minVersion) >= 0;
|
|
439
|
+
if (!isCompatible) {
|
|
440
|
+
console.warn(`[RuntimeManager] ${runtime.name} version ${runtime.version} is below minimum required ${minVersion}`);
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
console.log(`[RuntimeManager] ${runtime.name} version ${runtime.version} meets minimum requirement ${minVersion}`);
|
|
444
|
+
}
|
|
445
|
+
return isCompatible;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Get detailed version compatibility report
|
|
449
|
+
*/
|
|
450
|
+
getVersionCompatibilityReport() {
|
|
451
|
+
const report = [];
|
|
452
|
+
for (const [name, runtime] of this.runtimes) {
|
|
453
|
+
const config = this.config.get(name);
|
|
454
|
+
const minVersion = config?.minVersion || runtime.minVersion;
|
|
455
|
+
const compatible = this.checkVersionCompatibility(runtime, config);
|
|
456
|
+
let status;
|
|
457
|
+
let recommendations = [];
|
|
458
|
+
if (!runtime.available) {
|
|
459
|
+
status = 'unknown';
|
|
460
|
+
recommendations.push(`Install ${runtime.name} runtime`);
|
|
461
|
+
recommendations.push(...this.getRuntimeInstallationSuggestions(name));
|
|
462
|
+
}
|
|
463
|
+
else if (runtime.version === 'unknown') {
|
|
464
|
+
status = 'unknown';
|
|
465
|
+
recommendations.push(`Unable to detect ${runtime.name} version`);
|
|
466
|
+
}
|
|
467
|
+
else if (!compatible) {
|
|
468
|
+
status = 'incompatible';
|
|
469
|
+
recommendations.push(`Update ${runtime.name} to version ${minVersion} or higher`);
|
|
470
|
+
recommendations.push(`Current: ${runtime.version}, Required: ${minVersion}`);
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
status = 'compatible';
|
|
474
|
+
}
|
|
475
|
+
report.push({
|
|
476
|
+
runtime: runtime.name,
|
|
477
|
+
currentVersion: runtime.version,
|
|
478
|
+
minVersion,
|
|
479
|
+
compatible: runtime.available && compatible,
|
|
480
|
+
status,
|
|
481
|
+
recommendations: recommendations.length > 0 ? recommendations : undefined
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
return report;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Validate all runtime versions and return summary
|
|
488
|
+
*/
|
|
489
|
+
async validateRuntimeVersions() {
|
|
490
|
+
if (!this.initialized) {
|
|
491
|
+
await this.initialize();
|
|
492
|
+
}
|
|
493
|
+
const report = this.getVersionCompatibilityReport();
|
|
494
|
+
const summary = {
|
|
495
|
+
compatible: 0,
|
|
496
|
+
incompatible: 0,
|
|
497
|
+
unknown: 0,
|
|
498
|
+
issues: []
|
|
499
|
+
};
|
|
500
|
+
for (const entry of report) {
|
|
501
|
+
switch (entry.status) {
|
|
502
|
+
case 'compatible':
|
|
503
|
+
summary.compatible++;
|
|
504
|
+
break;
|
|
505
|
+
case 'incompatible':
|
|
506
|
+
summary.incompatible++;
|
|
507
|
+
summary.issues.push({
|
|
508
|
+
runtime: entry.runtime,
|
|
509
|
+
issue: `Version ${entry.currentVersion} is below minimum ${entry.minVersion}`,
|
|
510
|
+
recommendations: entry.recommendations || []
|
|
511
|
+
});
|
|
512
|
+
break;
|
|
513
|
+
case 'unknown':
|
|
514
|
+
summary.unknown++;
|
|
515
|
+
summary.issues.push({
|
|
516
|
+
runtime: entry.runtime,
|
|
517
|
+
issue: 'Runtime not available or version unknown',
|
|
518
|
+
recommendations: entry.recommendations || []
|
|
519
|
+
});
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return summary;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Compare semantic versions with enhanced parsing
|
|
527
|
+
*/
|
|
528
|
+
compareVersions(version1, version2) {
|
|
529
|
+
// Handle various version formats: v1.2.3, 1.2.3, go1.19.5, etc.
|
|
530
|
+
const normalizeVersion = (version) => {
|
|
531
|
+
return version
|
|
532
|
+
.replace(/^v/, '') // Remove 'v' prefix
|
|
533
|
+
.replace(/^go/, '') // Remove 'go' prefix
|
|
534
|
+
.replace(/[^\d.]/g, '') // Remove non-digit, non-dot chars
|
|
535
|
+
.split('.')
|
|
536
|
+
.slice(0, 3) // Take only major.minor.patch
|
|
537
|
+
.join('.');
|
|
538
|
+
};
|
|
539
|
+
const v1 = normalizeVersion(version1);
|
|
540
|
+
const v2 = normalizeVersion(version2);
|
|
541
|
+
const v1Parts = v1.split('.').map(Number);
|
|
542
|
+
const v2Parts = v2.split('.').map(Number);
|
|
543
|
+
const maxLength = Math.max(v1Parts.length, v2Parts.length);
|
|
544
|
+
for (let i = 0; i < maxLength; i++) {
|
|
545
|
+
const v1Part = v1Parts[i] || 0;
|
|
546
|
+
const v2Part = v2Parts[i] || 0;
|
|
547
|
+
if (v1Part > v2Part)
|
|
548
|
+
return 1;
|
|
549
|
+
if (v1Part < v2Part)
|
|
550
|
+
return -1;
|
|
551
|
+
}
|
|
552
|
+
return 0;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Log runtime status
|
|
556
|
+
*/
|
|
557
|
+
logRuntimeStatus() {
|
|
558
|
+
console.log('\n[RuntimeManager] Runtime Status:');
|
|
559
|
+
for (const [name, runtime] of this.runtimes) {
|
|
560
|
+
const status = runtime.available ? '✅' : '❌';
|
|
561
|
+
const analyzer = runtime.analyzer ? '(analyzer available)' : '(no analyzer)';
|
|
562
|
+
console.log(` ${status} ${runtime.name} ${runtime.version} ${analyzer}`);
|
|
563
|
+
}
|
|
564
|
+
console.log('');
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Get runtime statistics
|
|
568
|
+
*/
|
|
569
|
+
getStats() {
|
|
570
|
+
const total = this.runtimes.size;
|
|
571
|
+
const available = Array.from(this.runtimes.values()).filter(r => r.available).length;
|
|
572
|
+
const withAnalyzers = Array.from(this.runtimes.values()).filter(r => r.available && r.analyzer).length;
|
|
573
|
+
return {
|
|
574
|
+
total,
|
|
575
|
+
available,
|
|
576
|
+
withAnalyzers,
|
|
577
|
+
runtimes: Object.fromEntries(Array.from(this.runtimes.entries()).map(([name, runtime]) => [
|
|
578
|
+
name,
|
|
579
|
+
{
|
|
580
|
+
available: runtime.available,
|
|
581
|
+
version: runtime.version,
|
|
582
|
+
hasAnalyzer: !!runtime.analyzer
|
|
583
|
+
}
|
|
584
|
+
]))
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
// Enhanced TypeScript analyzer that integrates with existing infrastructure
|
|
589
|
+
class TypeScriptAnalyzer {
|
|
590
|
+
name = 'typescript';
|
|
591
|
+
runtime = 'node';
|
|
592
|
+
command = 'node';
|
|
593
|
+
supports = ['.ts', '.tsx', '.js', '.jsx'];
|
|
594
|
+
async analyze(files, options) {
|
|
595
|
+
console.log(`[TypeScriptAnalyzer] Analyzing ${files.length} TypeScript files`);
|
|
596
|
+
const startTime = Date.now();
|
|
597
|
+
try {
|
|
598
|
+
// Import the existing TypeScript adapter and audit infrastructure
|
|
599
|
+
const { TypeScriptAdapter } = await import('./typescript/TypeScriptAdapter.js');
|
|
600
|
+
const { runAudit } = await import('../auditRunner.js');
|
|
601
|
+
// Run the existing audit process
|
|
602
|
+
const auditResult = await runAudit({
|
|
603
|
+
includePaths: files,
|
|
604
|
+
enabledAnalyzers: options?.analyzers || ['solid', 'dry', 'documentation', 'dataAccess'],
|
|
605
|
+
minSeverity: options?.minSeverity || 'warning',
|
|
606
|
+
verbose: false
|
|
607
|
+
});
|
|
608
|
+
// Extract index entries from the audit process
|
|
609
|
+
const indexEntries = [];
|
|
610
|
+
// Try to get functions from the audit metadata if available
|
|
611
|
+
if (auditResult.metadata?.collectedFunctions) {
|
|
612
|
+
for (const func of auditResult.metadata.collectedFunctions) {
|
|
613
|
+
indexEntries.push({
|
|
614
|
+
id: `typescript:function:${func.filePath}:${func.name}:${func.lineNumber || 0}`,
|
|
615
|
+
name: func.name,
|
|
616
|
+
type: 'function',
|
|
617
|
+
language: 'typescript',
|
|
618
|
+
file: func.filePath,
|
|
619
|
+
signature: func.name, // Basic signature
|
|
620
|
+
parameters: [],
|
|
621
|
+
purpose: func.purpose || '',
|
|
622
|
+
context: func.context || '',
|
|
623
|
+
startLine: func.startLine,
|
|
624
|
+
endLine: func.endLine,
|
|
625
|
+
lineNumber: func.lineNumber,
|
|
626
|
+
metadata: func.metadata
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
// Add component information if available
|
|
631
|
+
if (auditResult.metadata?.fileToFunctionsMap) {
|
|
632
|
+
for (const [filePath, funcs] of Object.entries(auditResult.metadata.fileToFunctionsMap)) {
|
|
633
|
+
for (const func of funcs) {
|
|
634
|
+
if (!indexEntries.find(e => e.id === `typescript:function:${func.filePath}:${func.name}:${func.lineNumber || 0}`)) {
|
|
635
|
+
indexEntries.push({
|
|
636
|
+
id: `typescript:function:${func.filePath}:${func.name}:${func.lineNumber || 0}`,
|
|
637
|
+
name: func.name,
|
|
638
|
+
type: 'function',
|
|
639
|
+
language: 'typescript',
|
|
640
|
+
file: func.filePath,
|
|
641
|
+
signature: func.name,
|
|
642
|
+
parameters: [],
|
|
643
|
+
purpose: func.purpose || '',
|
|
644
|
+
context: func.context || '',
|
|
645
|
+
startLine: func.startLine,
|
|
646
|
+
endLine: func.endLine,
|
|
647
|
+
lineNumber: func.lineNumber,
|
|
648
|
+
metadata: func.metadata
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
const executionTime = Date.now() - startTime;
|
|
655
|
+
// Extract violations from all analyzer results
|
|
656
|
+
const violations = [];
|
|
657
|
+
for (const [analyzerName, analyzerResult] of Object.entries(auditResult.analyzerResults)) {
|
|
658
|
+
violations.push(...analyzerResult.violations);
|
|
659
|
+
}
|
|
660
|
+
// Extract errors from analyzer results
|
|
661
|
+
const errors = [];
|
|
662
|
+
for (const [analyzerName, analyzerResult] of Object.entries(auditResult.analyzerResults)) {
|
|
663
|
+
if (analyzerResult.errors) {
|
|
664
|
+
errors.push(...analyzerResult.errors);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return {
|
|
668
|
+
violations,
|
|
669
|
+
indexEntries,
|
|
670
|
+
metrics: {
|
|
671
|
+
filesAnalyzed: files.length,
|
|
672
|
+
executionTime
|
|
673
|
+
},
|
|
674
|
+
errors
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
catch (error) {
|
|
678
|
+
const executionTime = Date.now() - startTime;
|
|
679
|
+
console.error(`[TypeScriptAnalyzer] Error during analysis:`, error);
|
|
680
|
+
return {
|
|
681
|
+
violations: [],
|
|
682
|
+
indexEntries: [],
|
|
683
|
+
metrics: {
|
|
684
|
+
filesAnalyzed: 0,
|
|
685
|
+
executionTime
|
|
686
|
+
},
|
|
687
|
+
errors: [{
|
|
688
|
+
message: error instanceof Error ? error.message : String(error),
|
|
689
|
+
type: 'analysis_error'
|
|
690
|
+
}]
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
class GoAnalyzer {
|
|
696
|
+
analyzerPath;
|
|
697
|
+
name = 'go';
|
|
698
|
+
runtime = 'go';
|
|
699
|
+
command;
|
|
700
|
+
supports = ['.go'];
|
|
701
|
+
constructor(analyzerPath) {
|
|
702
|
+
this.analyzerPath = analyzerPath;
|
|
703
|
+
this.command = analyzerPath || 'go';
|
|
704
|
+
}
|
|
705
|
+
async analyze(files, options) {
|
|
706
|
+
console.log(`[GoAnalyzer] Analyzing ${files.length} Go files`);
|
|
707
|
+
const startTime = Date.now();
|
|
708
|
+
try {
|
|
709
|
+
// Build the Go analyzer if needed
|
|
710
|
+
const goAnalyzerDir = path.join(__dirname, 'go');
|
|
711
|
+
await this.ensureGoAnalyzerBuilt(goAnalyzerDir);
|
|
712
|
+
// Prepare analysis options
|
|
713
|
+
const analysisOptions = {
|
|
714
|
+
analyzers: options?.analyzers || ['solid', 'imports', 'errors'],
|
|
715
|
+
minSeverity: options?.minSeverity || 'warning',
|
|
716
|
+
timeout: options?.timeout || 30000,
|
|
717
|
+
language: options?.language || 'go',
|
|
718
|
+
verbose: options?.verbose || false
|
|
719
|
+
};
|
|
720
|
+
// Run the Go analyzer via JSON-RPC
|
|
721
|
+
const result = await this.runGoAnalyzer(goAnalyzerDir, files, analysisOptions);
|
|
722
|
+
const executionTime = Date.now() - startTime;
|
|
723
|
+
result.metrics.executionTime = executionTime;
|
|
724
|
+
console.log(`[GoAnalyzer] Analysis complete: ${result.violations.length} violations, ${result.indexEntries.length} entities`);
|
|
725
|
+
return result;
|
|
726
|
+
}
|
|
727
|
+
catch (error) {
|
|
728
|
+
const executionTime = Date.now() - startTime;
|
|
729
|
+
console.error(`[GoAnalyzer] Error during analysis:`, error);
|
|
730
|
+
return {
|
|
731
|
+
violations: [],
|
|
732
|
+
indexEntries: [],
|
|
733
|
+
metrics: {
|
|
734
|
+
filesAnalyzed: 0,
|
|
735
|
+
executionTime
|
|
736
|
+
},
|
|
737
|
+
errors: [{
|
|
738
|
+
message: error instanceof Error ? error.message : String(error),
|
|
739
|
+
type: 'go_analyzer_error'
|
|
740
|
+
}]
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
async ensureGoAnalyzerBuilt(goDir) {
|
|
745
|
+
const binaryPath = path.join(goDir, 'analyzer');
|
|
746
|
+
const mainGoPath = path.join(goDir, 'main.go');
|
|
747
|
+
try {
|
|
748
|
+
// Check if binary exists
|
|
749
|
+
await fs.access(binaryPath);
|
|
750
|
+
console.log(`[GoAnalyzer] Binary already exists at ${binaryPath}`);
|
|
751
|
+
}
|
|
752
|
+
catch {
|
|
753
|
+
// Binary doesn't exist, build it
|
|
754
|
+
console.log(`[GoAnalyzer] Building Go analyzer binary...`);
|
|
755
|
+
try {
|
|
756
|
+
const { stdout, stderr } = await execAsync(`cd "${goDir}" && go build -o analyzer main.go`);
|
|
757
|
+
if (stderr) {
|
|
758
|
+
console.warn(`[GoAnalyzer] Build warnings: ${stderr}`);
|
|
759
|
+
}
|
|
760
|
+
console.log(`[GoAnalyzer] Successfully built Go analyzer binary`);
|
|
761
|
+
}
|
|
762
|
+
catch (buildError) {
|
|
763
|
+
throw new Error(`Failed to build Go analyzer: ${buildError}`);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
async runGoAnalyzer(goDir, files, options) {
|
|
768
|
+
const binaryPath = path.join(goDir, 'analyzer');
|
|
769
|
+
return new Promise((resolve, reject) => {
|
|
770
|
+
const child = spawn(binaryPath, [], {
|
|
771
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
772
|
+
cwd: goDir
|
|
773
|
+
});
|
|
774
|
+
let stdout = '';
|
|
775
|
+
let stderr = '';
|
|
776
|
+
child.stdout?.on('data', (data) => {
|
|
777
|
+
stdout += data.toString();
|
|
778
|
+
});
|
|
779
|
+
child.stderr?.on('data', (data) => {
|
|
780
|
+
stderr += data.toString();
|
|
781
|
+
});
|
|
782
|
+
child.on('close', (code) => {
|
|
783
|
+
if (code !== 0) {
|
|
784
|
+
reject(new Error(`Go analyzer exited with code ${code}: ${stderr}`));
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
try {
|
|
788
|
+
// Parse the last line as JSON response
|
|
789
|
+
const lines = stdout.trim().split('\n');
|
|
790
|
+
const lastLine = lines[lines.length - 1];
|
|
791
|
+
const response = JSON.parse(lastLine);
|
|
792
|
+
if (response.error) {
|
|
793
|
+
reject(new Error(`Go analyzer error: ${response.error.message}`));
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
resolve(response.result);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
catch (parseError) {
|
|
800
|
+
reject(new Error(`Failed to parse Go analyzer response: ${parseError}`));
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
child.on('error', (error) => {
|
|
804
|
+
reject(new Error(`Failed to spawn Go analyzer: ${error}`));
|
|
805
|
+
});
|
|
806
|
+
// Send JSON-RPC request
|
|
807
|
+
const request = {
|
|
808
|
+
method: 'analyze',
|
|
809
|
+
params: {
|
|
810
|
+
files,
|
|
811
|
+
options
|
|
812
|
+
},
|
|
813
|
+
id: 1
|
|
814
|
+
};
|
|
815
|
+
child.stdin?.write(JSON.stringify(request) + '\n');
|
|
816
|
+
child.stdin?.end();
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
class PythonAnalyzer {
|
|
821
|
+
name = 'python';
|
|
822
|
+
runtime = 'python';
|
|
823
|
+
command;
|
|
824
|
+
supports = ['.py'];
|
|
825
|
+
constructor(pythonCommand) {
|
|
826
|
+
this.command = pythonCommand;
|
|
827
|
+
}
|
|
828
|
+
async analyze(files, options) {
|
|
829
|
+
console.log(`[PythonAnalyzer] Analyzing ${files.length} Python files`);
|
|
830
|
+
// This will spawn the Python analyzer process
|
|
831
|
+
return {
|
|
832
|
+
violations: [],
|
|
833
|
+
indexEntries: [],
|
|
834
|
+
metrics: {
|
|
835
|
+
filesAnalyzed: files.length,
|
|
836
|
+
executionTime: 0
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
//# sourceMappingURL=RuntimeManager.js.map
|