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,339 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { readFile, readdir, stat } from '../../utils/fs-utils.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* JavaIntegrationAnalyzer
|
|
6
|
+
*
|
|
7
|
+
* Analisa código Java para encontrar integrações HTTP:
|
|
8
|
+
* - RestTemplate
|
|
9
|
+
* - WebClient
|
|
10
|
+
* - FeignClient
|
|
11
|
+
* - HttpClient (Java 11+)
|
|
12
|
+
* - OkHttp
|
|
13
|
+
* - Retrofit
|
|
14
|
+
*/
|
|
15
|
+
export class JavaIntegrationAnalyzer {
|
|
16
|
+
|
|
17
|
+
constructor(workspacePath) {
|
|
18
|
+
this.workspacePath = workspacePath;
|
|
19
|
+
this.integrations = [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Encontra todas as integrações no workspace
|
|
24
|
+
* @returns {Promise<Integration[]>}
|
|
25
|
+
*/
|
|
26
|
+
async findAll() {
|
|
27
|
+
this.integrations = [];
|
|
28
|
+
|
|
29
|
+
const srcPath = path.join(this.workspacePath, 'src/main/java');
|
|
30
|
+
await this.scanDirectory(srcPath);
|
|
31
|
+
|
|
32
|
+
return this.integrations;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Escaneia diretório recursivamente
|
|
37
|
+
*/
|
|
38
|
+
async scanDirectory(dir) {
|
|
39
|
+
try {
|
|
40
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
41
|
+
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
const fullPath = path.join(dir, entry.name);
|
|
44
|
+
|
|
45
|
+
if (entry.isDirectory()) {
|
|
46
|
+
await this.scanDirectory(fullPath);
|
|
47
|
+
} else if (entry.name.endsWith('.java')) {
|
|
48
|
+
await this.analyzeFile(fullPath);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// Diretório não existe ou erro de leitura
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Analisa um arquivo Java
|
|
58
|
+
*/
|
|
59
|
+
async analyzeFile(filePath) {
|
|
60
|
+
try {
|
|
61
|
+
const content = await readFile(filePath, 'utf8');
|
|
62
|
+
const relativePath = path.relative(this.workspacePath, filePath);
|
|
63
|
+
const lines = content.split('\n');
|
|
64
|
+
|
|
65
|
+
// Analisar cada tipo de integração
|
|
66
|
+
this.findRestTemplate(content, relativePath, lines);
|
|
67
|
+
this.findWebClient(content, relativePath, lines);
|
|
68
|
+
this.findFeignClient(content, relativePath, lines);
|
|
69
|
+
this.findHttpClient(content, relativePath, lines);
|
|
70
|
+
this.findKafka(content, relativePath, lines);
|
|
71
|
+
this.findRabbitMQ(content, relativePath, lines);
|
|
72
|
+
|
|
73
|
+
} catch (e) {
|
|
74
|
+
// Erro ao ler arquivo
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Encontra uso de RestTemplate
|
|
80
|
+
*/
|
|
81
|
+
findRestTemplate(content, filePath, lines) {
|
|
82
|
+
// Padrões de RestTemplate
|
|
83
|
+
const patterns = [
|
|
84
|
+
// Injeção
|
|
85
|
+
{ regex: /@Autowired\s*\n?\s*(?:private|protected|public)?\s*RestTemplate\s+\w+/g, type: 'injection' },
|
|
86
|
+
{ regex: /private\s+(?:final\s+)?RestTemplate\s+\w+/g, type: 'field' },
|
|
87
|
+
|
|
88
|
+
// Chamadas HTTP
|
|
89
|
+
{ regex: /restTemplate\.(getForObject|getForEntity|postForObject|postForEntity|exchange|put|delete)\s*\(/gi, type: 'call' },
|
|
90
|
+
|
|
91
|
+
// Criação
|
|
92
|
+
{ regex: /new\s+RestTemplate\s*\(/g, type: 'instantiation' },
|
|
93
|
+
{ regex: /RestTemplateBuilder/g, type: 'builder' }
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
for (const pattern of patterns) {
|
|
97
|
+
let match;
|
|
98
|
+
while ((match = pattern.regex.exec(content)) !== null) {
|
|
99
|
+
const line = this.getLineNumber(content, match.index);
|
|
100
|
+
const url = this.extractUrl(content, match.index);
|
|
101
|
+
const method = this.extractHttpMethod(match[0]);
|
|
102
|
+
|
|
103
|
+
this.integrations.push({
|
|
104
|
+
type: 'rest-template',
|
|
105
|
+
subType: pattern.type,
|
|
106
|
+
file: filePath,
|
|
107
|
+
line,
|
|
108
|
+
targetUrl: url,
|
|
109
|
+
method,
|
|
110
|
+
snippet: lines[line - 1]?.trim() || match[0]
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Encontra uso de WebClient (Spring WebFlux)
|
|
118
|
+
*/
|
|
119
|
+
findWebClient(content, filePath, lines) {
|
|
120
|
+
const patterns = [
|
|
121
|
+
{ regex: /WebClient\.builder\(\)/g, type: 'builder' },
|
|
122
|
+
{ regex: /WebClient\.create\s*\(/g, type: 'create' },
|
|
123
|
+
{ regex: /\.get\(\)\s*\.uri\s*\(/g, type: 'get' },
|
|
124
|
+
{ regex: /\.post\(\)\s*\.uri\s*\(/g, type: 'post' },
|
|
125
|
+
{ regex: /\.put\(\)\s*\.uri\s*\(/g, type: 'put' },
|
|
126
|
+
{ regex: /\.delete\(\)\s*\.uri\s*\(/g, type: 'delete' },
|
|
127
|
+
{ regex: /\.patch\(\)\s*\.uri\s*\(/g, type: 'patch' }
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
for (const pattern of patterns) {
|
|
131
|
+
let match;
|
|
132
|
+
while ((match = pattern.regex.exec(content)) !== null) {
|
|
133
|
+
const line = this.getLineNumber(content, match.index);
|
|
134
|
+
const url = this.extractUrl(content, match.index);
|
|
135
|
+
|
|
136
|
+
this.integrations.push({
|
|
137
|
+
type: 'web-client',
|
|
138
|
+
subType: pattern.type,
|
|
139
|
+
file: filePath,
|
|
140
|
+
line,
|
|
141
|
+
targetUrl: url,
|
|
142
|
+
method: pattern.type.toUpperCase(),
|
|
143
|
+
snippet: lines[line - 1]?.trim() || match[0]
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Encontra FeignClient
|
|
151
|
+
*/
|
|
152
|
+
findFeignClient(content, filePath, lines) {
|
|
153
|
+
// @FeignClient annotation
|
|
154
|
+
const feignRegex = /@FeignClient\s*\(\s*(?:name\s*=\s*)?["']?([^"'\s,)]+)["']?/g;
|
|
155
|
+
|
|
156
|
+
let match;
|
|
157
|
+
while ((match = feignRegex.exec(content)) !== null) {
|
|
158
|
+
const line = this.getLineNumber(content, match.index);
|
|
159
|
+
const serviceName = match[1];
|
|
160
|
+
|
|
161
|
+
// Extrair URL se especificada
|
|
162
|
+
const urlMatch = content.substring(match.index, match.index + 200)
|
|
163
|
+
.match(/url\s*=\s*["']([^"']+)["']/);
|
|
164
|
+
|
|
165
|
+
this.integrations.push({
|
|
166
|
+
type: 'feign-client',
|
|
167
|
+
subType: 'declaration',
|
|
168
|
+
file: filePath,
|
|
169
|
+
line,
|
|
170
|
+
targetUrl: urlMatch ? urlMatch[1] : null,
|
|
171
|
+
serviceName,
|
|
172
|
+
method: null,
|
|
173
|
+
snippet: lines[line - 1]?.trim() || match[0]
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Métodos do FeignClient
|
|
178
|
+
const methodPatterns = [
|
|
179
|
+
{ regex: /@GetMapping\s*\(?\s*["']?([^"'\s)]*)/g, method: 'GET' },
|
|
180
|
+
{ regex: /@PostMapping\s*\(?\s*["']?([^"'\s)]*)/g, method: 'POST' },
|
|
181
|
+
{ regex: /@PutMapping\s*\(?\s*["']?([^"'\s)]*)/g, method: 'PUT' },
|
|
182
|
+
{ regex: /@DeleteMapping\s*\(?\s*["']?([^"'\s)]*)/g, method: 'DELETE' },
|
|
183
|
+
{ regex: /@PatchMapping\s*\(?\s*["']?([^"'\s)]*)/g, method: 'PATCH' }
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
// Só procurar métodos se arquivo tem @FeignClient
|
|
187
|
+
if (content.includes('@FeignClient')) {
|
|
188
|
+
for (const pattern of methodPatterns) {
|
|
189
|
+
let methodMatch;
|
|
190
|
+
while ((methodMatch = pattern.regex.exec(content)) !== null) {
|
|
191
|
+
const line = this.getLineNumber(content, methodMatch.index);
|
|
192
|
+
|
|
193
|
+
this.integrations.push({
|
|
194
|
+
type: 'feign-client',
|
|
195
|
+
subType: 'method',
|
|
196
|
+
file: filePath,
|
|
197
|
+
line,
|
|
198
|
+
targetUrl: methodMatch[1] || '/',
|
|
199
|
+
method: pattern.method,
|
|
200
|
+
snippet: lines[line - 1]?.trim() || methodMatch[0]
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Encontra Java HttpClient (11+)
|
|
209
|
+
*/
|
|
210
|
+
findHttpClient(content, filePath, lines) {
|
|
211
|
+
const patterns = [
|
|
212
|
+
{ regex: /HttpClient\.newBuilder\(\)/g, type: 'builder' },
|
|
213
|
+
{ regex: /HttpClient\.newHttpClient\(\)/g, type: 'create' },
|
|
214
|
+
{ regex: /HttpRequest\.newBuilder\(\)/g, type: 'request' }
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
for (const pattern of patterns) {
|
|
218
|
+
let match;
|
|
219
|
+
while ((match = pattern.regex.exec(content)) !== null) {
|
|
220
|
+
const line = this.getLineNumber(content, match.index);
|
|
221
|
+
const url = this.extractUrl(content, match.index);
|
|
222
|
+
|
|
223
|
+
this.integrations.push({
|
|
224
|
+
type: 'http-client',
|
|
225
|
+
subType: pattern.type,
|
|
226
|
+
file: filePath,
|
|
227
|
+
line,
|
|
228
|
+
targetUrl: url,
|
|
229
|
+
method: null,
|
|
230
|
+
snippet: lines[line - 1]?.trim() || match[0]
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Encontra integrações Kafka
|
|
238
|
+
*/
|
|
239
|
+
findKafka(content, filePath, lines) {
|
|
240
|
+
const patterns = [
|
|
241
|
+
{ regex: /@KafkaListener\s*\(\s*topics\s*=\s*["']([^"']+)["']/g, type: 'consumer' },
|
|
242
|
+
{ regex: /kafkaTemplate\.send\s*\(\s*["']([^"']+)["']/gi, type: 'producer' },
|
|
243
|
+
{ regex: /@SendTo\s*\(\s*["']([^"']+)["']\s*\)/g, type: 'reply' }
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
for (const pattern of patterns) {
|
|
247
|
+
let match;
|
|
248
|
+
while ((match = pattern.regex.exec(content)) !== null) {
|
|
249
|
+
const line = this.getLineNumber(content, match.index);
|
|
250
|
+
|
|
251
|
+
this.integrations.push({
|
|
252
|
+
type: 'kafka',
|
|
253
|
+
subType: pattern.type,
|
|
254
|
+
file: filePath,
|
|
255
|
+
line,
|
|
256
|
+
topic: match[1],
|
|
257
|
+
snippet: lines[line - 1]?.trim() || match[0]
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Encontra integrações RabbitMQ
|
|
265
|
+
*/
|
|
266
|
+
findRabbitMQ(content, filePath, lines) {
|
|
267
|
+
const patterns = [
|
|
268
|
+
{ regex: /@RabbitListener\s*\(\s*queues\s*=\s*["']([^"']+)["']/g, type: 'consumer' },
|
|
269
|
+
{ regex: /rabbitTemplate\.convertAndSend\s*\(/gi, type: 'producer' },
|
|
270
|
+
{ regex: /@RabbitHandler/g, type: 'handler' }
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
for (const pattern of patterns) {
|
|
274
|
+
let match;
|
|
275
|
+
while ((match = pattern.regex.exec(content)) !== null) {
|
|
276
|
+
const line = this.getLineNumber(content, match.index);
|
|
277
|
+
|
|
278
|
+
this.integrations.push({
|
|
279
|
+
type: 'rabbitmq',
|
|
280
|
+
subType: pattern.type,
|
|
281
|
+
file: filePath,
|
|
282
|
+
line,
|
|
283
|
+
queue: match[1] || null,
|
|
284
|
+
snippet: lines[line - 1]?.trim() || match[0]
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ==========================================
|
|
291
|
+
// HELPERS
|
|
292
|
+
// ==========================================
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Obtém número da linha a partir do índice
|
|
296
|
+
*/
|
|
297
|
+
getLineNumber(content, index) {
|
|
298
|
+
return content.substring(0, index).split('\n').length;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Tenta extrair URL do contexto
|
|
303
|
+
*/
|
|
304
|
+
extractUrl(content, startIndex) {
|
|
305
|
+
// Procurar URL nos próximos 500 caracteres
|
|
306
|
+
const context = content.substring(startIndex, startIndex + 500);
|
|
307
|
+
|
|
308
|
+
// Padrões de URL
|
|
309
|
+
const urlPatterns = [
|
|
310
|
+
/"(https?:\/\/[^"]+)"/,
|
|
311
|
+
/'(https?:\/\/[^']+)'/,
|
|
312
|
+
/\$\{([^}]+)\}/, // Property placeholder
|
|
313
|
+
/"([^"]*\/api\/[^"]+)"/,
|
|
314
|
+
/"(\/[a-zA-Z][a-zA-Z0-9/\-_{}]+)"/
|
|
315
|
+
];
|
|
316
|
+
|
|
317
|
+
for (const pattern of urlPatterns) {
|
|
318
|
+
const match = context.match(pattern);
|
|
319
|
+
if (match) return match[1];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Extrai método HTTP da chamada
|
|
327
|
+
*/
|
|
328
|
+
extractHttpMethod(snippet) {
|
|
329
|
+
if (/getFor|\.get\(/i.test(snippet)) return 'GET';
|
|
330
|
+
if (/postFor|\.post\(/i.test(snippet)) return 'POST';
|
|
331
|
+
if (/\.put\(/i.test(snippet)) return 'PUT';
|
|
332
|
+
if (/\.delete\(/i.test(snippet)) return 'DELETE';
|
|
333
|
+
if (/\.patch\(/i.test(snippet)) return 'PATCH';
|
|
334
|
+
if (/exchange/i.test(snippet)) return 'EXCHANGE';
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export default JavaIntegrationAnalyzer;
|