deepdebug-local-agent 1.0.17 → 1.0.18
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/package.json +1 -1
- package/src/server.js +298 -230
package/src/server.js
CHANGED
|
@@ -48,7 +48,7 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
48
48
|
this.currentSession = null;
|
|
49
49
|
this.lastSuccessfulConfig = null;
|
|
50
50
|
|
|
51
|
-
console.log('
|
|
51
|
+
console.log('[AI-Engine] Vibe Coding Engine initialized');
|
|
52
52
|
this.setupErrorMonitoring();
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -61,9 +61,9 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
61
61
|
|
|
62
62
|
this.processManager.on('stopped', async ({ serviceId, code, signal }) => {
|
|
63
63
|
if (this.isActive && code !== 0 && code !== null) {
|
|
64
|
-
console.log(`
|
|
64
|
+
console.log(`[AI-Engine] Process ${serviceId} crashed (code: ${code})`);
|
|
65
65
|
if (this.pendingFixes.length > 0) {
|
|
66
|
-
console.log(`
|
|
66
|
+
console.log(`[AI-Engine] ${this.pendingFixes.length} fixes pending`);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
});
|
|
@@ -164,12 +164,12 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
164
164
|
this.errorHistory.push(entry);
|
|
165
165
|
if (this.errorHistory.length > 200) this.errorHistory = this.errorHistory.slice(-200);
|
|
166
166
|
|
|
167
|
-
console.log(`
|
|
167
|
+
console.log(`[AI-Engine] Error: ${classification.type} (${classification.severity})`);
|
|
168
168
|
|
|
169
169
|
if (classification.autoFixable) {
|
|
170
170
|
const fix = this.getQuickFix(classification.type, errorMessage);
|
|
171
171
|
if (fix) {
|
|
172
|
-
console.log(`
|
|
172
|
+
console.log(`[AI-Engine] Quick fix: ${fix.description}`);
|
|
173
173
|
this.pendingFixes.push({ errorId: entry.id, fix, timestamp: Date.now() });
|
|
174
174
|
}
|
|
175
175
|
}
|
|
@@ -204,7 +204,7 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
async startWithAutoHealing(config) {
|
|
207
|
-
console.log('
|
|
207
|
+
console.log('[AI-Engine] Starting with auto-healing...');
|
|
208
208
|
|
|
209
209
|
this.currentSession = {
|
|
210
210
|
startTime: Date.now(),
|
|
@@ -222,12 +222,12 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
222
222
|
|
|
223
223
|
if (attempts > 1 && this.pendingFixes.length > 0) {
|
|
224
224
|
const fix = this.pendingFixes.shift();
|
|
225
|
-
console.log(`
|
|
225
|
+
console.log(`Applying: ${fix.fix.description}`);
|
|
226
226
|
currentConfig = this.applyFix(currentConfig, fix.fix);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
if (currentConfig.recompile) {
|
|
230
|
-
console.log('
|
|
230
|
+
console.log('Recompiling...');
|
|
231
231
|
await this.recompile();
|
|
232
232
|
delete currentConfig.recompile;
|
|
233
233
|
}
|
|
@@ -257,16 +257,16 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
lastError = result.error;
|
|
260
|
-
console.log(`
|
|
260
|
+
console.log(`Attempt ${attempts} failed: ${lastError?.substring(0, 100)}...`);
|
|
261
261
|
|
|
262
262
|
const fix = this.getFix(result.error, currentConfig);
|
|
263
263
|
if (fix) {
|
|
264
|
-
console.log(`
|
|
264
|
+
console.log(`Fix: ${fix.description}`);
|
|
265
265
|
currentConfig = this.applyFix(currentConfig, fix);
|
|
266
266
|
} else {
|
|
267
267
|
const ai = await this.analyzeWithAI('startup', lastError, currentConfig);
|
|
268
268
|
if (ai?.newConfig) {
|
|
269
|
-
console.log(`
|
|
269
|
+
console.log(`AI: ${ai.suggestion}`);
|
|
270
270
|
currentConfig = ai.newConfig;
|
|
271
271
|
} else {
|
|
272
272
|
break;
|
|
@@ -305,7 +305,7 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
305
305
|
|
|
306
306
|
// Detectar sucesso nos logs do Spring Boot
|
|
307
307
|
if (isStartupSuccess(message) && !resolved) {
|
|
308
|
-
console.log(`
|
|
308
|
+
console.log(`[AI-Engine] Detected startup success: ${message.substring(0, 80)}...`);
|
|
309
309
|
resolved = true;
|
|
310
310
|
cleanup();
|
|
311
311
|
resolve({ success: true, logs });
|
|
@@ -372,7 +372,7 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
372
372
|
newConfig.profile = next;
|
|
373
373
|
newConfig.args = this.updateArgs(config.args, 'profile', next);
|
|
374
374
|
newConfig.env = { ...config.env, SPRING_PROFILES_ACTIVE: next };
|
|
375
|
-
console.log(`
|
|
375
|
+
console.log(`Profile: ${config.profile} ${next}`);
|
|
376
376
|
break;
|
|
377
377
|
|
|
378
378
|
case 'change_port':
|
|
@@ -380,7 +380,7 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
380
380
|
newConfig.port = newPort;
|
|
381
381
|
newConfig.args = this.updateArgs(config.args, 'port', newPort);
|
|
382
382
|
newConfig.env = { ...config.env, SERVER_PORT: String(newPort), PORT: String(newPort) };
|
|
383
|
-
console.log(`
|
|
383
|
+
console.log(`Port: ${config.port} ${newPort}`);
|
|
384
384
|
break;
|
|
385
385
|
|
|
386
386
|
case 'recompile':
|
|
@@ -418,7 +418,7 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
418
418
|
|
|
419
419
|
if (response.ok) return await response.json();
|
|
420
420
|
} catch (err) {
|
|
421
|
-
console.log(`
|
|
421
|
+
console.log(`[AI-Engine] AI unavailable: ${err.message}`);
|
|
422
422
|
}
|
|
423
423
|
|
|
424
424
|
return this.localFallback(errorType, error, config);
|
|
@@ -527,7 +527,7 @@ class AIVibeCodingEngine extends EventEmitter {
|
|
|
527
527
|
|
|
528
528
|
setActive(active) {
|
|
529
529
|
this.isActive = active;
|
|
530
|
-
console.log(`
|
|
530
|
+
console.log(`[AI-Engine] ${active ? 'ENABLED' : 'DISABLED'}`);
|
|
531
531
|
}
|
|
532
532
|
|
|
533
533
|
clearHistory() {
|
|
@@ -577,7 +577,7 @@ const DEFAULT_WORKSPACE = process.env.DEFAULT_WORKSPACE || '/Users/macintosh/Ide
|
|
|
577
577
|
|
|
578
578
|
let WORKSPACE_ROOT = fs.existsSync(DEFAULT_WORKSPACE) ? DEFAULT_WORKSPACE : null;
|
|
579
579
|
if (WORKSPACE_ROOT) {
|
|
580
|
-
console.log(`
|
|
580
|
+
console.log(`Default workspace: ${WORKSPACE_ROOT}`);
|
|
581
581
|
}
|
|
582
582
|
|
|
583
583
|
let DETECTED_SERVICES = [];
|
|
@@ -585,11 +585,31 @@ const processManager = new ProcessManager();
|
|
|
585
585
|
const wsManager = new WorkspaceManager();
|
|
586
586
|
|
|
587
587
|
function resolveWorkspaceRoot(req) {
|
|
588
|
+
// TENANT ISOLATION: stateless, never mutates global state.
|
|
589
|
+
// Returns the workspace path for THIS specific request only.
|
|
590
|
+
//
|
|
591
|
+
// Priority:
|
|
592
|
+
// 1. X-Workspace-Root header -- sent by Gateway with tenant NFS path (post-onboarding)
|
|
593
|
+
// 2. X-Workspace-Id header -- fallback via WorkspaceManager (dev/local mode)
|
|
594
|
+
// 3. null -- no workspace for this request (onboarding flow)
|
|
595
|
+
//
|
|
596
|
+
// IMPORTANT: Does NOT fall back to WORKSPACE_ROOT global.
|
|
597
|
+
// A request without a workspace header gets null, not another tenant's path.
|
|
598
|
+
|
|
599
|
+
// 1. Tenant-isolated NFS path from Gateway
|
|
600
|
+
const secureRoot = req.headers['x-workspace-root'];
|
|
601
|
+
if (secureRoot && secureRoot.trim()) {
|
|
602
|
+
return secureRoot.trim();
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// 2. WorkspaceId (dev/local mode only)
|
|
588
606
|
const wsId = req.headers['x-workspace-id']
|
|
589
607
|
|| (req.body && req.body.workspaceId)
|
|
590
608
|
|| req.query.workspaceId;
|
|
591
609
|
if (wsId && wsManager.isOpen(wsId)) return wsManager.getRoot(wsId);
|
|
592
|
-
|
|
610
|
+
|
|
611
|
+
// 3. No workspace -- caller must handle null (e.g. onboarding endpoints)
|
|
612
|
+
return null;
|
|
593
613
|
}
|
|
594
614
|
const MCP_PORT = process.env.MCP_PORT || 5056;
|
|
595
615
|
|
|
@@ -652,7 +672,7 @@ function loadBackupIndex() {
|
|
|
652
672
|
});
|
|
653
673
|
}
|
|
654
674
|
}
|
|
655
|
-
console.log(`
|
|
675
|
+
console.log(`Restored ${BACKUPS.size} backups from disk`);
|
|
656
676
|
}
|
|
657
677
|
} catch (e) {
|
|
658
678
|
console.warn('Could not load backup index:', e.message);
|
|
@@ -664,19 +684,19 @@ loadBackupIndex();
|
|
|
664
684
|
|
|
665
685
|
// Event listeners do ProcessManager
|
|
666
686
|
processManager.on("started", ({ serviceId }) => {
|
|
667
|
-
console.log(`
|
|
687
|
+
console.log(`Service ${serviceId} started successfully`);
|
|
668
688
|
updateServiceStatus(serviceId, "running");
|
|
669
689
|
addServerLog("info", `Service ${serviceId} started successfully`);
|
|
670
690
|
});
|
|
671
691
|
|
|
672
692
|
processManager.on("stopped", ({ serviceId }) => {
|
|
673
|
-
console.log(`
|
|
693
|
+
console.log(`Service ${serviceId} stopped`);
|
|
674
694
|
updateServiceStatus(serviceId, "stopped");
|
|
675
695
|
addServerLog("info", `Service ${serviceId} stopped`);
|
|
676
696
|
});
|
|
677
697
|
|
|
678
698
|
processManager.on("error", ({ serviceId, error }) => {
|
|
679
|
-
console.error(`
|
|
699
|
+
console.error(`Service ${serviceId} error: ${error}`);
|
|
680
700
|
updateServiceStatus(serviceId, "failed");
|
|
681
701
|
addServerLog("error", `Service ${serviceId} error: ${error}`);
|
|
682
702
|
});
|
|
@@ -731,7 +751,7 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
731
751
|
// CLOUD MODE: Git clone (when Gateway sends repoUrl)
|
|
732
752
|
// ==========================================
|
|
733
753
|
if (repoUrl) {
|
|
734
|
-
console.log(`
|
|
754
|
+
console.log(`[/workspace/open] CLOUD MODE cloning ${repoUrl} (branch: ${branch})`);
|
|
735
755
|
|
|
736
756
|
try {
|
|
737
757
|
// Extract repo name: https://github.com/org/repo org_repo
|
|
@@ -772,16 +792,16 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
772
792
|
const alreadyCloned = await exists(gitDir);
|
|
773
793
|
|
|
774
794
|
if (alreadyCloned) {
|
|
775
|
-
console.log(`
|
|
795
|
+
console.log(`[cloud] Repo exists: ${clonePath}, updating...`);
|
|
776
796
|
// Update remote URL with fresh token
|
|
777
797
|
await execAsync(`git remote set-url origin "${authUrl}"`, { cwd: clonePath }).catch(() => {});
|
|
778
798
|
await execAsync(`git fetch origin`, { cwd: clonePath, timeout: 120000 });
|
|
779
799
|
// Checkout correct branch and reset to remote (discard previous patches)
|
|
780
800
|
await execAsync(`git checkout ${branch} 2>/dev/null || git checkout -b ${branch} origin/${branch}`, { cwd: clonePath }).catch(() => {});
|
|
781
801
|
await execAsync(`git reset --hard origin/${branch}`, { cwd: clonePath });
|
|
782
|
-
console.log(`
|
|
802
|
+
console.log(`[cloud] Updated to latest ${branch}`);
|
|
783
803
|
} else {
|
|
784
|
-
console.log(`
|
|
804
|
+
console.log(`[cloud] Cloning ${repoUrl} (branch: ${branch})...`);
|
|
785
805
|
await execAsync(
|
|
786
806
|
`git clone --branch ${branch} --single-branch --depth 50 "${authUrl}" "${clonePath}"`,
|
|
787
807
|
{ timeout: 300000 }
|
|
@@ -789,7 +809,7 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
789
809
|
// Configure git user for future commits
|
|
790
810
|
await execAsync(`git config user.email "deepdebug-ai@deepdebug.ai"`, { cwd: clonePath });
|
|
791
811
|
await execAsync(`git config user.name "DeepDebug AI"`, { cwd: clonePath });
|
|
792
|
-
console.log(`
|
|
812
|
+
console.log(`[cloud] Cloned successfully: ${clonePath}`);
|
|
793
813
|
}
|
|
794
814
|
|
|
795
815
|
// Set as active workspace
|
|
@@ -799,7 +819,7 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
799
819
|
}
|
|
800
820
|
const wsId = workspaceId || "default";
|
|
801
821
|
try { await wsManager.open(wsId, clonePath); } catch (err) {
|
|
802
|
-
console.warn(`
|
|
822
|
+
console.warn(`WorkspaceManager.open failed (non-fatal): ${err.message}`);
|
|
803
823
|
}
|
|
804
824
|
|
|
805
825
|
const meta = await detectProject(clonePath);
|
|
@@ -815,7 +835,7 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
815
835
|
});
|
|
816
836
|
|
|
817
837
|
} catch (gitErr) {
|
|
818
|
-
console.error(`
|
|
838
|
+
console.error(`[cloud] Git clone failed:`, gitErr.message);
|
|
819
839
|
const hint = gitErr.message.includes('Authentication') || gitErr.message.includes('could not read')
|
|
820
840
|
? "Authentication failed. Check token and repo URL."
|
|
821
841
|
: gitErr.message.includes('not found') || gitErr.message.includes('does not exist')
|
|
@@ -842,7 +862,7 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
842
862
|
try {
|
|
843
863
|
await wsManager.open(wsId, abs);
|
|
844
864
|
} catch (err) {
|
|
845
|
-
console.warn(`
|
|
865
|
+
console.warn(`WorkspaceManager open failed (non-fatal): ${err.message}`);
|
|
846
866
|
}
|
|
847
867
|
|
|
848
868
|
const meta = await detectProject(abs);
|
|
@@ -859,24 +879,30 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
859
879
|
*/
|
|
860
880
|
app.post("/workspace/clone", async (req, res) => {
|
|
861
881
|
const body = req.body || {};
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
const gitUrl = body.gitUrl || body.repoUrl;
|
|
866
|
-
const targetPath = body.targetPath || body.targetDir;
|
|
882
|
+
const gitUrl = body.gitUrl || body.repoUrl;
|
|
883
|
+
const targetPath = body.targetPath || body.targetDir;
|
|
884
|
+
const tenantId = body.tenantId;
|
|
867
885
|
const workspaceId = body.workspaceId;
|
|
868
|
-
const gitToken
|
|
869
|
-
const branch
|
|
886
|
+
const gitToken = body.gitToken;
|
|
887
|
+
const branch = body.branch;
|
|
870
888
|
|
|
871
889
|
if (!gitUrl) return res.status(400).json({ ok: false, error: "gitUrl is required" });
|
|
872
890
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
const
|
|
876
|
-
|
|
877
|
-
|
|
891
|
+
const repoName = gitUrl.split('/').pop().replace('.git', '');
|
|
892
|
+
|
|
893
|
+
const NFS_MOUNT = '/mnt/workspaces';
|
|
894
|
+
let absTarget;
|
|
895
|
+
if (tenantId && fs.existsSync(NFS_MOUNT)) {
|
|
896
|
+
absTarget = path.join(NFS_MOUNT, tenantId, repoName);
|
|
897
|
+
console.log(`[clone] Using NFS persistent storage: ${absTarget}`);
|
|
898
|
+
} else if (targetPath) {
|
|
899
|
+
absTarget = path.resolve(targetPath);
|
|
900
|
+
console.log(`[clone] Using explicit targetPath: ${absTarget}`);
|
|
901
|
+
} else {
|
|
902
|
+
absTarget = path.join(process.env.HOME || '/home/deepdebug', 'DeepDebug', repoName);
|
|
903
|
+
console.log(`[clone] Using fallback path: ${absTarget}`);
|
|
904
|
+
}
|
|
878
905
|
|
|
879
|
-
// If gitToken provided and not already embedded in URL, embed it
|
|
880
906
|
let authenticatedUrl = gitUrl;
|
|
881
907
|
if (gitToken && gitUrl.startsWith('https://') && !gitUrl.includes('@')) {
|
|
882
908
|
if (gitUrl.includes('bitbucket.org')) {
|
|
@@ -887,9 +913,7 @@ app.post("/workspace/clone", async (req, res) => {
|
|
|
887
913
|
authenticatedUrl = gitUrl.replace('https://', `https://x-access-token:${gitToken}@`);
|
|
888
914
|
}
|
|
889
915
|
}
|
|
890
|
-
|
|
891
|
-
const absTarget = resolvedTarget;
|
|
892
|
-
console.log(` Clone request: ${gitUrl} -> ${absTarget}`);
|
|
916
|
+
console.log(`Clone request: ${gitUrl} -> ${absTarget}`);
|
|
893
917
|
|
|
894
918
|
try {
|
|
895
919
|
// Ensure parent directory exists
|
|
@@ -901,14 +925,22 @@ app.post("/workspace/clone", async (req, res) => {
|
|
|
901
925
|
const alreadyCloned = await exists(gitDir);
|
|
902
926
|
|
|
903
927
|
if (alreadyCloned) {
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
928
|
+
const targetBranch = branch || 'develop';
|
|
929
|
+
console.log(`Repo already exists at ${absTarget}, updating branch: ${targetBranch}...`);
|
|
930
|
+
await execAsync('git fetch --all', { cwd: absTarget, timeout: 60000 });
|
|
931
|
+
try {
|
|
932
|
+
await execAsync(`git checkout ${targetBranch}`, { cwd: absTarget, timeout: 30000 });
|
|
933
|
+
const { stdout } = await execAsync(`git pull origin ${targetBranch}`, { cwd: absTarget, timeout: 120000 });
|
|
934
|
+
console.log(`git pull: ${stdout.trim()}`);
|
|
935
|
+
} catch (checkoutErr) {
|
|
936
|
+
console.warn(`Checkout ${targetBranch} failed, staying on current branch: ${checkoutErr.message}`);
|
|
937
|
+
}
|
|
907
938
|
} else {
|
|
908
939
|
const safeLog = authenticatedUrl.replace(/x-token-auth:[^@]+@/g, 'x-token-auth:***@').replace(/oauth2:[^@]+@/g, 'oauth2:***@').replace(/x-access-token:[^@]+@/g, 'x-access-token:***@');
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
940
|
+
const cloneCmd = branch ? `git clone -b ${branch} "${authenticatedUrl}" "${absTarget}"` : `git clone "${authenticatedUrl}" "${absTarget}"`;
|
|
941
|
+
console.log(`Running: git clone${branch ? ' -b ' + branch : ''} "${safeLog}" "${absTarget}"`);
|
|
942
|
+
await execAsync(cloneCmd, { timeout: 300000 });
|
|
943
|
+
console.log(`Clone complete: ${absTarget}`);
|
|
912
944
|
}
|
|
913
945
|
|
|
914
946
|
// Open the cloned workspace
|
|
@@ -917,7 +949,7 @@ app.post("/workspace/clone", async (req, res) => {
|
|
|
917
949
|
try {
|
|
918
950
|
await wsManager.open(wsId, absTarget);
|
|
919
951
|
} catch (err) {
|
|
920
|
-
console.warn(`
|
|
952
|
+
console.warn(`WorkspaceManager.open failed (non-fatal): ${err.message}`);
|
|
921
953
|
}
|
|
922
954
|
|
|
923
955
|
const meta = await detectProject(absTarget);
|
|
@@ -933,7 +965,7 @@ app.post("/workspace/clone", async (req, res) => {
|
|
|
933
965
|
});
|
|
934
966
|
|
|
935
967
|
} catch (err) {
|
|
936
|
-
console.error(`
|
|
968
|
+
console.error(`Clone failed: ${err.message}`);
|
|
937
969
|
const hint = err.message.includes('Authentication') || err.message.includes('could not read')
|
|
938
970
|
? "Authentication failed. Ensure the repo is public or GitHub integration is configured."
|
|
939
971
|
: err.message.includes('not found') || err.message.includes('does not exist')
|
|
@@ -1015,6 +1047,35 @@ app.get("/workspace/file-content", async (req, res) => {
|
|
|
1015
1047
|
}
|
|
1016
1048
|
});
|
|
1017
1049
|
|
|
1050
|
+
/** Escreve/salva conteudo de arquivo no workspace */
|
|
1051
|
+
app.post("/workspace/write-file", async (req, res) => {
|
|
1052
|
+
const workspaceRoot = getEffectiveRoot(req);
|
|
1053
|
+
if (!workspaceRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1054
|
+
|
|
1055
|
+
const { path: relativePath, content: fileContent } = req.body || {};
|
|
1056
|
+
if (!relativePath) return res.status(400).json({ error: "path is required" });
|
|
1057
|
+
if (fileContent === undefined || fileContent === null) return res.status(400).json({ error: "content is required" });
|
|
1058
|
+
|
|
1059
|
+
const pathMod = await import('path');
|
|
1060
|
+
const fullPath = pathMod.default.resolve(workspaceRoot, relativePath);
|
|
1061
|
+
|
|
1062
|
+
// Security: prevent path traversal outside workspace root
|
|
1063
|
+
if (!fullPath.startsWith(workspaceRoot)) {
|
|
1064
|
+
return res.status(403).json({ error: "Path traversal not allowed" });
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
try {
|
|
1068
|
+
const { mkdir } = await import('fs/promises');
|
|
1069
|
+
await mkdir(pathMod.default.dirname(fullPath), { recursive: true });
|
|
1070
|
+
await fsPromises.writeFile(fullPath, fileContent, 'utf8');
|
|
1071
|
+
res.json({ ok: true, path: relativePath, size: fileContent.length });
|
|
1072
|
+
} catch (err) {
|
|
1073
|
+
res.status(500).json({ error: "Could not write file", details: err.message });
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
|
|
1018
1079
|
/** L mltiplos arquivos */
|
|
1019
1080
|
app.post("/workspace/batch-read", async (req, res) => {
|
|
1020
1081
|
const wsRoot = resolveWorkspaceRoot(req);
|
|
@@ -1057,7 +1118,7 @@ app.get("/workspace/file-exists", async (req, res) => {
|
|
|
1057
1118
|
const fullPath = path.join(wsRoot, relativePath);
|
|
1058
1119
|
const fileExists = await exists(fullPath);
|
|
1059
1120
|
|
|
1060
|
-
console.log(`
|
|
1121
|
+
console.log(`[file-exists] ${relativePath} -> ${fileExists ? 'EXISTS' : 'NOT FOUND'}`);
|
|
1061
1122
|
|
|
1062
1123
|
res.json({
|
|
1063
1124
|
ok: true,
|
|
@@ -1066,7 +1127,7 @@ app.get("/workspace/file-exists", async (req, res) => {
|
|
|
1066
1127
|
fullPath: fullPath
|
|
1067
1128
|
});
|
|
1068
1129
|
} catch (err) {
|
|
1069
|
-
console.error(`
|
|
1130
|
+
console.error(`[file-exists] Error:`, err.message);
|
|
1070
1131
|
res.status(500).json({ ok: false, error: err.message });
|
|
1071
1132
|
}
|
|
1072
1133
|
});
|
|
@@ -1098,7 +1159,7 @@ app.post("/workspace/validate-paths", async (req, res) => {
|
|
|
1098
1159
|
const allExist = results.every(r => r.exists);
|
|
1099
1160
|
const missingPaths = results.filter(r => !r.exists).map(r => r.path);
|
|
1100
1161
|
|
|
1101
|
-
console.log(`
|
|
1162
|
+
console.log(`[validate-paths] Checked ${pathList.length} paths, ${missingPaths.length} missing`);
|
|
1102
1163
|
|
|
1103
1164
|
res.json({
|
|
1104
1165
|
ok: true,
|
|
@@ -1108,7 +1169,7 @@ app.post("/workspace/validate-paths", async (req, res) => {
|
|
|
1108
1169
|
totalChecked: pathList.length
|
|
1109
1170
|
});
|
|
1110
1171
|
} catch (err) {
|
|
1111
|
-
console.error(`
|
|
1172
|
+
console.error(`[validate-paths] Error:`, err.message);
|
|
1112
1173
|
res.status(500).json({ ok: false, error: err.message });
|
|
1113
1174
|
}
|
|
1114
1175
|
});
|
|
@@ -1131,7 +1192,7 @@ app.post("/workspace/search-file", async (req, res) => {
|
|
|
1131
1192
|
}
|
|
1132
1193
|
|
|
1133
1194
|
try {
|
|
1134
|
-
console.log(`
|
|
1195
|
+
console.log(`[search-file] Searching for: ${fileName}`);
|
|
1135
1196
|
|
|
1136
1197
|
const rawFiles = await listRecursive(wsRoot, {
|
|
1137
1198
|
maxDepth: 15,
|
|
@@ -1148,7 +1209,7 @@ app.post("/workspace/search-file", async (req, res) => {
|
|
|
1148
1209
|
return String(f);
|
|
1149
1210
|
}).filter(f => f && typeof f === 'string');
|
|
1150
1211
|
|
|
1151
|
-
console.log(`
|
|
1212
|
+
console.log(`[search-file] Scanning ${allFiles.length} files`);
|
|
1152
1213
|
|
|
1153
1214
|
// Strategy 1: Exact path match
|
|
1154
1215
|
let foundPath = allFiles.find(f => f.endsWith('/' + fileName) || f === fileName);
|
|
@@ -1178,14 +1239,14 @@ app.post("/workspace/search-file", async (req, res) => {
|
|
|
1178
1239
|
}
|
|
1179
1240
|
|
|
1180
1241
|
if (foundPath) {
|
|
1181
|
-
console.log(`
|
|
1242
|
+
console.log(`[search-file] Found: ${foundPath}`);
|
|
1182
1243
|
res.json({ ok: true, found: true, path: foundPath, fileName });
|
|
1183
1244
|
} else {
|
|
1184
|
-
console.log(`
|
|
1245
|
+
console.log(`[search-file] Not found: ${fileName}`);
|
|
1185
1246
|
res.json({ ok: true, found: false, fileName, searchedFiles: allFiles.length });
|
|
1186
1247
|
}
|
|
1187
1248
|
} catch (err) {
|
|
1188
|
-
console.error(`
|
|
1249
|
+
console.error(`[search-file] Error:`, err.message);
|
|
1189
1250
|
res.status(500).json({ ok: false, error: err.message });
|
|
1190
1251
|
}
|
|
1191
1252
|
});
|
|
@@ -1209,7 +1270,7 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1209
1270
|
}
|
|
1210
1271
|
|
|
1211
1272
|
try {
|
|
1212
|
-
console.log(`
|
|
1273
|
+
console.log(`[search-by-content] Searching for terms: ${terms.join(', ')}`);
|
|
1213
1274
|
|
|
1214
1275
|
const rawFiles = await listRecursive(wsRoot, {
|
|
1215
1276
|
maxDepth: 15,
|
|
@@ -1229,7 +1290,7 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1229
1290
|
return extensions.some(ext => filePath.endsWith(ext));
|
|
1230
1291
|
});
|
|
1231
1292
|
|
|
1232
|
-
console.log(`
|
|
1293
|
+
console.log(`[search-by-content] Scanning ${filteredFiles.length} files`);
|
|
1233
1294
|
|
|
1234
1295
|
const results = [];
|
|
1235
1296
|
|
|
@@ -1274,7 +1335,7 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1274
1335
|
return true;
|
|
1275
1336
|
}).slice(0, maxResults);
|
|
1276
1337
|
|
|
1277
|
-
console.log(`
|
|
1338
|
+
console.log(`[search-by-content] Found ${dedupedResults.length} matching files`);
|
|
1278
1339
|
|
|
1279
1340
|
res.json({
|
|
1280
1341
|
ok: true,
|
|
@@ -1283,7 +1344,7 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1283
1344
|
termsSearched: terms
|
|
1284
1345
|
});
|
|
1285
1346
|
} catch (err) {
|
|
1286
|
-
console.error(`
|
|
1347
|
+
console.error(`[search-by-content] Error:`, err.message);
|
|
1287
1348
|
res.status(500).json({ ok: false, error: err.message });
|
|
1288
1349
|
}
|
|
1289
1350
|
});
|
|
@@ -1307,7 +1368,7 @@ app.post("/workspace/find-field-definition", async (req, res) => {
|
|
|
1307
1368
|
}
|
|
1308
1369
|
|
|
1309
1370
|
try {
|
|
1310
|
-
console.log(`
|
|
1371
|
+
console.log(`[find-field] Searching for field: ${fieldName}`);
|
|
1311
1372
|
|
|
1312
1373
|
const rawFiles = await listRecursive(wsRoot, {
|
|
1313
1374
|
maxDepth: 15,
|
|
@@ -1386,7 +1447,7 @@ app.post("/workspace/find-field-definition", async (req, res) => {
|
|
|
1386
1447
|
return 0;
|
|
1387
1448
|
});
|
|
1388
1449
|
|
|
1389
|
-
console.log(`
|
|
1450
|
+
console.log(`[find-field] Found ${definitions.length} definitions for '${fieldName}'`);
|
|
1390
1451
|
|
|
1391
1452
|
res.json({
|
|
1392
1453
|
ok: true,
|
|
@@ -1395,7 +1456,7 @@ app.post("/workspace/find-field-definition", async (req, res) => {
|
|
|
1395
1456
|
totalSearched: targetFiles.length
|
|
1396
1457
|
});
|
|
1397
1458
|
} catch (err) {
|
|
1398
|
-
console.error(`
|
|
1459
|
+
console.error(`[find-field] Error:`, err.message);
|
|
1399
1460
|
res.status(500).json({ ok: false, error: err.message });
|
|
1400
1461
|
}
|
|
1401
1462
|
});
|
|
@@ -1609,7 +1670,7 @@ app.post("/workspace/patch", async (req, res) => {
|
|
|
1609
1670
|
if (!diff) return res.status(400).json({ error: "diff is required" });
|
|
1610
1671
|
|
|
1611
1672
|
try {
|
|
1612
|
-
console.log(`
|
|
1673
|
+
console.log(`Applying patch for incident: ${incidentId || 'unknown'}`);
|
|
1613
1674
|
const out = await applyUnifiedDiff(wsRoot, diff);
|
|
1614
1675
|
|
|
1615
1676
|
// CRITICAL FIX: Format response as expected by Gateway
|
|
@@ -1624,7 +1685,7 @@ app.post("/workspace/patch", async (req, res) => {
|
|
|
1624
1685
|
incidentId: incidentId
|
|
1625
1686
|
};
|
|
1626
1687
|
|
|
1627
|
-
console.log(`
|
|
1688
|
+
console.log(`Patch applied successfully:`, {
|
|
1628
1689
|
target: out.target,
|
|
1629
1690
|
bytes: out.bytes,
|
|
1630
1691
|
incident: incidentId
|
|
@@ -1632,7 +1693,7 @@ app.post("/workspace/patch", async (req, res) => {
|
|
|
1632
1693
|
|
|
1633
1694
|
res.json(response);
|
|
1634
1695
|
} catch (e) {
|
|
1635
|
-
console.error(`
|
|
1696
|
+
console.error(`Patch failed:`, e.message);
|
|
1636
1697
|
res.status(400).json({
|
|
1637
1698
|
ok: false,
|
|
1638
1699
|
error: "patch failed",
|
|
@@ -1684,7 +1745,7 @@ let TEST_LOCAL_STATE = {
|
|
|
1684
1745
|
* Returns current test local state with auto-detected port
|
|
1685
1746
|
*/
|
|
1686
1747
|
app.get("/workspace/test-local/state", async (req, res) => {
|
|
1687
|
-
console.log("
|
|
1748
|
+
console.log("[TEST-LOCAL] Getting state:", TEST_LOCAL_STATE.status);
|
|
1688
1749
|
|
|
1689
1750
|
// Auto-detect port if not set in config
|
|
1690
1751
|
let port = TEST_LOCAL_STATE.config?.server?.port;
|
|
@@ -1723,7 +1784,7 @@ app.post("/workspace/test-local/compile", async (req, res) => {
|
|
|
1723
1784
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1724
1785
|
|
|
1725
1786
|
try {
|
|
1726
|
-
console.log("
|
|
1787
|
+
console.log("[TEST-LOCAL] Starting compilation...");
|
|
1727
1788
|
TEST_LOCAL_STATE.status = "compiling";
|
|
1728
1789
|
|
|
1729
1790
|
const meta = await detectProject(wsRoot);
|
|
@@ -1758,7 +1819,7 @@ app.post("/workspace/test-local/compile", async (req, res) => {
|
|
|
1758
1819
|
duration: compileResult.duration
|
|
1759
1820
|
};
|
|
1760
1821
|
|
|
1761
|
-
console.log("
|
|
1822
|
+
console.log("[TEST-LOCAL] Compilation successful");
|
|
1762
1823
|
|
|
1763
1824
|
res.json({
|
|
1764
1825
|
ok: true,
|
|
@@ -1768,7 +1829,7 @@ app.post("/workspace/test-local/compile", async (req, res) => {
|
|
|
1768
1829
|
stdout: compileResult.stdout
|
|
1769
1830
|
});
|
|
1770
1831
|
} catch (err) {
|
|
1771
|
-
console.error("
|
|
1832
|
+
console.error("[TEST-LOCAL] Compilation failed:", err.message);
|
|
1772
1833
|
TEST_LOCAL_STATE.status = "error";
|
|
1773
1834
|
res.status(500).json({ ok: false, error: err.message });
|
|
1774
1835
|
}
|
|
@@ -1821,7 +1882,7 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1821
1882
|
const command = 'java';
|
|
1822
1883
|
const args = ['-jar', jarPath, `--server.port=${serverPort}`];
|
|
1823
1884
|
|
|
1824
|
-
console.log(`
|
|
1885
|
+
console.log(`Command: ${command} ${args.join(' ')}`);
|
|
1825
1886
|
|
|
1826
1887
|
// Env LIMPO - remover TODAS as variveis Spring que podem interferir
|
|
1827
1888
|
const cleanEnv = { ...process.env };
|
|
@@ -1851,7 +1912,7 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1851
1912
|
TEST_LOCAL_STATE.status = "running";
|
|
1852
1913
|
TEST_LOCAL_STATE.config = { port: serverPort };
|
|
1853
1914
|
|
|
1854
|
-
console.log(`
|
|
1915
|
+
console.log(`[TEST-LOCAL] Server started on port ${serverPort}`);
|
|
1855
1916
|
|
|
1856
1917
|
res.json({
|
|
1857
1918
|
ok: true,
|
|
@@ -1901,7 +1962,7 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1901
1962
|
});
|
|
1902
1963
|
}
|
|
1903
1964
|
} catch (err) {
|
|
1904
|
-
console.error("
|
|
1965
|
+
console.error("[TEST-LOCAL] Server start failed:", err.message);
|
|
1905
1966
|
TEST_LOCAL_STATE.status = "error";
|
|
1906
1967
|
res.status(500).json({ ok: false, error: err.message });
|
|
1907
1968
|
}
|
|
@@ -1913,19 +1974,19 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1913
1974
|
*/
|
|
1914
1975
|
app.post("/workspace/test-local/stop", async (req, res) => {
|
|
1915
1976
|
try {
|
|
1916
|
-
console.log("
|
|
1977
|
+
console.log("[TEST-LOCAL] Stopping server...");
|
|
1917
1978
|
|
|
1918
1979
|
await processManager.stop('test-local');
|
|
1919
1980
|
TEST_LOCAL_STATE.status = "stopped";
|
|
1920
1981
|
|
|
1921
|
-
console.log("
|
|
1982
|
+
console.log("[TEST-LOCAL] Server stopped");
|
|
1922
1983
|
|
|
1923
1984
|
res.json({
|
|
1924
1985
|
ok: true,
|
|
1925
1986
|
status: "stopped"
|
|
1926
1987
|
});
|
|
1927
1988
|
} catch (err) {
|
|
1928
|
-
console.error("
|
|
1989
|
+
console.error("[TEST-LOCAL] Server stop failed:", err.message);
|
|
1929
1990
|
res.status(500).json({ ok: false, error: err.message });
|
|
1930
1991
|
}
|
|
1931
1992
|
});
|
|
@@ -1939,7 +2000,7 @@ app.get("/workspace/test-local/endpoints", async (req, res) => {
|
|
|
1939
2000
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1940
2001
|
|
|
1941
2002
|
try {
|
|
1942
|
-
console.log("
|
|
2003
|
+
console.log("[TEST-LOCAL] Getting endpoints...");
|
|
1943
2004
|
|
|
1944
2005
|
// If endpoints are cached, return them
|
|
1945
2006
|
if (TEST_LOCAL_STATE.endpoints && TEST_LOCAL_STATE.endpoints.length > 0) {
|
|
@@ -1962,7 +2023,7 @@ app.get("/workspace/test-local/endpoints", async (req, res) => {
|
|
|
1962
2023
|
|
|
1963
2024
|
TEST_LOCAL_STATE.endpoints = payloadDocs.endpoints;
|
|
1964
2025
|
|
|
1965
|
-
console.log(`
|
|
2026
|
+
console.log(`[TEST-LOCAL] Discovered ${payloadDocs.endpoints.length} endpoints`);
|
|
1966
2027
|
|
|
1967
2028
|
res.json({
|
|
1968
2029
|
ok: true,
|
|
@@ -1970,7 +2031,7 @@ app.get("/workspace/test-local/endpoints", async (req, res) => {
|
|
|
1970
2031
|
cached: false
|
|
1971
2032
|
});
|
|
1972
2033
|
} catch (err) {
|
|
1973
|
-
console.error("
|
|
2034
|
+
console.error("[TEST-LOCAL] Failed to get endpoints:", err.message);
|
|
1974
2035
|
res.status(500).json({ ok: false, error: err.message });
|
|
1975
2036
|
}
|
|
1976
2037
|
});
|
|
@@ -1990,7 +2051,7 @@ app.post("/workspace/test-local/execute", async (req, res) => {
|
|
|
1990
2051
|
const serverPort = port || TEST_LOCAL_STATE.config?.server?.port || 8080;
|
|
1991
2052
|
const url = `http://localhost:${serverPort}${reqPath}`;
|
|
1992
2053
|
|
|
1993
|
-
console.log(`
|
|
2054
|
+
console.log(`[TEST-LOCAL] Executing: ${method} ${url}`);
|
|
1994
2055
|
|
|
1995
2056
|
const startTime = Date.now();
|
|
1996
2057
|
|
|
@@ -2039,14 +2100,14 @@ app.post("/workspace/test-local/execute", async (req, res) => {
|
|
|
2039
2100
|
TEST_LOCAL_STATE.testResults.shift();
|
|
2040
2101
|
}
|
|
2041
2102
|
|
|
2042
|
-
console.log(`
|
|
2103
|
+
console.log(`[TEST-LOCAL] Test result: ${response.status} (${duration}ms)`);
|
|
2043
2104
|
|
|
2044
2105
|
res.json({
|
|
2045
2106
|
ok: true,
|
|
2046
2107
|
result
|
|
2047
2108
|
});
|
|
2048
2109
|
} catch (err) {
|
|
2049
|
-
console.error("
|
|
2110
|
+
console.error("[TEST-LOCAL] Test execution failed:", err.message);
|
|
2050
2111
|
res.json({
|
|
2051
2112
|
ok: false,
|
|
2052
2113
|
error: err.message,
|
|
@@ -2065,7 +2126,7 @@ app.post("/workspace/test-local/execute", async (req, res) => {
|
|
|
2065
2126
|
* Returns stored test results
|
|
2066
2127
|
*/
|
|
2067
2128
|
app.get("/workspace/test-local/results", (req, res) => {
|
|
2068
|
-
console.log("
|
|
2129
|
+
console.log("[TEST-LOCAL] Getting test results...");
|
|
2069
2130
|
|
|
2070
2131
|
res.json({
|
|
2071
2132
|
ok: true,
|
|
@@ -2079,7 +2140,7 @@ app.get("/workspace/test-local/results", (req, res) => {
|
|
|
2079
2140
|
* Clears stored test results
|
|
2080
2141
|
*/
|
|
2081
2142
|
app.post("/workspace/test-local/clear-results", (req, res) => {
|
|
2082
|
-
console.log("
|
|
2143
|
+
console.log("[TEST-LOCAL] Clearing test results...");
|
|
2083
2144
|
|
|
2084
2145
|
TEST_LOCAL_STATE.testResults = [];
|
|
2085
2146
|
|
|
@@ -2096,7 +2157,7 @@ app.post("/workspace/test-local/clear-results", (req, res) => {
|
|
|
2096
2157
|
app.get("/workspace/test-local/logs", (req, res) => {
|
|
2097
2158
|
const limit = parseInt(req.query.limit) || 500;
|
|
2098
2159
|
|
|
2099
|
-
console.log(`
|
|
2160
|
+
console.log(`[TEST-LOCAL] Getting logs (limit: ${limit})`);
|
|
2100
2161
|
|
|
2101
2162
|
const logs = TEST_LOCAL_STATE.serverLogs.slice(-limit);
|
|
2102
2163
|
|
|
@@ -2115,7 +2176,7 @@ app.get("/workspace/controllers", async (req, res) => {
|
|
|
2115
2176
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
2116
2177
|
|
|
2117
2178
|
try {
|
|
2118
|
-
console.log("
|
|
2179
|
+
console.log("[CONTROLLERS] Discovering controllers...");
|
|
2119
2180
|
|
|
2120
2181
|
const scanner = new WorkspaceScanner(wsRoot);
|
|
2121
2182
|
const structure = await scanner.scan();
|
|
@@ -2123,14 +2184,14 @@ app.get("/workspace/controllers", async (req, res) => {
|
|
|
2123
2184
|
const controllerAnalyzer = new ControllerAnalyzer(wsRoot);
|
|
2124
2185
|
const apiDocs = await controllerAnalyzer.generateApiDocs(structure.files);
|
|
2125
2186
|
|
|
2126
|
-
console.log(`
|
|
2187
|
+
console.log(`[CONTROLLERS] Found ${apiDocs.totalEndpoints} endpoints in ${apiDocs.totalControllers} controllers`);
|
|
2127
2188
|
|
|
2128
2189
|
res.json({
|
|
2129
2190
|
ok: true,
|
|
2130
2191
|
...apiDocs
|
|
2131
2192
|
});
|
|
2132
2193
|
} catch (err) {
|
|
2133
|
-
console.error("
|
|
2194
|
+
console.error("[CONTROLLERS] Failed:", err.message);
|
|
2134
2195
|
res.status(500).json({ ok: false, error: err.message });
|
|
2135
2196
|
}
|
|
2136
2197
|
});
|
|
@@ -2141,7 +2202,7 @@ app.get("/workspace/dtos", async (req, res) => {
|
|
|
2141
2202
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
2142
2203
|
|
|
2143
2204
|
try {
|
|
2144
|
-
console.log("
|
|
2205
|
+
console.log("[DTOS] Analyzing DTOs...");
|
|
2145
2206
|
|
|
2146
2207
|
const scanner = new WorkspaceScanner(wsRoot);
|
|
2147
2208
|
const structure = await scanner.scan();
|
|
@@ -2152,14 +2213,14 @@ app.get("/workspace/dtos", async (req, res) => {
|
|
|
2152
2213
|
const dtoAnalyzer = new DTOAnalyzer(wsRoot);
|
|
2153
2214
|
const payloadDocs = await dtoAnalyzer.generatePayloadDocs(structure.files, apiDocs.endpoints);
|
|
2154
2215
|
|
|
2155
|
-
console.log(`
|
|
2216
|
+
console.log(`[DTOS] Found ${payloadDocs.totalDtos} DTOs`);
|
|
2156
2217
|
|
|
2157
2218
|
res.json({
|
|
2158
2219
|
ok: true,
|
|
2159
2220
|
...payloadDocs
|
|
2160
2221
|
});
|
|
2161
2222
|
} catch (err) {
|
|
2162
|
-
console.error("
|
|
2223
|
+
console.error("[DTOS] Failed:", err.message);
|
|
2163
2224
|
res.status(500).json({ ok: false, error: err.message });
|
|
2164
2225
|
}
|
|
2165
2226
|
});
|
|
@@ -2170,19 +2231,19 @@ app.get("/workspace/config", async (req, res) => {
|
|
|
2170
2231
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
2171
2232
|
|
|
2172
2233
|
try {
|
|
2173
|
-
console.log("
|
|
2234
|
+
console.log("[CONFIG] Analyzing configuration...");
|
|
2174
2235
|
|
|
2175
2236
|
const configAnalyzer = new ConfigAnalyzer(wsRoot);
|
|
2176
2237
|
const config = await configAnalyzer.analyze();
|
|
2177
2238
|
|
|
2178
|
-
console.log(`
|
|
2239
|
+
console.log(`[CONFIG] Server port: ${config.server.port}`);
|
|
2179
2240
|
|
|
2180
2241
|
res.json({
|
|
2181
2242
|
ok: true,
|
|
2182
2243
|
...config
|
|
2183
2244
|
});
|
|
2184
2245
|
} catch (err) {
|
|
2185
|
-
console.error("
|
|
2246
|
+
console.error("[CONFIG] Failed:", err.message);
|
|
2186
2247
|
res.status(500).json({ ok: false, error: err.message });
|
|
2187
2248
|
}
|
|
2188
2249
|
});
|
|
@@ -2251,7 +2312,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2251
2312
|
const { diff, incidentId } = req.body || {};
|
|
2252
2313
|
if (!diff) return res.status(400).json({ error: "diff is required" });
|
|
2253
2314
|
|
|
2254
|
-
console.log(`
|
|
2315
|
+
console.log(`Safe patch requested for incident: ${incidentId || 'unknown'}`);
|
|
2255
2316
|
|
|
2256
2317
|
try {
|
|
2257
2318
|
// 1. Validate diff
|
|
@@ -2266,7 +2327,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2266
2327
|
|
|
2267
2328
|
// 2. Extract target files
|
|
2268
2329
|
const targetFiles = extractTargetFiles(diff);
|
|
2269
|
-
console.log(`
|
|
2330
|
+
console.log(`Target files: ${targetFiles.join(', ')}`);
|
|
2270
2331
|
|
|
2271
2332
|
// 3. Create backup
|
|
2272
2333
|
const backupId = `backup-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
@@ -2297,23 +2358,23 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2297
2358
|
}
|
|
2298
2359
|
saveBackupIndex();
|
|
2299
2360
|
} catch (e) {
|
|
2300
|
-
console.warn(`
|
|
2361
|
+
console.warn(`Could not persist backup to disk: ${e.message}`);
|
|
2301
2362
|
}
|
|
2302
2363
|
|
|
2303
2364
|
// Cleanup old backups if exceeded max
|
|
2304
2365
|
if (BACKUPS.size > MAX_BACKUPS) {
|
|
2305
2366
|
const oldest = Array.from(BACKUPS.keys())[0];
|
|
2306
2367
|
BACKUPS.delete(oldest);
|
|
2307
|
-
console.log(`
|
|
2368
|
+
console.log(`Removed old backup: ${oldest}`);
|
|
2308
2369
|
saveBackupIndex();
|
|
2309
2370
|
}
|
|
2310
2371
|
|
|
2311
|
-
console.log(`
|
|
2372
|
+
console.log(`Backup created: ${backupId} (${backupFiles.length} files)`);
|
|
2312
2373
|
|
|
2313
2374
|
// 4. Apply patch
|
|
2314
2375
|
try {
|
|
2315
2376
|
const result = await applyUnifiedDiff(wsRoot, diff);
|
|
2316
|
-
console.log(`
|
|
2377
|
+
console.log(`Patch applied successfully: ${result.target}`);
|
|
2317
2378
|
|
|
2318
2379
|
res.json({
|
|
2319
2380
|
ok: true,
|
|
@@ -2324,7 +2385,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2324
2385
|
});
|
|
2325
2386
|
} catch (patchError) {
|
|
2326
2387
|
// 5. Rollback on failure
|
|
2327
|
-
console.error(`
|
|
2388
|
+
console.error(`Patch failed, rolling back: ${patchError.message}`);
|
|
2328
2389
|
|
|
2329
2390
|
for (const file of backupFiles) {
|
|
2330
2391
|
const fullPath = path.join(wsRoot, file.path);
|
|
@@ -2341,7 +2402,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2341
2402
|
});
|
|
2342
2403
|
}
|
|
2343
2404
|
} catch (err) {
|
|
2344
|
-
console.error(`
|
|
2405
|
+
console.error(`Safe patch error: ${err.message}`);
|
|
2345
2406
|
res.status(500).json({ ok: false, error: err.message });
|
|
2346
2407
|
}
|
|
2347
2408
|
});
|
|
@@ -2362,7 +2423,7 @@ app.post("/workspace/patch/dry-run", async (req, res) => {
|
|
|
2362
2423
|
const { diff } = req.body || {};
|
|
2363
2424
|
if (!diff) return res.status(400).json({ error: "diff is required" });
|
|
2364
2425
|
|
|
2365
|
-
console.log(`
|
|
2426
|
+
console.log(`Dry-run patch validation requested`);
|
|
2366
2427
|
|
|
2367
2428
|
try {
|
|
2368
2429
|
// 1. Validate diff format
|
|
@@ -2413,7 +2474,7 @@ app.post("/workspace/patch/dry-run", async (req, res) => {
|
|
|
2413
2474
|
const allFilesExist = fileChecks.every(f => f.exists || diff.includes('--- /dev/null'));
|
|
2414
2475
|
const missingFiles = fileChecks.filter(f => !f.exists && !diff.includes('--- /dev/null'));
|
|
2415
2476
|
|
|
2416
|
-
console.log(`
|
|
2477
|
+
console.log(`Dry-run complete: ${targetFiles.length} files, ${hunkCount} hunks`);
|
|
2417
2478
|
|
|
2418
2479
|
res.json({
|
|
2419
2480
|
ok: true,
|
|
@@ -2426,7 +2487,7 @@ app.post("/workspace/patch/dry-run", async (req, res) => {
|
|
|
2426
2487
|
warnings: missingFiles.length > 0 ? [`${missingFiles.length} file(s) not found`] : []
|
|
2427
2488
|
});
|
|
2428
2489
|
} catch (err) {
|
|
2429
|
-
console.error(`
|
|
2490
|
+
console.error(`Dry-run error: ${err.message}`);
|
|
2430
2491
|
res.status(500).json({ ok: false, error: err.message });
|
|
2431
2492
|
}
|
|
2432
2493
|
});
|
|
@@ -2494,7 +2555,7 @@ app.post("/workspace/backup", async (req, res) => {
|
|
|
2494
2555
|
incidentId: incidentId || null
|
|
2495
2556
|
});
|
|
2496
2557
|
|
|
2497
|
-
console.log(`
|
|
2558
|
+
console.log(`Manual backup created: ${backupId}`);
|
|
2498
2559
|
|
|
2499
2560
|
res.json({
|
|
2500
2561
|
ok: true,
|
|
@@ -2503,7 +2564,7 @@ app.post("/workspace/backup", async (req, res) => {
|
|
|
2503
2564
|
timestamp: new Date().toISOString()
|
|
2504
2565
|
});
|
|
2505
2566
|
} catch (err) {
|
|
2506
|
-
console.error(`
|
|
2567
|
+
console.error(`Backup error: ${err.message}`);
|
|
2507
2568
|
res.status(500).json({ ok: false, error: err.message });
|
|
2508
2569
|
}
|
|
2509
2570
|
});
|
|
@@ -2535,14 +2596,14 @@ app.post("/workspace/rollback", async (req, res) => {
|
|
|
2535
2596
|
}
|
|
2536
2597
|
|
|
2537
2598
|
try {
|
|
2538
|
-
console.log(`
|
|
2599
|
+
console.log(`Rolling back to backup: ${backupId}`);
|
|
2539
2600
|
|
|
2540
2601
|
for (const file of backup.files) {
|
|
2541
2602
|
const fullPath = path.join(wsRoot, file.path);
|
|
2542
2603
|
await writeFile(fullPath, file.content, 'utf8');
|
|
2543
2604
|
}
|
|
2544
2605
|
|
|
2545
|
-
console.log(`
|
|
2606
|
+
console.log(`Rollback completed: ${backup.files.length} files restored`);
|
|
2546
2607
|
|
|
2547
2608
|
res.json({
|
|
2548
2609
|
ok: true,
|
|
@@ -2551,7 +2612,7 @@ app.post("/workspace/rollback", async (req, res) => {
|
|
|
2551
2612
|
timestamp: backup.timestamp
|
|
2552
2613
|
});
|
|
2553
2614
|
} catch (err) {
|
|
2554
|
-
console.error(`
|
|
2615
|
+
console.error(`Rollback error: ${err.message}`);
|
|
2555
2616
|
res.status(500).json({ ok: false, error: err.message });
|
|
2556
2617
|
}
|
|
2557
2618
|
});
|
|
@@ -2591,7 +2652,7 @@ app.delete("/workspace/backups/:backupId", (req, res) => {
|
|
|
2591
2652
|
}
|
|
2592
2653
|
|
|
2593
2654
|
BACKUPS.delete(backupId);
|
|
2594
|
-
console.log(`
|
|
2655
|
+
console.log(`Backup deleted: ${backupId}`);
|
|
2595
2656
|
|
|
2596
2657
|
res.json({
|
|
2597
2658
|
ok: true,
|
|
@@ -2650,7 +2711,7 @@ app.get("/workspace/diff/by-incident/:incidentId", async (req, res) => {
|
|
|
2650
2711
|
}
|
|
2651
2712
|
|
|
2652
2713
|
if (!matchedBackup) {
|
|
2653
|
-
console.log(`
|
|
2714
|
+
console.log(`No backup found for incident ${incidentId}. Available backups:`, Array.from(BACKUPS.keys()));
|
|
2654
2715
|
return res.status(404).json({
|
|
2655
2716
|
ok: false,
|
|
2656
2717
|
error: "No backup found for this incident",
|
|
@@ -2659,7 +2720,7 @@ app.get("/workspace/diff/by-incident/:incidentId", async (req, res) => {
|
|
|
2659
2720
|
});
|
|
2660
2721
|
}
|
|
2661
2722
|
|
|
2662
|
-
console.log(`
|
|
2723
|
+
console.log(`Found backup ${matchedBackupId} for incident ${incidentId}`);
|
|
2663
2724
|
|
|
2664
2725
|
// Reuse the diff logic
|
|
2665
2726
|
try {
|
|
@@ -2696,7 +2757,7 @@ app.get("/workspace/diff/by-incident/:incidentId", async (req, res) => {
|
|
|
2696
2757
|
files: diffs
|
|
2697
2758
|
});
|
|
2698
2759
|
} catch (err) {
|
|
2699
|
-
console.error(`
|
|
2760
|
+
console.error(`Diff error: ${err.message}`);
|
|
2700
2761
|
res.status(500).json({ ok: false, error: err.message });
|
|
2701
2762
|
}
|
|
2702
2763
|
});
|
|
@@ -2768,7 +2829,7 @@ app.get("/workspace/diff/:backupId", async (req, res) => {
|
|
|
2768
2829
|
}
|
|
2769
2830
|
}
|
|
2770
2831
|
|
|
2771
|
-
console.log(`
|
|
2832
|
+
console.log(`Diff for ${backupId}: ${diffs.length} file(s) changed`);
|
|
2772
2833
|
|
|
2773
2834
|
res.json({
|
|
2774
2835
|
ok: true,
|
|
@@ -2780,7 +2841,7 @@ app.get("/workspace/diff/:backupId", async (req, res) => {
|
|
|
2780
2841
|
files: diffs
|
|
2781
2842
|
});
|
|
2782
2843
|
} catch (err) {
|
|
2783
|
-
console.error(`
|
|
2844
|
+
console.error(`Diff error: ${err.message}`);
|
|
2784
2845
|
res.status(500).json({ ok: false, error: err.message });
|
|
2785
2846
|
}
|
|
2786
2847
|
});
|
|
@@ -2813,7 +2874,7 @@ app.get("/workspace/detect-port", async (req, res) => {
|
|
|
2813
2874
|
|
|
2814
2875
|
try {
|
|
2815
2876
|
const fullPath = path.join(wsRoot, servicePath);
|
|
2816
|
-
console.log(`
|
|
2877
|
+
console.log(`Detecting port for service at: ${fullPath}`);
|
|
2817
2878
|
|
|
2818
2879
|
let port = null;
|
|
2819
2880
|
let detectionMethod = null;
|
|
@@ -2848,7 +2909,7 @@ app.get("/workspace/detect-port", async (req, res) => {
|
|
|
2848
2909
|
detectionMethod = 'global-detection';
|
|
2849
2910
|
}
|
|
2850
2911
|
|
|
2851
|
-
console.log(`
|
|
2912
|
+
console.log(`Detected port: ${port || 'default'} via ${detectionMethod}`);
|
|
2852
2913
|
|
|
2853
2914
|
res.json({
|
|
2854
2915
|
ok: true,
|
|
@@ -2859,7 +2920,7 @@ app.get("/workspace/detect-port", async (req, res) => {
|
|
|
2859
2920
|
servicePath: servicePath || '/'
|
|
2860
2921
|
});
|
|
2861
2922
|
} catch (err) {
|
|
2862
|
-
console.error('
|
|
2923
|
+
console.error('Error detecting port:', err.message);
|
|
2863
2924
|
res.status(500).json({ ok: false, error: err.message });
|
|
2864
2925
|
}
|
|
2865
2926
|
});
|
|
@@ -3070,7 +3131,7 @@ app.post("/workspace/test-local/prepare", async (req, res) => {
|
|
|
3070
3131
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
3071
3132
|
|
|
3072
3133
|
try {
|
|
3073
|
-
console.log("
|
|
3134
|
+
console.log("[TEST-LOCAL] Preparing test environment...");
|
|
3074
3135
|
TEST_LOCAL_STATE.status = "compiling";
|
|
3075
3136
|
|
|
3076
3137
|
// Step 1: Compile
|
|
@@ -3110,7 +3171,7 @@ app.post("/workspace/test-local/prepare", async (req, res) => {
|
|
|
3110
3171
|
const config = await configAnalyzer.analyze();
|
|
3111
3172
|
TEST_LOCAL_STATE.config = config;
|
|
3112
3173
|
|
|
3113
|
-
console.log(`
|
|
3174
|
+
console.log(`[TEST-LOCAL] Prepared: ${payloadDocs.endpoints.length} endpoints discovered`);
|
|
3114
3175
|
|
|
3115
3176
|
res.json({
|
|
3116
3177
|
ok: true,
|
|
@@ -3133,7 +3194,7 @@ app.post("/workspace/test-local/prepare", async (req, res) => {
|
|
|
3133
3194
|
endpoints: payloadDocs.endpoints
|
|
3134
3195
|
});
|
|
3135
3196
|
} catch (err) {
|
|
3136
|
-
console.error("
|
|
3197
|
+
console.error("[TEST-LOCAL] Prepare failed:", err.message);
|
|
3137
3198
|
TEST_LOCAL_STATE.status = "error";
|
|
3138
3199
|
res.status(500).json({ ok: false, error: err.message });
|
|
3139
3200
|
}
|
|
@@ -3335,9 +3396,9 @@ app.post("/workspace/scan-files", async (req, res) => {
|
|
|
3335
3396
|
});
|
|
3336
3397
|
}
|
|
3337
3398
|
|
|
3338
|
-
console.log(`
|
|
3339
|
-
console.log(`
|
|
3340
|
-
console.log(`
|
|
3399
|
+
console.log(`[SCAN-FILES] Scanning: ${workspaceRoot}`);
|
|
3400
|
+
console.log(`Extensions: ${includeExtensions.join(", ")}`);
|
|
3401
|
+
console.log(`Exclude: ${excludePatterns.join(", ")}`);
|
|
3341
3402
|
|
|
3342
3403
|
try {
|
|
3343
3404
|
const files = [];
|
|
@@ -3387,13 +3448,13 @@ app.post("/workspace/scan-files", async (req, res) => {
|
|
|
3387
3448
|
}
|
|
3388
3449
|
}
|
|
3389
3450
|
} catch (error) {
|
|
3390
|
-
console.warn(`
|
|
3451
|
+
console.warn(`Cannot read directory ${dir}: ${error.message}`);
|
|
3391
3452
|
}
|
|
3392
3453
|
}
|
|
3393
3454
|
|
|
3394
3455
|
await scanDir(workspaceRoot);
|
|
3395
3456
|
|
|
3396
|
-
console.log(`
|
|
3457
|
+
console.log(`[SCAN-FILES] Found ${files.length} files`);
|
|
3397
3458
|
|
|
3398
3459
|
res.json({
|
|
3399
3460
|
success: true,
|
|
@@ -3403,7 +3464,7 @@ app.post("/workspace/scan-files", async (req, res) => {
|
|
|
3403
3464
|
});
|
|
3404
3465
|
|
|
3405
3466
|
} catch (error) {
|
|
3406
|
-
console.error(`
|
|
3467
|
+
console.error(`[SCAN-FILES] Error: ${error.message}`);
|
|
3407
3468
|
res.status(500).json({
|
|
3408
3469
|
success: false,
|
|
3409
3470
|
error: error.message,
|
|
@@ -3432,7 +3493,7 @@ app.post("/workspace/read-file", async (req, res) => {
|
|
|
3432
3493
|
});
|
|
3433
3494
|
}
|
|
3434
3495
|
|
|
3435
|
-
console.log(`
|
|
3496
|
+
console.log(`[READ-FILE] Reading: ${filePath}`);
|
|
3436
3497
|
|
|
3437
3498
|
try {
|
|
3438
3499
|
const fs = await import("fs/promises");
|
|
@@ -3463,7 +3524,7 @@ app.post("/workspace/read-file", async (req, res) => {
|
|
|
3463
3524
|
});
|
|
3464
3525
|
|
|
3465
3526
|
} catch (error) {
|
|
3466
|
-
console.error(`
|
|
3527
|
+
console.error(`[READ-FILE] Error: ${error.message}`);
|
|
3467
3528
|
res.status(404).json({
|
|
3468
3529
|
success: false,
|
|
3469
3530
|
error: error.message,
|
|
@@ -3492,7 +3553,7 @@ app.post("/workspace/read-files", async (req, res) => {
|
|
|
3492
3553
|
});
|
|
3493
3554
|
}
|
|
3494
3555
|
|
|
3495
|
-
console.log(`
|
|
3556
|
+
console.log(`[READ-FILES] Reading ${filePaths.length} files`);
|
|
3496
3557
|
|
|
3497
3558
|
try {
|
|
3498
3559
|
const fs = await import("fs/promises");
|
|
@@ -3519,7 +3580,7 @@ app.post("/workspace/read-files", async (req, res) => {
|
|
|
3519
3580
|
}
|
|
3520
3581
|
}
|
|
3521
3582
|
|
|
3522
|
-
console.log(`
|
|
3583
|
+
console.log(`[READ-FILES] Read ${Object.keys(files).length} files, ${errors.length} errors`);
|
|
3523
3584
|
|
|
3524
3585
|
res.json({
|
|
3525
3586
|
success: true,
|
|
@@ -3529,7 +3590,7 @@ app.post("/workspace/read-files", async (req, res) => {
|
|
|
3529
3590
|
});
|
|
3530
3591
|
|
|
3531
3592
|
} catch (error) {
|
|
3532
|
-
console.error(`
|
|
3593
|
+
console.error(`[READ-FILES] Error: ${error.message}`);
|
|
3533
3594
|
res.status(500).json({
|
|
3534
3595
|
success: false,
|
|
3535
3596
|
error: error.message
|
|
@@ -3547,7 +3608,7 @@ app.post("/workspace/read-files", async (req, res) => {
|
|
|
3547
3608
|
* Abre o file picker nativo do sistema operacional
|
|
3548
3609
|
*/
|
|
3549
3610
|
app.get("/system/folder-picker", async (req, res) => {
|
|
3550
|
-
console.log(`
|
|
3611
|
+
console.log(`[FOLDER-PICKER] Opening native folder picker...`);
|
|
3551
3612
|
|
|
3552
3613
|
try {
|
|
3553
3614
|
const platform = process.platform;
|
|
@@ -3630,7 +3691,7 @@ app.get("/system/folder-picker", async (req, res) => {
|
|
|
3630
3691
|
selectedPath = selectedPath.slice(0, -1);
|
|
3631
3692
|
}
|
|
3632
3693
|
|
|
3633
|
-
console.log(`
|
|
3694
|
+
console.log(`[FOLDER-PICKER] Selected: ${selectedPath}`);
|
|
3634
3695
|
|
|
3635
3696
|
res.json({
|
|
3636
3697
|
success: true,
|
|
@@ -3639,7 +3700,7 @@ app.get("/system/folder-picker", async (req, res) => {
|
|
|
3639
3700
|
});
|
|
3640
3701
|
|
|
3641
3702
|
} catch (error) {
|
|
3642
|
-
console.error(`
|
|
3703
|
+
console.error(`[FOLDER-PICKER] Error:`, error.message);
|
|
3643
3704
|
res.status(500).json({
|
|
3644
3705
|
success: false,
|
|
3645
3706
|
error: error.message,
|
|
@@ -3681,7 +3742,7 @@ app.get("/workspace/api-docs", async (req, res) => {
|
|
|
3681
3742
|
});
|
|
3682
3743
|
}
|
|
3683
3744
|
|
|
3684
|
-
console.log(`
|
|
3745
|
+
console.log(`[API-DOCS] Analyzing controllers in ${wsRoot}`);
|
|
3685
3746
|
|
|
3686
3747
|
try {
|
|
3687
3748
|
// Primeiro, escanear arquivos do workspace
|
|
@@ -3720,13 +3781,13 @@ app.get("/workspace/api-docs", async (req, res) => {
|
|
|
3720
3781
|
|
|
3721
3782
|
await scanDir(wsRoot);
|
|
3722
3783
|
|
|
3723
|
-
console.log(`
|
|
3784
|
+
console.log(`[API-DOCS] Found ${files.length} Java files`);
|
|
3724
3785
|
|
|
3725
3786
|
// Agora analisar os controllers
|
|
3726
3787
|
const controllerAnalyzer = new ControllerAnalyzer(wsRoot);
|
|
3727
3788
|
const apiDocs = await controllerAnalyzer.generateApiDocs(files);
|
|
3728
3789
|
|
|
3729
|
-
console.log(`
|
|
3790
|
+
console.log(`[API-DOCS] Found ${apiDocs.totalEndpoints} endpoints in ${apiDocs.totalControllers} controllers`);
|
|
3730
3791
|
|
|
3731
3792
|
res.json({
|
|
3732
3793
|
success: true,
|
|
@@ -3738,7 +3799,7 @@ app.get("/workspace/api-docs", async (req, res) => {
|
|
|
3738
3799
|
});
|
|
3739
3800
|
|
|
3740
3801
|
} catch (error) {
|
|
3741
|
-
console.error(`
|
|
3802
|
+
console.error(`[API-DOCS] Error:`, error.message);
|
|
3742
3803
|
|
|
3743
3804
|
res.json({
|
|
3744
3805
|
success: false,
|
|
@@ -3765,7 +3826,7 @@ app.get("/workspace/api-docs/:controller", async (req, res) => {
|
|
|
3765
3826
|
}
|
|
3766
3827
|
|
|
3767
3828
|
const { controller } = req.params;
|
|
3768
|
-
console.log(`
|
|
3829
|
+
console.log(`[API-DOCS] Getting endpoints for controller: ${controller}`);
|
|
3769
3830
|
|
|
3770
3831
|
try {
|
|
3771
3832
|
// Primeiro, escanear arquivos do workspace
|
|
@@ -3840,7 +3901,7 @@ app.get("/workspace/api-docs/:controller", async (req, res) => {
|
|
|
3840
3901
|
});
|
|
3841
3902
|
|
|
3842
3903
|
} catch (error) {
|
|
3843
|
-
console.error(`
|
|
3904
|
+
console.error(`[API-DOCS] Error:`, error.message);
|
|
3844
3905
|
res.status(500).json({
|
|
3845
3906
|
success: false,
|
|
3846
3907
|
error: error.message
|
|
@@ -3930,7 +3991,7 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3930
3991
|
return res.status(400).json({ error: "workspace not set" });
|
|
3931
3992
|
}
|
|
3932
3993
|
|
|
3933
|
-
console.log("
|
|
3994
|
+
console.log("[SMART-CONFIG] Collecting configuration files...");
|
|
3934
3995
|
|
|
3935
3996
|
try {
|
|
3936
3997
|
const configPatterns = [
|
|
@@ -3964,9 +4025,9 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3964
4025
|
: content,
|
|
3965
4026
|
size: content.length
|
|
3966
4027
|
});
|
|
3967
|
-
console.log(`
|
|
4028
|
+
console.log(`Found: ${pattern}`);
|
|
3968
4029
|
} catch (err) {
|
|
3969
|
-
console.error(`
|
|
4030
|
+
console.error(`Error reading ${pattern}: ${err.message}`);
|
|
3970
4031
|
}
|
|
3971
4032
|
}
|
|
3972
4033
|
}
|
|
@@ -3982,7 +4043,7 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3982
4043
|
});
|
|
3983
4044
|
}
|
|
3984
4045
|
|
|
3985
|
-
console.log(`
|
|
4046
|
+
console.log(`Collected ${collectedFiles.length} files, profiles: ${availableProfiles.join(', ')}`);
|
|
3986
4047
|
|
|
3987
4048
|
res.json({
|
|
3988
4049
|
ok: true,
|
|
@@ -3996,7 +4057,7 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3996
4057
|
});
|
|
3997
4058
|
|
|
3998
4059
|
} catch (err) {
|
|
3999
|
-
console.error("
|
|
4060
|
+
console.error("[SMART-CONFIG] Error:", err.message);
|
|
4000
4061
|
res.status(500).json({ ok: false, error: err.message });
|
|
4001
4062
|
}
|
|
4002
4063
|
});
|
|
@@ -4007,7 +4068,7 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
4007
4068
|
*/
|
|
4008
4069
|
app.post("/workspace/test-local/restart", async (req, res) => {
|
|
4009
4070
|
try {
|
|
4010
|
-
console.log("
|
|
4071
|
+
console.log("[TEST-LOCAL] Restarting server...");
|
|
4011
4072
|
|
|
4012
4073
|
// Stop
|
|
4013
4074
|
await processManager.stop('test-local');
|
|
@@ -4052,7 +4113,7 @@ app.post("/workspace/test-local/restart", async (req, res) => {
|
|
|
4052
4113
|
}
|
|
4053
4114
|
|
|
4054
4115
|
} catch (err) {
|
|
4055
|
-
console.error("
|
|
4116
|
+
console.error("[TEST-LOCAL] Restart failed:", err.message);
|
|
4056
4117
|
res.status(500).json({ ok: false, error: err.message });
|
|
4057
4118
|
}
|
|
4058
4119
|
});
|
|
@@ -4086,7 +4147,7 @@ app.post("/workspace/search", async (req, res) => {
|
|
|
4086
4147
|
return res.status(400).json({ ok: false, error: "pattern is required" });
|
|
4087
4148
|
}
|
|
4088
4149
|
|
|
4089
|
-
console.log(`
|
|
4150
|
+
console.log(`[workspace/search] pattern="${pattern}" filter="${fileFilter || '*'}"`);
|
|
4090
4151
|
|
|
4091
4152
|
try {
|
|
4092
4153
|
const { execSync } = await import('child_process');
|
|
@@ -4144,7 +4205,7 @@ app.post("/workspace/search", async (req, res) => {
|
|
|
4144
4205
|
.join('\n');
|
|
4145
4206
|
|
|
4146
4207
|
const count = results.split('\n').filter(l => l.trim()).length;
|
|
4147
|
-
console.log(`
|
|
4208
|
+
console.log(`Found ${count} matches`);
|
|
4148
4209
|
|
|
4149
4210
|
res.json({
|
|
4150
4211
|
ok: true,
|
|
@@ -4152,7 +4213,7 @@ app.post("/workspace/search", async (req, res) => {
|
|
|
4152
4213
|
count: count
|
|
4153
4214
|
});
|
|
4154
4215
|
} catch (err) {
|
|
4155
|
-
console.error(`
|
|
4216
|
+
console.error(`[workspace/search] Error:`, err.message);
|
|
4156
4217
|
res.status(500).json({ ok: false, error: err.message });
|
|
4157
4218
|
}
|
|
4158
4219
|
});
|
|
@@ -4192,14 +4253,14 @@ app.post("/workspace/exec", async (req, res) => {
|
|
|
4192
4253
|
];
|
|
4193
4254
|
const lowerCmd = command.toLowerCase().trim();
|
|
4194
4255
|
if (dangerous.some(d => lowerCmd.includes(d))) {
|
|
4195
|
-
console.warn(`
|
|
4256
|
+
console.warn(`[workspace/exec] BLOCKED dangerous command: ${command}`);
|
|
4196
4257
|
return res.status(403).json({
|
|
4197
4258
|
ok: false,
|
|
4198
4259
|
error: "Command blocked for security reasons"
|
|
4199
4260
|
});
|
|
4200
4261
|
}
|
|
4201
4262
|
|
|
4202
|
-
console.log(`
|
|
4263
|
+
console.log(`[workspace/exec] Running: ${command.substring(0, 120)}...`);
|
|
4203
4264
|
|
|
4204
4265
|
try {
|
|
4205
4266
|
const { exec: execCb } = await import('child_process');
|
|
@@ -4213,7 +4274,7 @@ app.post("/workspace/exec", async (req, res) => {
|
|
|
4213
4274
|
env: { ...process.env, FORCE_COLOR: '0' }
|
|
4214
4275
|
});
|
|
4215
4276
|
|
|
4216
|
-
console.log(`
|
|
4277
|
+
console.log(`Command completed (stdout: ${result.stdout.length} chars)`);
|
|
4217
4278
|
|
|
4218
4279
|
res.json({
|
|
4219
4280
|
ok: true,
|
|
@@ -4222,7 +4283,7 @@ app.post("/workspace/exec", async (req, res) => {
|
|
|
4222
4283
|
exitCode: 0
|
|
4223
4284
|
});
|
|
4224
4285
|
} catch (err) {
|
|
4225
|
-
console.error(`
|
|
4286
|
+
console.error(`Command failed (exit: ${err.code}):`, err.message?.substring(0, 200));
|
|
4226
4287
|
res.json({
|
|
4227
4288
|
ok: false,
|
|
4228
4289
|
stdout: err.stdout || '',
|
|
@@ -4255,7 +4316,7 @@ app.post("/workspace/test-endpoint", async (req, res) => {
|
|
|
4255
4316
|
return res.status(403).json({ error: "Only localhost URLs allowed for security" });
|
|
4256
4317
|
}
|
|
4257
4318
|
|
|
4258
|
-
console.log(`
|
|
4319
|
+
console.log(`Testing: ${method} ${url} (${testName || 'unnamed'})`);
|
|
4259
4320
|
|
|
4260
4321
|
const startTime = Date.now();
|
|
4261
4322
|
|
|
@@ -4305,7 +4366,7 @@ app.post("/workspace/test-endpoint", async (req, res) => {
|
|
|
4305
4366
|
: response.status >= 200 && response.status < 400
|
|
4306
4367
|
};
|
|
4307
4368
|
|
|
4308
|
-
console.log(
|
|
4369
|
+
console.log(`${response.status} ${response.statusText} (${durationMs}ms) ${result.passed ? '' : ''}`);
|
|
4309
4370
|
res.json(result);
|
|
4310
4371
|
|
|
4311
4372
|
} catch (err) {
|
|
@@ -4381,7 +4442,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4381
4442
|
try {
|
|
4382
4443
|
const stashResult = execSync('git stash --include-untracked 2>&1', opts).trim();
|
|
4383
4444
|
hadStash = !stashResult.includes('No local changes');
|
|
4384
|
-
if (hadStash) console.log('
|
|
4445
|
+
if (hadStash) console.log('Stashed uncommitted changes');
|
|
4385
4446
|
} catch {}
|
|
4386
4447
|
|
|
4387
4448
|
// 4. Create and checkout new branch (with unique name if exists)
|
|
@@ -4405,7 +4466,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4405
4466
|
try {
|
|
4406
4467
|
execSync('git stash pop', opts);
|
|
4407
4468
|
} catch (e) {
|
|
4408
|
-
console.warn('
|
|
4469
|
+
console.warn('Stash pop had conflicts, trying apply:', e.message);
|
|
4409
4470
|
try { execSync('git stash apply', opts); } catch {}
|
|
4410
4471
|
}
|
|
4411
4472
|
}
|
|
@@ -4427,6 +4488,13 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4427
4488
|
}
|
|
4428
4489
|
|
|
4429
4490
|
// 8. Commit
|
|
4491
|
+
// Ensure git identity is configured (required in Cloud Run environment)
|
|
4492
|
+
try {
|
|
4493
|
+
execSync('git config user.email "ai@deepdebug.ai"', opts);
|
|
4494
|
+
execSync('git config user.name "DeepDebug AI"', opts);
|
|
4495
|
+
} catch (e) {
|
|
4496
|
+
console.warn('Could not set git config:', e.message);
|
|
4497
|
+
}
|
|
4430
4498
|
const safeMsg = commitMessage.replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
4431
4499
|
execSync(`git commit -m "${safeMsg}"`, opts);
|
|
4432
4500
|
|
|
@@ -4434,7 +4502,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4434
4502
|
let pushResult = '';
|
|
4435
4503
|
let pushed = false;
|
|
4436
4504
|
|
|
4437
|
-
console.log(`
|
|
4505
|
+
console.log(`Git push gitToken provided: ${!!gitToken}, repoUrl provided: ${!!repoUrl}`);
|
|
4438
4506
|
|
|
4439
4507
|
// If gitToken provided, set up authenticated remote URL for push
|
|
4440
4508
|
if (gitToken) {
|
|
@@ -4446,11 +4514,11 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4446
4514
|
// No remote configured use repoUrl from integration if available
|
|
4447
4515
|
if (repoUrl) {
|
|
4448
4516
|
remoteUrlRaw = repoUrl;
|
|
4449
|
-
console.log(`
|
|
4517
|
+
console.log(`No git remote, using repoUrl from integration: ${repoUrl}`);
|
|
4450
4518
|
}
|
|
4451
4519
|
}
|
|
4452
4520
|
|
|
4453
|
-
console.log(`
|
|
4521
|
+
console.log(`Remote URL: ${remoteUrlRaw.replace(gitToken, '***')}`);
|
|
4454
4522
|
let authenticatedUrl = '';
|
|
4455
4523
|
|
|
4456
4524
|
if (remoteUrlRaw.includes('github.com')) {
|
|
@@ -4459,7 +4527,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4459
4527
|
.replace(/^git@github\.com:/, '')
|
|
4460
4528
|
.replace(/\.git$/, '');
|
|
4461
4529
|
authenticatedUrl = `https://x-access-token:${gitToken}@github.com/${repoPath}.git`;
|
|
4462
|
-
console.log(`
|
|
4530
|
+
console.log(`GitHub auth URL: https://x-access-token:***@github.com/${repoPath}.git`);
|
|
4463
4531
|
} else if (remoteUrlRaw.includes('gitlab.com') || remoteUrlRaw.includes('gitlab')) {
|
|
4464
4532
|
const repoPath = remoteUrlRaw
|
|
4465
4533
|
.replace(/^https?:\/\/(.*@)?[^\/]+\//, '')
|
|
@@ -4467,7 +4535,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4467
4535
|
.replace(/\.git$/, '');
|
|
4468
4536
|
const host = remoteUrlRaw.match(/https?:\/\/([^\/]+)/)?.[1] || 'gitlab.com';
|
|
4469
4537
|
authenticatedUrl = `https://oauth2:${gitToken}@${host}/${repoPath}.git`;
|
|
4470
|
-
console.log(`
|
|
4538
|
+
console.log(`GitLab auth URL: https://oauth2:***@${host}/${repoPath}.git`);
|
|
4471
4539
|
} else if (remoteUrlRaw.includes('bitbucket.org') || remoteUrlRaw.includes('bitbucket')) {
|
|
4472
4540
|
const repoPath = remoteUrlRaw
|
|
4473
4541
|
.replace(/^https?:\/\/(.*@)?bitbucket\.org\//, '')
|
|
@@ -4481,11 +4549,11 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4481
4549
|
const urlObj = new URL(remoteUrlRaw.replace(/^git@([^:]+):/, 'https://$1/'));
|
|
4482
4550
|
authenticatedUrl = `https://oauth2:${gitToken}@${urlObj.host}${urlObj.pathname}`;
|
|
4483
4551
|
if (!authenticatedUrl.endsWith('.git')) authenticatedUrl += '.git';
|
|
4484
|
-
console.log(`
|
|
4552
|
+
console.log(`Generic auth URL for ${urlObj.host}`);
|
|
4485
4553
|
}
|
|
4486
4554
|
|
|
4487
4555
|
if (authenticatedUrl) {
|
|
4488
|
-
console.log(`
|
|
4556
|
+
console.log(`Pushing ${finalBranchName} with token auth...`);
|
|
4489
4557
|
try {
|
|
4490
4558
|
// Don't use 2>&1 let execSync capture stderr separately
|
|
4491
4559
|
pushResult = execSync(`git push ${authenticatedUrl} ${finalBranchName}`, {
|
|
@@ -4494,20 +4562,20 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4494
4562
|
stdio: ['pipe', 'pipe', 'pipe'] // capture stdin, stdout, stderr separately
|
|
4495
4563
|
}).toString();
|
|
4496
4564
|
pushed = true;
|
|
4497
|
-
console.log(`
|
|
4565
|
+
console.log(`Pushed with token authentication`);
|
|
4498
4566
|
} catch (innerErr) {
|
|
4499
4567
|
// Mask token in error messages before logging
|
|
4500
4568
|
const maskToken = (str) => str ? str.replace(gitToken, '***TOKEN***') : '';
|
|
4501
4569
|
const errMsg = maskToken(innerErr.stderr?.toString() || innerErr.stdout?.toString() || innerErr.message || '');
|
|
4502
|
-
console.warn(`
|
|
4570
|
+
console.warn(`Authenticated push failed: ${errMsg}`);
|
|
4503
4571
|
pushResult = errMsg;
|
|
4504
4572
|
}
|
|
4505
4573
|
} else {
|
|
4506
|
-
console.warn(`
|
|
4574
|
+
console.warn(`Could not construct authenticated URL from: ${remoteUrlRaw}`);
|
|
4507
4575
|
}
|
|
4508
4576
|
} catch (pushErr) {
|
|
4509
4577
|
const maskToken = (str) => str ? str.replace(gitToken, '***TOKEN***') : '';
|
|
4510
|
-
console.warn(`
|
|
4578
|
+
console.warn(`Git auth setup failed: ${maskToken(pushErr.message)}`);
|
|
4511
4579
|
pushResult = maskToken(pushErr.message) || 'Push setup failed';
|
|
4512
4580
|
}
|
|
4513
4581
|
}
|
|
@@ -4522,7 +4590,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4522
4590
|
pushResult = execSync(`git push origin ${finalBranchName} 2>&1`, opts).toString();
|
|
4523
4591
|
pushed = true;
|
|
4524
4592
|
} catch (pushErr2) {
|
|
4525
|
-
console.warn(`
|
|
4593
|
+
console.warn(`Git push failed: ${pushErr2.message}`);
|
|
4526
4594
|
pushResult = pushErr2.message || 'Push failed no remote configured or auth required';
|
|
4527
4595
|
}
|
|
4528
4596
|
}
|
|
@@ -4581,8 +4649,8 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4581
4649
|
console.warn('Could not detect remote URL:', e.message);
|
|
4582
4650
|
}
|
|
4583
4651
|
|
|
4584
|
-
console.log(`
|
|
4585
|
-
if (mrUrl) console.log(`
|
|
4652
|
+
console.log(`Fix branch created: ${finalBranchName} (${commitSha.substring(0, 8)}) pushed=${pushed}`);
|
|
4653
|
+
if (mrUrl) console.log(`MR URL: ${mrUrl}`);
|
|
4586
4654
|
|
|
4587
4655
|
// 10. Switch back to original branch and merge fix to keep changes on disk
|
|
4588
4656
|
try {
|
|
@@ -4591,7 +4659,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4591
4659
|
// This keeps the diff viewer working (disk has patched version)
|
|
4592
4660
|
try {
|
|
4593
4661
|
execSync(`git merge ${finalBranchName} --no-edit`, opts);
|
|
4594
|
-
console.log(`
|
|
4662
|
+
console.log(`Merged ${finalBranchName} into ${currentBranch}`);
|
|
4595
4663
|
} catch (mergeErr) {
|
|
4596
4664
|
// If merge fails (conflicts), just cherry-pick the changes without committing
|
|
4597
4665
|
try {
|
|
@@ -4605,7 +4673,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4605
4673
|
execSync(`git checkout ${finalBranchName} -- ${f}`, opts);
|
|
4606
4674
|
} catch {}
|
|
4607
4675
|
}
|
|
4608
|
-
console.log(`
|
|
4676
|
+
console.log(`Cherry-picked ${changedFiles.length} file(s) from ${finalBranchName}`);
|
|
4609
4677
|
} catch {}
|
|
4610
4678
|
}
|
|
4611
4679
|
} catch {}
|
|
@@ -4652,14 +4720,14 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4652
4720
|
if (response.ok) {
|
|
4653
4721
|
const prData = await response.json();
|
|
4654
4722
|
prUrl = prData.html_url;
|
|
4655
|
-
console.log(`
|
|
4723
|
+
console.log(`GitHub PR created: ${prUrl}`);
|
|
4656
4724
|
} else {
|
|
4657
4725
|
const errText = await response.text();
|
|
4658
|
-
console.warn(`
|
|
4726
|
+
console.warn(`GitHub PR creation failed (${response.status}): ${errText.substring(0, 200)}`);
|
|
4659
4727
|
prUrl = mrUrl;
|
|
4660
4728
|
}
|
|
4661
4729
|
} catch (prErr) {
|
|
4662
|
-
console.warn(`
|
|
4730
|
+
console.warn(`GitHub PR error: ${prErr.message}`);
|
|
4663
4731
|
prUrl = mrUrl;
|
|
4664
4732
|
}
|
|
4665
4733
|
|
|
@@ -4693,14 +4761,14 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4693
4761
|
if (response.ok) {
|
|
4694
4762
|
const mrData = await response.json();
|
|
4695
4763
|
prUrl = mrData.web_url;
|
|
4696
|
-
console.log(`
|
|
4764
|
+
console.log(`GitLab MR created: ${prUrl}`);
|
|
4697
4765
|
} else {
|
|
4698
4766
|
const errText = await response.text();
|
|
4699
|
-
console.warn(`
|
|
4767
|
+
console.warn(`GitLab MR creation failed (${response.status}): ${errText.substring(0, 200)}`);
|
|
4700
4768
|
prUrl = mrUrl;
|
|
4701
4769
|
}
|
|
4702
4770
|
} catch (glErr) {
|
|
4703
|
-
console.warn(`
|
|
4771
|
+
console.warn(`GitLab MR error: ${glErr.message}`);
|
|
4704
4772
|
prUrl = mrUrl;
|
|
4705
4773
|
}
|
|
4706
4774
|
|
|
@@ -4753,14 +4821,14 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4753
4821
|
if (response.ok) {
|
|
4754
4822
|
const prData = await response.json();
|
|
4755
4823
|
prUrl = prData.links && prData.links.html ? prData.links.html.href : mrUrl;
|
|
4756
|
-
console.log(`
|
|
4824
|
+
console.log(`Bitbucket PR created: ${prUrl}`);
|
|
4757
4825
|
} else {
|
|
4758
4826
|
const errText = await response.text();
|
|
4759
|
-
console.warn(`
|
|
4827
|
+
console.warn(`Bitbucket PR creation failed (${response.status}): ${errText.substring(0, 200)}`);
|
|
4760
4828
|
prUrl = mrUrl;
|
|
4761
4829
|
}
|
|
4762
4830
|
} catch (bbErr) {
|
|
4763
|
-
console.warn(`
|
|
4831
|
+
console.warn(`Bitbucket PR error: ${bbErr.message}`);
|
|
4764
4832
|
prUrl = mrUrl;
|
|
4765
4833
|
}
|
|
4766
4834
|
}
|
|
@@ -4783,7 +4851,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4783
4851
|
});
|
|
4784
4852
|
|
|
4785
4853
|
} catch (err) {
|
|
4786
|
-
console.error(`
|
|
4854
|
+
console.error(`Git branch creation failed: ${err.message}`);
|
|
4787
4855
|
res.status(500).json({ ok: false, error: err.message });
|
|
4788
4856
|
}
|
|
4789
4857
|
});
|
|
@@ -4895,7 +4963,7 @@ app.get("/workspace/git/pr/comments", async (req, res) => {
|
|
|
4895
4963
|
unresolvedCount: comments.filter(c => c.resolved === false || c.resolved === undefined).length
|
|
4896
4964
|
});
|
|
4897
4965
|
} catch (err) {
|
|
4898
|
-
console.error('
|
|
4966
|
+
console.error('Failed to fetch PR comments:', err.message);
|
|
4899
4967
|
res.status(500).json({ ok: false, error: err.message });
|
|
4900
4968
|
}
|
|
4901
4969
|
});
|
|
@@ -4932,7 +5000,7 @@ app.get("/workspace/git/pr/comments/:commentId", async (req, res) => {
|
|
|
4932
5000
|
|
|
4933
5001
|
res.json({ ok: true, comment });
|
|
4934
5002
|
} catch (err) {
|
|
4935
|
-
console.error('
|
|
5003
|
+
console.error('Failed to fetch PR comment:', err.message);
|
|
4936
5004
|
res.status(500).json({ ok: false, error: err.message });
|
|
4937
5005
|
}
|
|
4938
5006
|
});
|
|
@@ -4994,10 +5062,10 @@ app.post("/workspace/git/pr/comments", async (req, res) => {
|
|
|
4994
5062
|
comment = await provider.instance.addPRComment(owner, repo, Number(prNumber), body);
|
|
4995
5063
|
}
|
|
4996
5064
|
|
|
4997
|
-
console.log(`
|
|
5065
|
+
console.log(`PR comment added to PR #${prNumber}: ${body.substring(0, 50)}...`);
|
|
4998
5066
|
res.json({ ok: true, comment });
|
|
4999
5067
|
} catch (err) {
|
|
5000
|
-
console.error('
|
|
5068
|
+
console.error('Failed to add PR comment:', err.message);
|
|
5001
5069
|
res.status(500).json({ ok: false, error: err.message });
|
|
5002
5070
|
}
|
|
5003
5071
|
});
|
|
@@ -5032,10 +5100,10 @@ app.post("/workspace/git/pr/comments/:commentId/resolve", async (req, res) => {
|
|
|
5032
5100
|
const { owner, repo } = provider.info;
|
|
5033
5101
|
const result = await provider.instance.resolvePRComment(owner, repo, Number(prNumber), commentId);
|
|
5034
5102
|
|
|
5035
|
-
console.log(`
|
|
5103
|
+
console.log(`PR comment ${commentId} resolved on PR #${prNumber}`);
|
|
5036
5104
|
res.json({ ok: true, commentId, ...result });
|
|
5037
5105
|
} catch (err) {
|
|
5038
|
-
console.error('
|
|
5106
|
+
console.error('Failed to resolve PR comment:', err.message);
|
|
5039
5107
|
res.status(500).json({ ok: false, error: err.message });
|
|
5040
5108
|
}
|
|
5041
5109
|
});
|
|
@@ -5070,10 +5138,10 @@ app.post("/workspace/git/pr/comments/:commentId/unresolve", async (req, res) =>
|
|
|
5070
5138
|
const { owner, repo } = provider.info;
|
|
5071
5139
|
const result = await provider.instance.unresolvePRComment(owner, repo, Number(prNumber), commentId);
|
|
5072
5140
|
|
|
5073
|
-
console.log(`
|
|
5141
|
+
console.log(`PR comment ${commentId} unresolve on PR #${prNumber}`);
|
|
5074
5142
|
res.json({ ok: true, commentId, ...result });
|
|
5075
5143
|
} catch (err) {
|
|
5076
|
-
console.error('
|
|
5144
|
+
console.error('Failed to unresolve PR comment:', err.message);
|
|
5077
5145
|
res.status(500).json({ ok: false, error: err.message });
|
|
5078
5146
|
}
|
|
5079
5147
|
});
|
|
@@ -5118,9 +5186,9 @@ app.post("/workspace/git/pr/comments/:commentId/fix-and-resolve", async (req, re
|
|
|
5118
5186
|
return res.status(404).json({ error: "Comment not found" });
|
|
5119
5187
|
}
|
|
5120
5188
|
|
|
5121
|
-
console.log(`
|
|
5122
|
-
console.log(`
|
|
5123
|
-
console.log(`
|
|
5189
|
+
console.log(`AI Fix & Resolve: PR #${prNumber}, comment ${commentId}`);
|
|
5190
|
+
console.log(`File: ${comment.path || '(general)'}, Line: ${comment.line || 'N/A'}`);
|
|
5191
|
+
console.log(`Request: ${comment.body.substring(0, 100)}...`);
|
|
5124
5192
|
|
|
5125
5193
|
// 2. Return the comment info for the Gateway to orchestrate the AI fix
|
|
5126
5194
|
// The actual AI analysis + patch is handled by the Gateway's AI service,
|
|
@@ -5150,7 +5218,7 @@ app.post("/workspace/git/pr/comments/:commentId/fix-and-resolve", async (req, re
|
|
|
5150
5218
|
message: 'Comment fetched. Gateway should now: 1) Read file, 2) AI analyze, 3) Apply patch, 4) Commit, 5) Reply, 6) Resolve'
|
|
5151
5219
|
});
|
|
5152
5220
|
} catch (err) {
|
|
5153
|
-
console.error('
|
|
5221
|
+
console.error('Failed to prepare fix-and-resolve:', err.message);
|
|
5154
5222
|
res.status(500).json({ ok: false, error: err.message });
|
|
5155
5223
|
}
|
|
5156
5224
|
});
|
|
@@ -5173,7 +5241,7 @@ async function _getGitProvider() {
|
|
|
5173
5241
|
try {
|
|
5174
5242
|
remoteUrl = execSync('git remote get-url origin', opts).trim();
|
|
5175
5243
|
} catch {
|
|
5176
|
-
console.warn('
|
|
5244
|
+
console.warn('No git remote found');
|
|
5177
5245
|
return null;
|
|
5178
5246
|
}
|
|
5179
5247
|
|
|
@@ -5183,7 +5251,7 @@ async function _getGitProvider() {
|
|
|
5183
5251
|
const providerId = registry.detectProviderFromUrl(remoteUrl);
|
|
5184
5252
|
|
|
5185
5253
|
if (!providerId) {
|
|
5186
|
-
console.warn(`
|
|
5254
|
+
console.warn(`Unknown git provider for URL: ${remoteUrl}`);
|
|
5187
5255
|
return null;
|
|
5188
5256
|
}
|
|
5189
5257
|
|
|
@@ -5198,7 +5266,7 @@ async function _getGitProvider() {
|
|
|
5198
5266
|
}
|
|
5199
5267
|
|
|
5200
5268
|
if (!token) {
|
|
5201
|
-
console.warn(`
|
|
5269
|
+
console.warn(`No token found for ${providerId}. Set ${providerId.toUpperCase()}_TOKEN or GIT_TOKEN env var.`);
|
|
5202
5270
|
return null;
|
|
5203
5271
|
}
|
|
5204
5272
|
|
|
@@ -5218,7 +5286,7 @@ async function _getGitProvider() {
|
|
|
5218
5286
|
|
|
5219
5287
|
return { instance, info, providerId, remoteUrl };
|
|
5220
5288
|
} catch (err) {
|
|
5221
|
-
console.error('
|
|
5289
|
+
console.error('Failed to initialize git provider:', err.message);
|
|
5222
5290
|
return null;
|
|
5223
5291
|
}
|
|
5224
5292
|
}
|
|
@@ -5308,7 +5376,7 @@ app.post("/workspace/:workspaceId/run", async (req, res) => {
|
|
|
5308
5376
|
// ============================================
|
|
5309
5377
|
async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
5310
5378
|
if (!gatewayUrl || !apiKey || !tenantId) {
|
|
5311
|
-
console.log('
|
|
5379
|
+
console.log('WebSocket tunnel skipped: missing gatewayUrl, apiKey or tenantId in ~/.deepdebug/config.json');
|
|
5312
5380
|
return;
|
|
5313
5381
|
}
|
|
5314
5382
|
|
|
@@ -5326,7 +5394,7 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5326
5394
|
ws = new WebSocket(fullUrl);
|
|
5327
5395
|
|
|
5328
5396
|
ws.on('open', () => {
|
|
5329
|
-
console.log(`
|
|
5397
|
+
console.log(`WebSocket tunnel connected to Gateway`);
|
|
5330
5398
|
reconnectDelay = 2000;
|
|
5331
5399
|
ws.send(JSON.stringify({ type: 'register', tenantId, port, workspacePath: WORKSPACE_ROOT || null }));
|
|
5332
5400
|
const hb = setInterval(() => {
|
|
@@ -5342,7 +5410,7 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5342
5410
|
try { request = JSON.parse(text); } catch { return; }
|
|
5343
5411
|
const { requestId, command, params } = request;
|
|
5344
5412
|
if (!requestId || !command) return;
|
|
5345
|
-
console.log(`
|
|
5413
|
+
console.log(`WS command: ${command}`);
|
|
5346
5414
|
let result;
|
|
5347
5415
|
try { result = await handleWsCommand(command, params || {}); }
|
|
5348
5416
|
catch (err) { result = { error: err.message }; }
|
|
@@ -5351,19 +5419,19 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5351
5419
|
|
|
5352
5420
|
ws.on('close', () => {
|
|
5353
5421
|
if (!isShuttingDown) {
|
|
5354
|
-
console.log(`
|
|
5422
|
+
console.log(`WS disconnected. Reconnecting in ${reconnectDelay/1000}s...`);
|
|
5355
5423
|
setTimeout(connect, reconnectDelay);
|
|
5356
5424
|
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
|
|
5357
5425
|
}
|
|
5358
5426
|
});
|
|
5359
5427
|
|
|
5360
|
-
ws.on('error', (err) => console.warn(`
|
|
5428
|
+
ws.on('error', (err) => console.warn(`WS error: ${err.message}`));
|
|
5361
5429
|
|
|
5362
5430
|
} catch (err) {
|
|
5363
5431
|
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
5364
|
-
console.warn('
|
|
5432
|
+
console.warn('WebSocket tunnel requires "ws" package. Run: npm install ws');
|
|
5365
5433
|
} else {
|
|
5366
|
-
console.warn(`
|
|
5434
|
+
console.warn(`WS tunnel error: ${err.message}`);
|
|
5367
5435
|
setTimeout(connect, reconnectDelay);
|
|
5368
5436
|
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
|
|
5369
5437
|
}
|
|
@@ -5434,7 +5502,7 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5434
5502
|
return { error: 'backupId or incidentId is required' };
|
|
5435
5503
|
}
|
|
5436
5504
|
|
|
5437
|
-
console.log(`
|
|
5505
|
+
console.log(`WS workspace.diff ${endpoint}`);
|
|
5438
5506
|
const res = await fetch(`${localBase}${endpoint}`, {
|
|
5439
5507
|
method: 'GET',
|
|
5440
5508
|
headers: { 'Content-Type': 'application/json' }
|
|
@@ -5568,7 +5636,7 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5568
5636
|
}
|
|
5569
5637
|
|
|
5570
5638
|
// ============================================
|
|
5571
|
-
//
|
|
5639
|
+
// SETUP AGENT ROUTES
|
|
5572
5640
|
// Supports the AI Assistant chat commands
|
|
5573
5641
|
// ============================================
|
|
5574
5642
|
|
|
@@ -5585,7 +5653,7 @@ app.post("/setup/run-command", async (req, res) => {
|
|
|
5585
5653
|
return res.status(403).json({ ok: false, error: "Command blocked for safety" });
|
|
5586
5654
|
}
|
|
5587
5655
|
|
|
5588
|
-
console.log(
|
|
5656
|
+
console.log(`[SetupAgent] Running: ${command.substring(0, 100)}`);
|
|
5589
5657
|
const { exec: execCb } = await import('child_process');
|
|
5590
5658
|
const { promisify } = await import('util');
|
|
5591
5659
|
const execAsync = promisify(execCb);
|
|
@@ -5683,10 +5751,10 @@ app.post("/setup/run-auth-command", async (req, res) => {
|
|
|
5683
5751
|
|
|
5684
5752
|
const { exec: execCb } = await import('child_process');
|
|
5685
5753
|
const bgCmd = process.platform === 'win32' ? `start cmd /c "${command}"` : `${command} &`;
|
|
5686
|
-
console.log(
|
|
5754
|
+
console.log(`[SetupAgent] Starting auth: ${command.substring(0, 80)}`);
|
|
5687
5755
|
|
|
5688
5756
|
execCb(bgCmd, { shell: true }, (err) => {
|
|
5689
|
-
if (err) console.warn(
|
|
5757
|
+
if (err) console.warn(`Auth command warning: ${err.message}`);
|
|
5690
5758
|
});
|
|
5691
5759
|
|
|
5692
5760
|
res.json({ ok: true, status: "started", session_id, message: "Auth command started, browser should open" });
|
|
@@ -5730,13 +5798,13 @@ app.get("/setup/auth-status", async (req, res) => {
|
|
|
5730
5798
|
// ============================================
|
|
5731
5799
|
app.listen(PORT, '0.0.0.0', async () => {
|
|
5732
5800
|
console.log(`\n DeepDebug Local Agent listening on port ${PORT}`);
|
|
5733
|
-
console.log(`
|
|
5734
|
-
console.log(`
|
|
5735
|
-
console.log(`
|
|
5736
|
-
console.log(`
|
|
5801
|
+
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
|
|
5802
|
+
console.log(`Process Manager initialized`);
|
|
5803
|
+
console.log(`Backup system ready (max: ${MAX_BACKUPS} backups)`);
|
|
5804
|
+
console.log(`AI Vibe Coding Engine: ${aiEngine?.isActive ? 'ACTIVE' : 'DISABLED'}`);
|
|
5737
5805
|
if (aiEngine) {
|
|
5738
|
-
console.log(`
|
|
5739
|
-
console.log(`
|
|
5806
|
+
console.log(`Gateway URL: ${aiEngine.gatewayUrl}`);
|
|
5807
|
+
console.log(`Max Retries: ${aiEngine.maxRetries}`);
|
|
5740
5808
|
}
|
|
5741
5809
|
console.log(`\n Ready to receive requests!\n`);
|
|
5742
5810
|
|
|
@@ -5748,7 +5816,7 @@ app.listen(PORT, '0.0.0.0', async () => {
|
|
|
5748
5816
|
|
|
5749
5817
|
const gwUrl = cfg.gatewayUrl || process.env.GATEWAY_URL;
|
|
5750
5818
|
const apiKey = cfg.apiKey || process.env.DEEPDEBUG_API_KEY;
|
|
5751
|
-
let tenantId = cfg.tenantId;
|
|
5819
|
+
let tenantId = cfg.tenantId || process.env.TENANT_ID;
|
|
5752
5820
|
if (!tenantId && apiKey) {
|
|
5753
5821
|
try {
|
|
5754
5822
|
const payload = JSON.parse(Buffer.from(apiKey.split('.')[1], 'base64').toString());
|
|
@@ -5765,8 +5833,8 @@ app.listen(PORT, '0.0.0.0', async () => {
|
|
|
5765
5833
|
try {
|
|
5766
5834
|
startMCPHttpServer(wsManager, parseInt(MCP_PORT));
|
|
5767
5835
|
} catch (err) {
|
|
5768
|
-
console.warn(`
|
|
5769
|
-
console.warn(`
|
|
5836
|
+
console.warn(`MCP HTTP Server failed to start: ${err.message}`);
|
|
5837
|
+
console.warn(`(MCP features disabled, REST API continues normally)`);
|
|
5770
5838
|
}
|
|
5771
5839
|
|
|
5772
5840
|
// Auto-register default workspace in WorkspaceManager
|