deepdebug-local-agent 0.3.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/.dockerignore +24 -0
- package/.idea/deepdebug-local-agent.iml +12 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/Dockerfile +46 -0
- package/cloudbuild.yaml +42 -0
- package/index.js +42 -0
- package/mcp-server.js +533 -0
- package/package.json +22 -0
- package/src/ai-engine.js +861 -0
- package/src/analyzers/config-analyzer.js +446 -0
- package/src/analyzers/controller-analyzer.js +429 -0
- package/src/analyzers/dto-analyzer.js +455 -0
- package/src/detectors/build-tool-detector.js +0 -0
- package/src/detectors/framework-detector.js +91 -0
- package/src/detectors/language-detector.js +89 -0
- package/src/detectors/multi-project-detector.js +191 -0
- package/src/detectors/service-detector.js +244 -0
- package/src/detectors.js +30 -0
- package/src/exec-utils.js +215 -0
- package/src/fs-utils.js +34 -0
- package/src/git/base-git-provider.js +384 -0
- package/src/git/git-provider-registry.js +110 -0
- package/src/git/github-provider.js +502 -0
- package/src/mcp-http-server.js +313 -0
- package/src/patch/patch-engine.js +339 -0
- package/src/patch-manager.js +816 -0
- package/src/patch.js +607 -0
- package/src/patch_bkp.js +154 -0
- package/src/ports.js +69 -0
- package/src/routes/workspace.route.js +528 -0
- package/src/runtimes/base-runtime.js +290 -0
- package/src/runtimes/java/gradle-runtime.js +378 -0
- package/src/runtimes/java/java-integrations.js +339 -0
- package/src/runtimes/java/maven-runtime.js +418 -0
- package/src/runtimes/node/node-integrations.js +247 -0
- package/src/runtimes/node/npm-runtime.js +466 -0
- package/src/runtimes/node/yarn-runtime.js +354 -0
- package/src/runtimes/runtime-registry.js +256 -0
- package/src/server-local.js +576 -0
- package/src/server.js +4565 -0
- package/src/utils/environment-diagnostics.js +666 -0
- package/src/utils/exec-utils.js +264 -0
- package/src/utils/fs-utils.js +218 -0
- package/src/workspace/detect-port.js +176 -0
- package/src/workspace/file-reader.js +54 -0
- package/src/workspace/git-client.js +0 -0
- package/src/workspace/process-manager.js +619 -0
- package/src/workspace/scanner.js +72 -0
- package/src/workspace-manager.js +172 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { BaseRuntime } from '../base-runtime.js';
|
|
3
|
+
import { exists, readFile } from '../../utils/fs-utils.js';
|
|
4
|
+
import { run } from '../../utils/exec-utils.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* YarnRuntime
|
|
8
|
+
*
|
|
9
|
+
* Runtime para projetos Node.js com Yarn.
|
|
10
|
+
* Suporta Express, NestJS, Next.js, React, Vue, etc.
|
|
11
|
+
*/
|
|
12
|
+
export class YarnRuntime extends BaseRuntime {
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
super();
|
|
16
|
+
this.framework = null;
|
|
17
|
+
this.packageJson = null;
|
|
18
|
+
this.yarnVersion = 1; // 1 ou 2+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ==========================================
|
|
22
|
+
// IDENTIFICATION
|
|
23
|
+
// ==========================================
|
|
24
|
+
|
|
25
|
+
getId() {
|
|
26
|
+
return 'node-yarn';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getName() {
|
|
30
|
+
return 'Node.js (Yarn)';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getLanguage() {
|
|
34
|
+
return 'node';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getBuildTool() {
|
|
38
|
+
return 'yarn';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ==========================================
|
|
42
|
+
// DETECTION
|
|
43
|
+
// ==========================================
|
|
44
|
+
|
|
45
|
+
getMarkerFiles() {
|
|
46
|
+
return ['yarn.lock', 'package.json'];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async isCompatible(workspacePath) {
|
|
50
|
+
const yarnLock = path.join(workspacePath, 'yarn.lock');
|
|
51
|
+
return exists(yarnLock);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async detectProject(workspacePath) {
|
|
55
|
+
const pkgPath = path.join(workspacePath, 'package.json');
|
|
56
|
+
const content = await readFile(pkgPath, 'utf8');
|
|
57
|
+
this.packageJson = JSON.parse(content);
|
|
58
|
+
|
|
59
|
+
// Detectar versão do Yarn
|
|
60
|
+
const yarnrcPath = path.join(workspacePath, '.yarnrc.yml');
|
|
61
|
+
if (await exists(yarnrcPath)) {
|
|
62
|
+
this.yarnVersion = 2;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const name = this.packageJson.name || path.basename(workspacePath);
|
|
66
|
+
const version = this.packageJson.version || '0.0.1';
|
|
67
|
+
|
|
68
|
+
const framework = this.detectFramework(this.packageJson);
|
|
69
|
+
this.framework = framework;
|
|
70
|
+
|
|
71
|
+
const port = await this.detectPort(workspacePath);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
name,
|
|
75
|
+
version,
|
|
76
|
+
language: 'node',
|
|
77
|
+
framework,
|
|
78
|
+
buildTool: 'yarn',
|
|
79
|
+
port
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
detectFramework(pkg) {
|
|
84
|
+
const deps = {
|
|
85
|
+
...pkg.dependencies,
|
|
86
|
+
...pkg.devDependencies
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if (deps['next']) return 'next';
|
|
90
|
+
if (deps['nuxt']) return 'nuxt';
|
|
91
|
+
if (deps['@angular/core']) return 'angular';
|
|
92
|
+
if (deps['vue']) return deps['nuxt'] ? 'nuxt' : 'vue';
|
|
93
|
+
if (deps['react']) return deps['next'] ? 'next' : 'react';
|
|
94
|
+
if (deps['svelte']) return 'svelte';
|
|
95
|
+
if (deps['@nestjs/core']) return 'nestjs';
|
|
96
|
+
if (deps['express']) return 'express';
|
|
97
|
+
if (deps['fastify']) return 'fastify';
|
|
98
|
+
if (deps['koa']) return 'koa';
|
|
99
|
+
|
|
100
|
+
return 'node-plain';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ==========================================
|
|
104
|
+
// BUILD & RUN
|
|
105
|
+
// ==========================================
|
|
106
|
+
|
|
107
|
+
async install(workspacePath, options = {}) {
|
|
108
|
+
const startTime = Date.now();
|
|
109
|
+
const args = this.yarnVersion >= 2 ? ['install'] : [];
|
|
110
|
+
|
|
111
|
+
if (options.production && this.yarnVersion < 2) {
|
|
112
|
+
args.push('--production');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const result = await run('yarn', args, workspacePath);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
...result,
|
|
119
|
+
duration: Date.now() - startTime
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async build(workspacePath, options = {}) {
|
|
124
|
+
const startTime = Date.now();
|
|
125
|
+
|
|
126
|
+
if (!this.packageJson?.scripts?.build) {
|
|
127
|
+
return {
|
|
128
|
+
code: 0,
|
|
129
|
+
stdout: 'No build script defined',
|
|
130
|
+
stderr: '',
|
|
131
|
+
duration: Date.now() - startTime
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const result = await run('yarn', ['build'], workspacePath);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
...result,
|
|
139
|
+
duration: Date.now() - startTime
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getStartCommand(options = {}) {
|
|
144
|
+
const framework = options.framework || this.framework || 'node-plain';
|
|
145
|
+
const port = options.port || 3000;
|
|
146
|
+
|
|
147
|
+
const commands = {
|
|
148
|
+
'next': {
|
|
149
|
+
cmd: 'yarn',
|
|
150
|
+
args: ['dev'],
|
|
151
|
+
env: { PORT: String(port) }
|
|
152
|
+
},
|
|
153
|
+
'nuxt': {
|
|
154
|
+
cmd: 'yarn',
|
|
155
|
+
args: ['dev'],
|
|
156
|
+
env: { PORT: String(port) }
|
|
157
|
+
},
|
|
158
|
+
'nestjs': {
|
|
159
|
+
cmd: 'yarn',
|
|
160
|
+
args: ['start:dev'],
|
|
161
|
+
env: { PORT: String(port) }
|
|
162
|
+
},
|
|
163
|
+
'express': {
|
|
164
|
+
cmd: 'yarn',
|
|
165
|
+
args: ['start'],
|
|
166
|
+
env: { PORT: String(port) }
|
|
167
|
+
},
|
|
168
|
+
'react': {
|
|
169
|
+
cmd: 'yarn',
|
|
170
|
+
args: ['start'],
|
|
171
|
+
env: { PORT: String(port) }
|
|
172
|
+
},
|
|
173
|
+
'vue': {
|
|
174
|
+
cmd: 'yarn',
|
|
175
|
+
args: ['serve'],
|
|
176
|
+
env: { PORT: String(port) }
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return commands[framework] || {
|
|
181
|
+
cmd: 'yarn',
|
|
182
|
+
args: ['start'],
|
|
183
|
+
env: { PORT: String(port) }
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
getReadyPattern() {
|
|
188
|
+
const patterns = {
|
|
189
|
+
'next': /ready.*started server on/i,
|
|
190
|
+
'nuxt': /Listening on/i,
|
|
191
|
+
'nestjs': /Nest application successfully started/i,
|
|
192
|
+
'express': /listening on port|server (is )?running/i,
|
|
193
|
+
'react': /compiled successfully|You can now view/i,
|
|
194
|
+
'vue': /App running at/i
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
return patterns[this.framework] || /listening|started|ready|running/i;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
getDefaultPort() {
|
|
201
|
+
const defaults = {
|
|
202
|
+
'next': 3000,
|
|
203
|
+
'nuxt': 3000,
|
|
204
|
+
'nestjs': 3000,
|
|
205
|
+
'express': 3000,
|
|
206
|
+
'react': 3000,
|
|
207
|
+
'vue': 8080
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
return defaults[this.framework] || 3000;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async detectPort(workspacePath) {
|
|
214
|
+
const envPath = path.join(workspacePath, '.env');
|
|
215
|
+
if (await exists(envPath)) {
|
|
216
|
+
const content = await readFile(envPath, 'utf8');
|
|
217
|
+
const match = content.match(/^PORT\s*=\s*(\d+)/m);
|
|
218
|
+
if (match) return parseInt(match[1]);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return this.getDefaultPort();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ==========================================
|
|
225
|
+
// TEST
|
|
226
|
+
// ==========================================
|
|
227
|
+
|
|
228
|
+
async test(workspacePath, options = {}) {
|
|
229
|
+
const startTime = Date.now();
|
|
230
|
+
|
|
231
|
+
if (!this.packageJson?.scripts?.test) {
|
|
232
|
+
return {
|
|
233
|
+
success: true,
|
|
234
|
+
passed: 0,
|
|
235
|
+
failed: 0,
|
|
236
|
+
skipped: 0,
|
|
237
|
+
duration: Date.now() - startTime,
|
|
238
|
+
output: 'No test script defined',
|
|
239
|
+
failures: []
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const result = await run('yarn', ['test', '--passWithNoTests'], workspacePath);
|
|
244
|
+
|
|
245
|
+
const testResult = this.parseTestOutput(result);
|
|
246
|
+
testResult.duration = Date.now() - startTime;
|
|
247
|
+
|
|
248
|
+
return testResult;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async testSingle(workspacePath, testFile) {
|
|
252
|
+
const startTime = Date.now();
|
|
253
|
+
|
|
254
|
+
const result = await run('yarn', ['test', testFile], workspacePath);
|
|
255
|
+
|
|
256
|
+
const testResult = this.parseTestOutput(result);
|
|
257
|
+
testResult.duration = Date.now() - startTime;
|
|
258
|
+
|
|
259
|
+
return testResult;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
parseTestOutput(result) {
|
|
263
|
+
const output = result.stdout + result.stderr;
|
|
264
|
+
|
|
265
|
+
const jestMatch = output.match(/Tests:\s+(\d+)\s+passed,?\s*(\d+)?\s*failed?,?\s*(\d+)?\s*skipped?/i);
|
|
266
|
+
if (jestMatch) {
|
|
267
|
+
return {
|
|
268
|
+
success: result.code === 0,
|
|
269
|
+
passed: parseInt(jestMatch[1]) || 0,
|
|
270
|
+
failed: parseInt(jestMatch[2]) || 0,
|
|
271
|
+
skipped: parseInt(jestMatch[3]) || 0,
|
|
272
|
+
duration: 0,
|
|
273
|
+
output,
|
|
274
|
+
failures: []
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
success: result.code === 0,
|
|
280
|
+
passed: result.code === 0 ? 1 : 0,
|
|
281
|
+
failed: result.code === 0 ? 0 : 1,
|
|
282
|
+
skipped: 0,
|
|
283
|
+
duration: 0,
|
|
284
|
+
output,
|
|
285
|
+
failures: []
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ==========================================
|
|
290
|
+
// CODE ANALYSIS
|
|
291
|
+
// ==========================================
|
|
292
|
+
|
|
293
|
+
getFileExtensions() {
|
|
294
|
+
return ['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs'];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async findIntegrations(workspacePath) {
|
|
298
|
+
const { NodeIntegrationAnalyzer } = await import('./node-integrations.js');
|
|
299
|
+
const analyzer = new NodeIntegrationAnalyzer(workspacePath);
|
|
300
|
+
return analyzer.findAll();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async analyzeImports(filePath, content) {
|
|
304
|
+
const imports = [];
|
|
305
|
+
|
|
306
|
+
const es6Regex = /import\s+(?:{[^}]+}|\*\s+as\s+\w+|\w+)\s+from\s+['"]([^'"]+)['"]/g;
|
|
307
|
+
let match;
|
|
308
|
+
while ((match = es6Regex.exec(content)) !== null) {
|
|
309
|
+
imports.push({
|
|
310
|
+
name: match[1],
|
|
311
|
+
type: 'import',
|
|
312
|
+
isExternal: !match[1].startsWith('.') && !match[1].startsWith('/')
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const cjsRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
317
|
+
while ((match = cjsRegex.exec(content)) !== null) {
|
|
318
|
+
imports.push({
|
|
319
|
+
name: match[1],
|
|
320
|
+
type: 'require',
|
|
321
|
+
isExternal: !match[1].startsWith('.') && !match[1].startsWith('/')
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return imports;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ==========================================
|
|
329
|
+
// PATCH
|
|
330
|
+
// ==========================================
|
|
331
|
+
|
|
332
|
+
async validatePatch(workspacePath, diff) {
|
|
333
|
+
const errors = [];
|
|
334
|
+
|
|
335
|
+
const fileRegex = /^[\+\-]{3}\s+[ab]\/(.+)$/gm;
|
|
336
|
+
let match;
|
|
337
|
+
|
|
338
|
+
while ((match = fileRegex.exec(diff)) !== null) {
|
|
339
|
+
const filePath = path.join(workspacePath, match[1]);
|
|
340
|
+
if (match[0].startsWith('---') && !(await exists(filePath))) {
|
|
341
|
+
if (!diff.includes('+++ /dev/null')) {
|
|
342
|
+
errors.push(`File not found: ${match[1]}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
valid: errors.length === 0,
|
|
349
|
+
errors
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export default YarnRuntime;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { exists } from '../utils/fs-utils.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* RuntimeRegistry
|
|
6
|
+
*
|
|
7
|
+
* Factory/Registry para gerenciar e detectar runtimes de linguagem.
|
|
8
|
+
* Singleton que mantém todos os runtimes registrados.
|
|
9
|
+
*/
|
|
10
|
+
export class RuntimeRegistry {
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
this.runtimes = new Map();
|
|
14
|
+
this.markerIndex = new Map(); // marker file -> runtime id
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Registra um runtime
|
|
19
|
+
* @param {BaseRuntime} runtime - Instância do runtime
|
|
20
|
+
*/
|
|
21
|
+
register(runtime) {
|
|
22
|
+
const id = runtime.getId();
|
|
23
|
+
this.runtimes.set(id, runtime);
|
|
24
|
+
|
|
25
|
+
// Indexar por marker files para detecção rápida
|
|
26
|
+
for (const marker of runtime.getMarkerFiles()) {
|
|
27
|
+
if (!this.markerIndex.has(marker)) {
|
|
28
|
+
this.markerIndex.set(marker, []);
|
|
29
|
+
}
|
|
30
|
+
this.markerIndex.get(marker).push(id);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log(`📦 Registered runtime: ${id}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Obtém um runtime pelo ID
|
|
38
|
+
* @param {string} id - ID do runtime
|
|
39
|
+
* @returns {BaseRuntime|null}
|
|
40
|
+
*/
|
|
41
|
+
get(id) {
|
|
42
|
+
return this.runtimes.get(id) || null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Lista todos os runtimes registrados
|
|
47
|
+
* @returns {BaseRuntime[]}
|
|
48
|
+
*/
|
|
49
|
+
list() {
|
|
50
|
+
return Array.from(this.runtimes.values());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Lista IDs dos runtimes registrados
|
|
55
|
+
* @returns {string[]}
|
|
56
|
+
*/
|
|
57
|
+
listIds() {
|
|
58
|
+
return Array.from(this.runtimes.keys());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Auto-detecta o runtime de um workspace
|
|
63
|
+
* @param {string} workspacePath - Caminho do workspace
|
|
64
|
+
* @returns {Promise<BaseRuntime|null>}
|
|
65
|
+
*/
|
|
66
|
+
async detectRuntime(workspacePath) {
|
|
67
|
+
console.log(`🔍 Detecting runtime for: ${workspacePath}`);
|
|
68
|
+
|
|
69
|
+
// Prioridade de detecção (mais específico primeiro)
|
|
70
|
+
const detectionOrder = [
|
|
71
|
+
// Java
|
|
72
|
+
'pom.xml',
|
|
73
|
+
'build.gradle',
|
|
74
|
+
'build.gradle.kts',
|
|
75
|
+
// Node
|
|
76
|
+
'package.json',
|
|
77
|
+
// Python
|
|
78
|
+
'pyproject.toml',
|
|
79
|
+
'requirements.txt',
|
|
80
|
+
'setup.py',
|
|
81
|
+
// .NET
|
|
82
|
+
'*.csproj',
|
|
83
|
+
'*.sln',
|
|
84
|
+
// Go
|
|
85
|
+
'go.mod',
|
|
86
|
+
// SAP
|
|
87
|
+
'mta.yaml',
|
|
88
|
+
'.cdsrc.json',
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
for (const marker of detectionOrder) {
|
|
92
|
+
const candidates = this.markerIndex.get(marker) || [];
|
|
93
|
+
|
|
94
|
+
for (const runtimeId of candidates) {
|
|
95
|
+
const runtime = this.runtimes.get(runtimeId);
|
|
96
|
+
|
|
97
|
+
if (await runtime.isCompatible(workspacePath)) {
|
|
98
|
+
console.log(`✅ Detected runtime: ${runtimeId}`);
|
|
99
|
+
return runtime;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Fallback: tentar cada runtime
|
|
105
|
+
for (const runtime of this.runtimes.values()) {
|
|
106
|
+
if (await runtime.isCompatible(workspacePath)) {
|
|
107
|
+
console.log(`✅ Detected runtime (fallback): ${runtime.getId()}`);
|
|
108
|
+
return runtime;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log(`⚠️ No runtime detected for: ${workspacePath}`);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Detecta runtime e retorna informações do projeto
|
|
118
|
+
* @param {string} workspacePath - Caminho do workspace
|
|
119
|
+
* @returns {Promise<{runtime: BaseRuntime, project: ProjectInfo}|null>}
|
|
120
|
+
*/
|
|
121
|
+
async analyzeWorkspace(workspacePath) {
|
|
122
|
+
const runtime = await this.detectRuntime(workspacePath);
|
|
123
|
+
|
|
124
|
+
if (!runtime) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const project = await runtime.detectProject(workspacePath);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
runtime,
|
|
132
|
+
project
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Obtém runtime por linguagem e build tool
|
|
138
|
+
* @param {string} language - Linguagem
|
|
139
|
+
* @param {string} buildTool - Build tool
|
|
140
|
+
* @returns {BaseRuntime|null}
|
|
141
|
+
*/
|
|
142
|
+
getByLanguageAndTool(language, buildTool) {
|
|
143
|
+
const id = `${language}-${buildTool}`;
|
|
144
|
+
|
|
145
|
+
// Tentar match exato
|
|
146
|
+
if (this.runtimes.has(id)) {
|
|
147
|
+
return this.runtimes.get(id);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Tentar match por linguagem
|
|
151
|
+
for (const runtime of this.runtimes.values()) {
|
|
152
|
+
if (runtime.getLanguage() === language &&
|
|
153
|
+
runtime.getBuildTool() === buildTool) {
|
|
154
|
+
return runtime;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Fallback: qualquer runtime da linguagem
|
|
159
|
+
for (const runtime of this.runtimes.values()) {
|
|
160
|
+
if (runtime.getLanguage() === language) {
|
|
161
|
+
return runtime;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Lista runtimes por linguagem
|
|
170
|
+
* @param {string} language - Linguagem
|
|
171
|
+
* @returns {BaseRuntime[]}
|
|
172
|
+
*/
|
|
173
|
+
listByLanguage(language) {
|
|
174
|
+
return Array.from(this.runtimes.values())
|
|
175
|
+
.filter(r => r.getLanguage() === language);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ==========================================
|
|
180
|
+
// SINGLETON INSTANCE
|
|
181
|
+
// ==========================================
|
|
182
|
+
|
|
183
|
+
let instance = null;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Obtém a instância singleton do RuntimeRegistry
|
|
187
|
+
* @returns {RuntimeRegistry}
|
|
188
|
+
*/
|
|
189
|
+
export function getRuntimeRegistry() {
|
|
190
|
+
if (!instance) {
|
|
191
|
+
instance = new RuntimeRegistry();
|
|
192
|
+
}
|
|
193
|
+
return instance;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Inicializa o registry com todos os runtimes disponíveis
|
|
198
|
+
* @returns {Promise<RuntimeRegistry>}
|
|
199
|
+
*/
|
|
200
|
+
export async function initializeRuntimes() {
|
|
201
|
+
const registry = getRuntimeRegistry();
|
|
202
|
+
|
|
203
|
+
// Import dinâmico dos runtimes
|
|
204
|
+
try {
|
|
205
|
+
// Java
|
|
206
|
+
const { MavenRuntime } = await import('./java/maven-runtime.js');
|
|
207
|
+
const { GradleRuntime } = await import('./java/gradle-runtime.js');
|
|
208
|
+
registry.register(new MavenRuntime());
|
|
209
|
+
registry.register(new GradleRuntime());
|
|
210
|
+
} catch (e) {
|
|
211
|
+
console.warn('⚠️ Java runtimes not available:', e.message);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
// Node.js
|
|
216
|
+
const { NpmRuntime } = await import('./node/npm-runtime.js');
|
|
217
|
+
const { YarnRuntime } = await import('./node/yarn-runtime.js');
|
|
218
|
+
registry.register(new NpmRuntime());
|
|
219
|
+
registry.register(new YarnRuntime());
|
|
220
|
+
} catch (e) {
|
|
221
|
+
console.warn('⚠️ Node runtimes not available:', e.message);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
// Python
|
|
226
|
+
const { PipRuntime } = await import('./python/pip-runtime.js');
|
|
227
|
+
const { PoetryRuntime } = await import('./python/poetry-runtime.js');
|
|
228
|
+
registry.register(new PipRuntime());
|
|
229
|
+
registry.register(new PoetryRuntime());
|
|
230
|
+
} catch (e) {
|
|
231
|
+
console.warn('⚠️ Python runtimes not available:', e.message);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
// .NET
|
|
236
|
+
const { DotNetRuntime } = await import('./dotnet/dotnet-runtime.js');
|
|
237
|
+
registry.register(new DotNetRuntime());
|
|
238
|
+
} catch (e) {
|
|
239
|
+
console.warn('⚠️ .NET runtime not available:', e.message);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
// Go
|
|
244
|
+
const { GoRuntime } = await import('./go/go-runtime.js');
|
|
245
|
+
registry.register(new GoRuntime());
|
|
246
|
+
} catch (e) {
|
|
247
|
+
console.warn('⚠️ Go runtime not available:', e.message);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
console.log(`📦 RuntimeRegistry initialized with ${registry.listIds().length} runtimes`);
|
|
251
|
+
console.log(` Available: ${registry.listIds().join(', ')}`);
|
|
252
|
+
|
|
253
|
+
return registry;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export default RuntimeRegistry;
|