deepdebug-local-agent 0.3.18 → 1.0.2
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 +38 -21
- package/.github/workflows/npm-publish.yml +76 -0
- package/Dockerfile +55 -27
- package/build.sh +123 -0
- package/docker-compose.yml +104 -0
- package/docs/ENTERPRISE_DEPLOYMENT_GUIDE.md +462 -0
- package/docs/QUICKSTART.md +193 -0
- package/docs/SECURITY_WHITEPAPER.md +249 -0
- package/env.example +41 -0
- package/helm/Chart.yaml +17 -0
- package/helm/templates/_helpers.tpl +60 -0
- package/helm/templates/deployment.yaml +95 -0
- package/helm/templates/secret.yaml +9 -0
- package/helm/templates/service.yaml +18 -0
- package/helm/values.yaml +162 -0
- package/package.json +55 -20
- package/src/mcp-http-server.js +3 -99
- package/src/runtimes/base-runtime.js +1 -1
- package/src/runtimes/java/java-integrations.js +1 -1
- package/src/runtimes/node/node-integrations.js +1 -1
- package/src/server.js +81 -10
- package/src/workspace/detect-port.js +1 -0
- package/.idea/deepdebug-local-agent.iml +0 -12
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
- /package/{cloudbuild.yaml → cloudbuild.yaml.deprecated} +0 -0
package/package.json
CHANGED
|
@@ -1,30 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepdebug-local-agent",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "DeepDebug Local Agent - AI-powered code debugging assistant",
|
|
5
|
+
"private": false,
|
|
5
6
|
"type": "module",
|
|
6
7
|
"main": "src/server.js",
|
|
7
8
|
"bin": {
|
|
8
|
-
"deepdebug-
|
|
9
|
+
"deepdebug-agent": "src/server.js"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
12
|
"start": "node src/server.js",
|
|
12
13
|
"dev": "NODE_ENV=development node src/server.js",
|
|
13
|
-
"mcp": "node src/mcp-server.js"
|
|
14
|
+
"mcp": "node src/mcp-server.js",
|
|
15
|
+
"build": "npm run build:all",
|
|
16
|
+
"build:all": "npm run build:win && npm run build:mac && npm run build:linux",
|
|
17
|
+
"build:win": "pkg . --target node20-win-x64 --output dist/deepdebug-agent-win.exe",
|
|
18
|
+
"build:mac": "pkg . --target node20-macos-x64 --output dist/deepdebug-agent-macos",
|
|
19
|
+
"build:mac-arm": "pkg . --target node20-macos-arm64 --output dist/deepdebug-agent-macos-arm64",
|
|
20
|
+
"build:linux": "pkg . --target node20-linux-x64 --output dist/deepdebug-agent-linux",
|
|
21
|
+
"docker:build": "docker build -t deepdebug/local-agent:latest .",
|
|
22
|
+
"docker:push": "docker push deepdebug/local-agent:latest",
|
|
23
|
+
"test": "node --test",
|
|
24
|
+
"lint": "eslint src/"
|
|
14
25
|
},
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
"pkg": {
|
|
27
|
+
"scripts": [
|
|
28
|
+
"src/**/*.js"
|
|
29
|
+
],
|
|
30
|
+
"assets": [
|
|
31
|
+
"src/**/*"
|
|
32
|
+
],
|
|
33
|
+
"targets": [
|
|
34
|
+
"node20-win-x64",
|
|
35
|
+
"node20-macos-x64",
|
|
36
|
+
"node20-macos-arm64",
|
|
37
|
+
"node20-linux-x64"
|
|
38
|
+
],
|
|
39
|
+
"outputPath": "dist"
|
|
28
40
|
},
|
|
29
41
|
"dependencies": {
|
|
30
42
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
@@ -35,7 +47,30 @@
|
|
|
35
47
|
"pidusage": "^3.0.2",
|
|
36
48
|
"properties-reader": "^2.3.0",
|
|
37
49
|
"strip-ansi": "^7.1.0",
|
|
38
|
-
"unidiff": "^1.0.2"
|
|
39
|
-
|
|
40
|
-
|
|
50
|
+
"unidiff": "^1.0.2"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"pkg": "^5.8.1",
|
|
54
|
+
"eslint": "^8.57.0"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=18.0.0"
|
|
58
|
+
},
|
|
59
|
+
"os": [
|
|
60
|
+
"darwin",
|
|
61
|
+
"linux",
|
|
62
|
+
"win32"
|
|
63
|
+
],
|
|
64
|
+
"cpu": [
|
|
65
|
+
"x64",
|
|
66
|
+
"arm64"
|
|
67
|
+
],
|
|
68
|
+
"keywords": [
|
|
69
|
+
"debugging",
|
|
70
|
+
"ai",
|
|
71
|
+
"code-analysis",
|
|
72
|
+
"developer-tools"
|
|
73
|
+
],
|
|
74
|
+
"author": "InspTech AI",
|
|
75
|
+
"license": "Proprietary"
|
|
41
76
|
}
|
package/src/mcp-http-server.js
CHANGED
|
@@ -161,7 +161,7 @@ export function startMCPHttpServer(workspaceManager, port = 5056) {
|
|
|
161
161
|
|
|
162
162
|
try {
|
|
163
163
|
const root = workspaceManager.resolveRoot(workspaceId);
|
|
164
|
-
const results = await
|
|
164
|
+
const results = await grepWorkspace(root, query, filePattern, maxResults);
|
|
165
165
|
res.json({ ok: true, query, matches: results.length, results });
|
|
166
166
|
} catch (e) {
|
|
167
167
|
res.status(400).json({ error: e.message });
|
|
@@ -183,7 +183,7 @@ export function startMCPHttpServer(workspaceManager, port = 5056) {
|
|
|
183
183
|
|
|
184
184
|
try {
|
|
185
185
|
const root = workspaceManager.resolveRoot(workspaceId);
|
|
186
|
-
const result = await
|
|
186
|
+
const result = await execInWorkspace(root, command, timeout);
|
|
187
187
|
res.json({ ok: result.exitCode === 0, ...result });
|
|
188
188
|
} catch (e) {
|
|
189
189
|
res.status(400).json({ error: e.message });
|
|
@@ -310,100 +310,4 @@ function execInWorkspace(root, command, timeoutSec) {
|
|
|
310
310
|
resolve({ exitCode: code, stdout, stderr: stderr.substring(0, 5000), timedOut: false });
|
|
311
311
|
});
|
|
312
312
|
});
|
|
313
|
-
}
|
|
314
|
-
// ========================================
|
|
315
|
-
// WINDOWS IMPLEMENTATIONS
|
|
316
|
-
// ========================================
|
|
317
|
-
|
|
318
|
-
import fs from "fs";
|
|
319
|
-
const { promises: fsp } = fs;
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* grepWorkspaceWindows — Pure Node.js search, no grep required.
|
|
323
|
-
* Used automatically on Windows (process.platform === "win32").
|
|
324
|
-
*/
|
|
325
|
-
async function grepWorkspaceWindows(root, query, filePattern, maxResults) {
|
|
326
|
-
const results = [];
|
|
327
|
-
const queryLower = query.toLowerCase();
|
|
328
|
-
|
|
329
|
-
let fileRegex = null;
|
|
330
|
-
if (filePattern && filePattern !== "*") {
|
|
331
|
-
const escaped = filePattern
|
|
332
|
-
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
333
|
-
.replace(/\*/g, ".*");
|
|
334
|
-
fileRegex = new RegExp(escaped + "$", "i");
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
async function searchDir(dir) {
|
|
338
|
-
if (results.length >= maxResults) return;
|
|
339
|
-
let entries;
|
|
340
|
-
try { entries = await fsp.readdir(dir, { withFileTypes: true }); }
|
|
341
|
-
catch { return; }
|
|
342
|
-
|
|
343
|
-
for (const entry of entries) {
|
|
344
|
-
if (results.length >= maxResults) break;
|
|
345
|
-
const fullPath = path.join(dir, entry.name);
|
|
346
|
-
const relPath = path.relative(root, fullPath).replace(/\\/g, "/");
|
|
347
|
-
|
|
348
|
-
if (entry.isDirectory()) {
|
|
349
|
-
if (IGNORE_DIRS.includes(entry.name)) continue;
|
|
350
|
-
await searchDir(fullPath);
|
|
351
|
-
} else if (entry.isFile()) {
|
|
352
|
-
if (fileRegex && !fileRegex.test(entry.name)) continue;
|
|
353
|
-
const stat = await fsp.stat(fullPath).catch(() => null);
|
|
354
|
-
if (!stat || stat.size > 500000) continue;
|
|
355
|
-
|
|
356
|
-
try {
|
|
357
|
-
const content = await fsp.readFile(fullPath, "utf8");
|
|
358
|
-
const lines = content.split("\n");
|
|
359
|
-
for (let i = 0; i < lines.length && results.length < maxResults; i++) {
|
|
360
|
-
if (lines[i].toLowerCase().includes(queryLower)) {
|
|
361
|
-
results.push({ file: relPath, line: i + 1, content: lines[i].trim() });
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
} catch { /* skip unreadable */ }
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
await searchDir(root);
|
|
370
|
-
return results;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* execInWorkspaceWindows — Uses cmd.exe instead of sh.
|
|
375
|
-
* Used automatically on Windows.
|
|
376
|
-
*/
|
|
377
|
-
function execInWorkspaceWindows(root, command, timeoutSec) {
|
|
378
|
-
return new Promise((resolve) => {
|
|
379
|
-
const child = spawn("cmd.exe", ["/c", command], { cwd: root });
|
|
380
|
-
let stdout = "";
|
|
381
|
-
let stderr = "";
|
|
382
|
-
|
|
383
|
-
child.stdout.on("data", d => stdout += d.toString());
|
|
384
|
-
child.stderr.on("data", d => stderr += d.toString());
|
|
385
|
-
|
|
386
|
-
child.on("error", (err) => {
|
|
387
|
-
resolve({ exitCode: -1, stdout: "", stderr: err.message, timedOut: false });
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
const timer = setTimeout(() => {
|
|
391
|
-
try { child.kill("SIGKILL"); } catch {}
|
|
392
|
-
resolve({ exitCode: -1, stdout, stderr, timedOut: true });
|
|
393
|
-
}, timeoutSec * 1000);
|
|
394
|
-
|
|
395
|
-
child.on("close", (code) => {
|
|
396
|
-
clearTimeout(timer);
|
|
397
|
-
const maxLen = 10000;
|
|
398
|
-
if (stdout.length > maxLen) {
|
|
399
|
-
stdout = stdout.substring(0, maxLen / 2) + "\n...(truncated)...\n" + stdout.substring(stdout.length - maxLen / 2);
|
|
400
|
-
}
|
|
401
|
-
resolve({ exitCode: code ?? 0, stdout, stderr: stderr.substring(0, 5000), timedOut: false });
|
|
402
|
-
});
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Platform dispatchers — original functions unchanged above
|
|
407
|
-
const IS_WINDOWS = process.platform === "win32";
|
|
408
|
-
const grepWorkspaceX = IS_WINDOWS ? grepWorkspaceWindows : grepWorkspace;
|
|
409
|
-
const execInWorkspaceX = IS_WINDOWS ? execInWorkspaceWindows : execInWorkspace;
|
|
313
|
+
}
|
|
@@ -267,7 +267,7 @@ export class BaseRuntime {
|
|
|
267
267
|
|
|
268
268
|
/**
|
|
269
269
|
* @typedef {object} Integration
|
|
270
|
-
* @property {string} type - Tipo (rest-
|
|
270
|
+
* @property {string} type - Tipo (rest-templates, feign, axios, etc.)
|
|
271
271
|
* @property {string} file - Arquivo onde foi encontrado
|
|
272
272
|
* @property {number} line - Linha
|
|
273
273
|
* @property {string} targetUrl - URL alvo (se detectável)
|
|
@@ -98,7 +98,7 @@ export class NodeIntegrationAnalyzer {
|
|
|
98
98
|
findFetch(content, filePath, lines) {
|
|
99
99
|
const patterns = [
|
|
100
100
|
{ regex: /fetch\s*\(\s*['"`]/g, type: 'call' },
|
|
101
|
-
{ regex: /fetch\s*\(\s*\$\{/g, type: '
|
|
101
|
+
{ regex: /fetch\s*\(\s*\$\{/g, type: 'templates-call' }
|
|
102
102
|
];
|
|
103
103
|
|
|
104
104
|
for (const pattern of patterns) {
|
package/src/server.js
CHANGED
|
@@ -27,9 +27,11 @@ import { startMCPHttpServer } from "./mcp-http-server.js";
|
|
|
27
27
|
|
|
28
28
|
const execAsync = promisify(exec);
|
|
29
29
|
|
|
30
|
+
// ============================================
|
|
30
31
|
// 🧠 AI VIBE CODING ENGINE
|
|
31
32
|
// Sistema universal de auto-healing que usa AI
|
|
32
33
|
// para resolver QUALQUER erro automaticamente
|
|
34
|
+
// ============================================
|
|
33
35
|
|
|
34
36
|
class AIVibeCodingEngine extends EventEmitter {
|
|
35
37
|
constructor(processManager, getWorkspaceRoot) {
|
|
@@ -586,8 +588,10 @@ const MCP_PORT = process.env.MCP_PORT || 5056;
|
|
|
586
588
|
// 🧠 Inicializar AI Vibe Coding Engine
|
|
587
589
|
aiEngine = new AIVibeCodingEngine(processManager, () => WORKSPACE_ROOT);
|
|
588
590
|
|
|
591
|
+
// ============================================
|
|
589
592
|
// 🆕 BACKUP STORAGE (Sprint 1.3)
|
|
590
593
|
// In-memory backup storage with configurable max size
|
|
594
|
+
// ============================================
|
|
591
595
|
const BACKUPS = new Map();
|
|
592
596
|
const MAX_BACKUPS = 50;
|
|
593
597
|
const BACKUP_INDEX_PATH = path.join(os.tmpdir(), 'deepdebug-backups-index.json');
|
|
@@ -884,7 +888,9 @@ app.post("/workspace/batch-read", async (req, res) => {
|
|
|
884
888
|
}
|
|
885
889
|
});
|
|
886
890
|
|
|
891
|
+
// ============================================
|
|
887
892
|
// 🆕 FILE VALIDATION ENDPOINTS (Enhanced Analysis)
|
|
893
|
+
// ============================================
|
|
888
894
|
|
|
889
895
|
/**
|
|
890
896
|
* GET /workspace/file-exists
|
|
@@ -1244,7 +1250,9 @@ app.post("/workspace/find-field-definition", async (req, res) => {
|
|
|
1244
1250
|
});
|
|
1245
1251
|
|
|
1246
1252
|
|
|
1253
|
+
// ============================================
|
|
1247
1254
|
// 🆕 RUNTIME MANAGEMENT ENDPOINTS
|
|
1255
|
+
// ============================================
|
|
1248
1256
|
|
|
1249
1257
|
/** Detecta serviços no workspace */
|
|
1250
1258
|
app.get("/workspace/services/detect", async (_req, res) => {
|
|
@@ -1395,7 +1403,9 @@ app.get("/workspace/services/:serviceId/logs/stream", (req, res) => {
|
|
|
1395
1403
|
});
|
|
1396
1404
|
});
|
|
1397
1405
|
|
|
1406
|
+
// ============================================
|
|
1398
1407
|
// ENDPOINTS LEGADOS (manter compatibilidade)
|
|
1408
|
+
// ============================================
|
|
1399
1409
|
|
|
1400
1410
|
app.get("/workspace/files", async (req, res) => {
|
|
1401
1411
|
if (!WORKSPACE_ROOT) return res.status(400).json({ error: "workspace not set" });
|
|
@@ -1428,7 +1438,9 @@ app.post("/workspace/write", async (req, res) => {
|
|
|
1428
1438
|
}
|
|
1429
1439
|
});
|
|
1430
1440
|
|
|
1441
|
+
// ============================================
|
|
1431
1442
|
// ✅ CORRECTED: /workspace/patch endpoint
|
|
1443
|
+
// ============================================
|
|
1432
1444
|
app.post("/workspace/patch", async (req, res) => {
|
|
1433
1445
|
if (!WORKSPACE_ROOT) return res.status(400).json({ error: "workspace not set" });
|
|
1434
1446
|
const { diff, incidentId } = req.body || {};
|
|
@@ -1484,7 +1496,9 @@ app.post("/workspace/run", async (req, res) => {
|
|
|
1484
1496
|
res.json(out);
|
|
1485
1497
|
});
|
|
1486
1498
|
|
|
1499
|
+
// ============================================
|
|
1487
1500
|
// 🆕 TEST LOCAL ENDPOINTS
|
|
1501
|
+
// ============================================
|
|
1488
1502
|
|
|
1489
1503
|
/** Store test local state */
|
|
1490
1504
|
let TEST_LOCAL_STATE = {
|
|
@@ -1497,7 +1511,9 @@ let TEST_LOCAL_STATE = {
|
|
|
1497
1511
|
serverLogs: [] // Buffer circular de logs do servidor (últimos 1000)
|
|
1498
1512
|
};
|
|
1499
1513
|
|
|
1514
|
+
// ============================================
|
|
1500
1515
|
// 🆕 TEST LOCAL STATE ENDPOINTS (ADDED)
|
|
1516
|
+
// ============================================
|
|
1501
1517
|
|
|
1502
1518
|
/**
|
|
1503
1519
|
* GET /workspace/test-local/state
|
|
@@ -2001,8 +2017,10 @@ app.get("/workspace/config", async (_req, res) => {
|
|
|
2001
2017
|
}
|
|
2002
2018
|
});
|
|
2003
2019
|
|
|
2020
|
+
// ============================================
|
|
2004
2021
|
// 🆕 BACKUP & ROLLBACK ENDPOINTS (Sprint 1.3)
|
|
2005
2022
|
// Added without modifying existing endpoints
|
|
2023
|
+
// ============================================
|
|
2006
2024
|
|
|
2007
2025
|
/**
|
|
2008
2026
|
* Helper: Validate diff format
|
|
@@ -2407,9 +2425,11 @@ app.delete("/workspace/backups/:backupId", (req, res) => {
|
|
|
2407
2425
|
});
|
|
2408
2426
|
});
|
|
2409
2427
|
|
|
2428
|
+
// ============================================
|
|
2410
2429
|
// 📊 DIFF VIEWER ENDPOINT
|
|
2411
2430
|
// Returns before/after content for files modified by a patch
|
|
2412
2431
|
// Used by the frontend diff viewer
|
|
2432
|
+
// ============================================
|
|
2413
2433
|
|
|
2414
2434
|
/**
|
|
2415
2435
|
* GET /workspace/diff/by-incident/:incidentId
|
|
@@ -2589,8 +2609,10 @@ app.get("/workspace/diff/:backupId", async (req, res) => {
|
|
|
2589
2609
|
}
|
|
2590
2610
|
});
|
|
2591
2611
|
|
|
2612
|
+
// ============================================
|
|
2592
2613
|
// 🆕 DETECT PORT ENDPOINT (Sprint 1.2)
|
|
2593
2614
|
// Multi-language port detection
|
|
2615
|
+
// ============================================
|
|
2594
2616
|
|
|
2595
2617
|
/**
|
|
2596
2618
|
* GET /workspace/detect-port
|
|
@@ -2861,7 +2883,9 @@ async function detectDotNetPort(servicePath) {
|
|
|
2861
2883
|
return { port: 5000, method: 'dotnet-default' };
|
|
2862
2884
|
}
|
|
2863
2885
|
|
|
2886
|
+
// ============================================
|
|
2864
2887
|
// TEST LOCAL ENDPOINTS (existing)
|
|
2888
|
+
// ============================================
|
|
2865
2889
|
|
|
2866
2890
|
/** Prepare for testing: compile + discover endpoints */
|
|
2867
2891
|
app.post("/workspace/test-local/prepare", async (req, res) => {
|
|
@@ -3112,8 +3136,10 @@ app.get("/workspace/test-local/logs/stream", async (req, res) => {
|
|
|
3112
3136
|
}
|
|
3113
3137
|
});
|
|
3114
3138
|
|
|
3139
|
+
// ============================================
|
|
3115
3140
|
// 🆕 AUTO-TRAINING ENDPOINTS
|
|
3116
3141
|
// Escanear e ler arquivos para treinamento AI
|
|
3142
|
+
// ============================================
|
|
3117
3143
|
|
|
3118
3144
|
/**
|
|
3119
3145
|
* POST /workspace/scan-files
|
|
@@ -3332,8 +3358,10 @@ app.post("/workspace/read-files", async (req, res) => {
|
|
|
3332
3358
|
}
|
|
3333
3359
|
});
|
|
3334
3360
|
|
|
3361
|
+
// ============================================
|
|
3335
3362
|
// 🆕 SYSTEM FOLDER PICKER ENDPOINT
|
|
3336
3363
|
// Abre file picker nativo do SO (Windows/Mac/Linux)
|
|
3364
|
+
// ============================================
|
|
3337
3365
|
|
|
3338
3366
|
/**
|
|
3339
3367
|
* GET /system/folder-picker
|
|
@@ -3456,8 +3484,10 @@ app.get("/system/info", (req, res) => {
|
|
|
3456
3484
|
});
|
|
3457
3485
|
});
|
|
3458
3486
|
|
|
3487
|
+
// ============================================
|
|
3459
3488
|
// 🆕 API DOCS ENDPOINT
|
|
3460
3489
|
// Retorna endpoints detectados das controllers
|
|
3490
|
+
// ============================================
|
|
3461
3491
|
|
|
3462
3492
|
/**
|
|
3463
3493
|
* GET /workspace/api-docs
|
|
@@ -3637,7 +3667,9 @@ app.get("/workspace/api-docs/:controller", async (req, res) => {
|
|
|
3637
3667
|
}
|
|
3638
3668
|
});
|
|
3639
3669
|
|
|
3670
|
+
// ============================================
|
|
3640
3671
|
// 🧠 AI ENGINE ENDPOINTS
|
|
3672
|
+
// ============================================
|
|
3641
3673
|
|
|
3642
3674
|
/**
|
|
3643
3675
|
* GET /ai-engine/status
|
|
@@ -3843,8 +3875,10 @@ app.post("/workspace/test-local/restart", async (req, res) => {
|
|
|
3843
3875
|
}
|
|
3844
3876
|
});
|
|
3845
3877
|
|
|
3878
|
+
// ============================================
|
|
3846
3879
|
// 🤖 AGENTIC TOOLS ENDPOINTS
|
|
3847
3880
|
// Used by the agentic Claude loop for autonomous debugging
|
|
3881
|
+
// ============================================
|
|
3848
3882
|
|
|
3849
3883
|
/**
|
|
3850
3884
|
* POST /workspace/search
|
|
@@ -4014,8 +4048,10 @@ app.post("/workspace/exec", async (req, res) => {
|
|
|
4014
4048
|
}
|
|
4015
4049
|
});
|
|
4016
4050
|
|
|
4051
|
+
// ============================================
|
|
4017
4052
|
// 🧪 ENDPOINT TESTING
|
|
4018
4053
|
// Execute curl-like requests and capture full request/response
|
|
4054
|
+
// ============================================
|
|
4019
4055
|
|
|
4020
4056
|
/**
|
|
4021
4057
|
* POST /workspace/test-endpoint
|
|
@@ -4106,8 +4142,10 @@ app.post("/workspace/test-endpoint", async (req, res) => {
|
|
|
4106
4142
|
}
|
|
4107
4143
|
});
|
|
4108
4144
|
|
|
4145
|
+
// ============================================
|
|
4109
4146
|
// 🔀 GIT INTEGRATION
|
|
4110
4147
|
// Create branch, commit, push for auto-fix PRs
|
|
4148
|
+
// ============================================
|
|
4111
4149
|
|
|
4112
4150
|
/**
|
|
4113
4151
|
* POST /workspace/git/create-fix-branch
|
|
@@ -4330,8 +4368,16 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4330
4368
|
}
|
|
4331
4369
|
|
|
4332
4370
|
// Generate MR/PR URL based on provider
|
|
4371
|
+
// 🔒 SECURITY FIX: Remove any embedded tokens from URL before constructing MR link
|
|
4372
|
+
// This prevents tokens from appearing in logs, UI, or being shared accidentally
|
|
4333
4373
|
const cleanUrl = remoteUrl
|
|
4334
4374
|
.replace(/\.git$/, '')
|
|
4375
|
+
// Remove token authentication patterns (SECURITY FIX - tokens must not appear in MR URL)
|
|
4376
|
+
.replace(/https:\/\/[^@]+@github\.com\//, 'https://github.com/')
|
|
4377
|
+
.replace(/https:\/\/[^@]+@gitlab\.com\//, 'https://gitlab.com/')
|
|
4378
|
+
.replace(/https:\/\/[^@]+@bitbucket\.org\//, 'https://bitbucket.org/')
|
|
4379
|
+
.replace(/https:\/\/[^@]+@([^\/]+)\//, 'https://$1/') // Generic: any host with embedded credentials
|
|
4380
|
+
// Handle SSH URLs
|
|
4335
4381
|
.replace(/^git@github\.com:/, 'https://github.com/')
|
|
4336
4382
|
.replace(/^git@gitlab\.com:/, 'https://gitlab.com/')
|
|
4337
4383
|
.replace(/^git@bitbucket\.org:/, 'https://bitbucket.org/');
|
|
@@ -4576,9 +4622,11 @@ app.get("/workspace/git/status", async (req, res) => {
|
|
|
4576
4622
|
}
|
|
4577
4623
|
});
|
|
4578
4624
|
|
|
4625
|
+
// ============================================
|
|
4579
4626
|
// 💬 PULL REQUEST COMMENTS ENDPOINTS
|
|
4580
4627
|
// Read, reply, and resolve PR comments via git providers
|
|
4581
4628
|
// Used by the frontend "Code Review" tab in incident detail
|
|
4629
|
+
// ============================================
|
|
4582
4630
|
|
|
4583
4631
|
/**
|
|
4584
4632
|
* GET /workspace/git/pr/comments
|
|
@@ -4898,8 +4946,10 @@ app.post("/workspace/git/pr/comments/:commentId/fix-and-resolve", async (req, re
|
|
|
4898
4946
|
});
|
|
4899
4947
|
|
|
4900
4948
|
|
|
4949
|
+
// ============================================
|
|
4901
4950
|
// 🔧 GIT PROVIDER HELPER (internal)
|
|
4902
4951
|
// Detects provider from remote URL and configures with token
|
|
4952
|
+
// ============================================
|
|
4903
4953
|
|
|
4904
4954
|
async function _getGitProvider() {
|
|
4905
4955
|
if (!WORKSPACE_ROOT) return null;
|
|
@@ -4962,7 +5012,9 @@ async function _getGitProvider() {
|
|
|
4962
5012
|
return null;
|
|
4963
5013
|
}
|
|
4964
5014
|
}
|
|
5015
|
+
// ============================================
|
|
4965
5016
|
// 📂 MULTI-WORKSPACE ENDPOINTS
|
|
5017
|
+
// ============================================
|
|
4966
5018
|
|
|
4967
5019
|
/** Lista todos os workspaces abertos */
|
|
4968
5020
|
app.get("/workspaces", (_req, res) => {
|
|
@@ -5040,8 +5092,10 @@ app.post("/workspace/:workspaceId/run", async (req, res) => {
|
|
|
5040
5092
|
});
|
|
5041
5093
|
|
|
5042
5094
|
|
|
5095
|
+
// ============================================
|
|
5043
5096
|
// 🔌 WEBSOCKET REVERSE TUNNEL
|
|
5044
5097
|
// Connects Local Agent to Gateway — no public URL needed
|
|
5098
|
+
// ============================================
|
|
5045
5099
|
async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
5046
5100
|
if (!gatewayUrl || !apiKey || !tenantId) {
|
|
5047
5101
|
console.log('⚠️ WebSocket tunnel skipped: missing gatewayUrl, apiKey or tenantId in ~/.deepdebug/config.json');
|
|
@@ -5133,16 +5187,6 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5133
5187
|
}
|
|
5134
5188
|
}
|
|
5135
5189
|
|
|
5136
|
-
case 'workspace.git.create-fix-branch': {
|
|
5137
|
-
const fetch2 = (await import('node-fetch')).default;
|
|
5138
|
-
const r = await fetch2(`http://localhost:5055/workspace/git/create-fix-branch`, {
|
|
5139
|
-
method: 'POST',
|
|
5140
|
-
headers: { 'Content-Type': 'application/json' },
|
|
5141
|
-
body: JSON.stringify(params)
|
|
5142
|
-
});
|
|
5143
|
-
return await r.json();
|
|
5144
|
-
}
|
|
5145
|
-
|
|
5146
5190
|
case 'workspace.safe-patch':
|
|
5147
5191
|
case 'workspace.compile':
|
|
5148
5192
|
case 'workspace.rollback':
|
|
@@ -5165,7 +5209,32 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5165
5209
|
return await res.json();
|
|
5166
5210
|
}
|
|
5167
5211
|
|
|
5212
|
+
// ============================================
|
|
5213
|
+
// WORKSPACE DIFF — Returns before/after for patches
|
|
5214
|
+
// ============================================
|
|
5215
|
+
case 'workspace.diff': {
|
|
5216
|
+
const { backupId, incidentId } = params;
|
|
5217
|
+
let endpoint;
|
|
5218
|
+
|
|
5219
|
+
if (backupId) {
|
|
5220
|
+
endpoint = `/workspace/diff/${backupId}`;
|
|
5221
|
+
} else if (incidentId) {
|
|
5222
|
+
endpoint = `/workspace/diff/by-incident/${incidentId}`;
|
|
5223
|
+
} else {
|
|
5224
|
+
return { error: 'backupId or incidentId is required' };
|
|
5225
|
+
}
|
|
5226
|
+
|
|
5227
|
+
console.log(`📊 WS workspace.diff → ${endpoint}`);
|
|
5228
|
+
const res = await fetch(`${localBase}${endpoint}`, {
|
|
5229
|
+
method: 'GET',
|
|
5230
|
+
headers: { 'Content-Type': 'application/json' }
|
|
5231
|
+
});
|
|
5232
|
+
return await res.json();
|
|
5233
|
+
}
|
|
5234
|
+
|
|
5235
|
+
// ============================================
|
|
5168
5236
|
// MCP TOOLS — routed via MCP HTTP Bridge (port 5056)
|
|
5237
|
+
// ============================================
|
|
5169
5238
|
case 'mcp.read-file': {
|
|
5170
5239
|
const mcpBase = `http://localhost:5056`;
|
|
5171
5240
|
const res = await fetch(`${mcpBase}/mcp/read-file`, {
|
|
@@ -5288,7 +5357,9 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5288
5357
|
connect();
|
|
5289
5358
|
}
|
|
5290
5359
|
|
|
5360
|
+
// ============================================
|
|
5291
5361
|
// START SERVER
|
|
5362
|
+
// ============================================
|
|
5292
5363
|
app.listen(PORT, '0.0.0.0', async () => {
|
|
5293
5364
|
console.log(`\n🔌 DeepDebug Local Agent listening on port ${PORT}`);
|
|
5294
5365
|
console.log(`📦 Environment: ${process.env.NODE_ENV || 'development'}`);
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<module type="WEB_MODULE" version="4">
|
|
3
|
-
<component name="NewModuleRootManager">
|
|
4
|
-
<content url="file://$MODULE_DIR$">
|
|
5
|
-
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
6
|
-
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
7
|
-
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
|
-
</content>
|
|
9
|
-
<orderEntry type="inheritedJdk" />
|
|
10
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
|
11
|
-
</component>
|
|
12
|
-
</module>
|
package/.idea/modules.xml
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="ProjectModuleManager">
|
|
4
|
-
<modules>
|
|
5
|
-
<module fileurl="file://$PROJECT_DIR$/.idea/deepdebug-local-agent.iml" filepath="$PROJECT_DIR$/.idea/deepdebug-local-agent.iml" />
|
|
6
|
-
</modules>
|
|
7
|
-
</component>
|
|
8
|
-
</project>
|
package/.idea/vcs.xml
DELETED
|
File without changes
|