deepdebug-local-agent 1.0.16 → 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 +316 -220
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);
|
|
@@ -850,19 +870,50 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
850
870
|
res.json({ ok: true, root: abs, workspaceId: wsId, mode: "local", meta, port });
|
|
851
871
|
});
|
|
852
872
|
|
|
873
|
+
|
|
874
|
+
|
|
853
875
|
/**
|
|
854
876
|
* POST /workspace/clone
|
|
855
877
|
* Clones a git repository into the local filesystem, then opens it as workspace.
|
|
856
878
|
* Body: { gitUrl, targetPath, workspaceId? }
|
|
857
879
|
*/
|
|
858
880
|
app.post("/workspace/clone", async (req, res) => {
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
881
|
+
const body = req.body || {};
|
|
882
|
+
const gitUrl = body.gitUrl || body.repoUrl;
|
|
883
|
+
const targetPath = body.targetPath || body.targetDir;
|
|
884
|
+
const tenantId = body.tenantId;
|
|
885
|
+
const workspaceId = body.workspaceId;
|
|
886
|
+
const gitToken = body.gitToken;
|
|
887
|
+
const branch = body.branch;
|
|
888
|
+
|
|
889
|
+
if (!gitUrl) return res.status(400).json({ ok: false, error: "gitUrl is required" });
|
|
890
|
+
|
|
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
|
+
}
|
|
863
905
|
|
|
864
|
-
|
|
865
|
-
|
|
906
|
+
let authenticatedUrl = gitUrl;
|
|
907
|
+
if (gitToken && gitUrl.startsWith('https://') && !gitUrl.includes('@')) {
|
|
908
|
+
if (gitUrl.includes('bitbucket.org')) {
|
|
909
|
+
authenticatedUrl = gitUrl.replace('https://', `https://x-token-auth:${gitToken}@`);
|
|
910
|
+
} else if (gitUrl.includes('gitlab.com')) {
|
|
911
|
+
authenticatedUrl = gitUrl.replace('https://', `https://oauth2:${gitToken}@`);
|
|
912
|
+
} else {
|
|
913
|
+
authenticatedUrl = gitUrl.replace('https://', `https://x-access-token:${gitToken}@`);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
console.log(`Clone request: ${gitUrl} -> ${absTarget}`);
|
|
866
917
|
|
|
867
918
|
try {
|
|
868
919
|
// Ensure parent directory exists
|
|
@@ -874,13 +925,22 @@ app.post("/workspace/clone", async (req, res) => {
|
|
|
874
925
|
const alreadyCloned = await exists(gitDir);
|
|
875
926
|
|
|
876
927
|
if (alreadyCloned) {
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
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
|
+
}
|
|
880
938
|
} else {
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
console.log(`
|
|
939
|
+
const safeLog = authenticatedUrl.replace(/x-token-auth:[^@]+@/g, 'x-token-auth:***@').replace(/oauth2:[^@]+@/g, 'oauth2:***@').replace(/x-access-token:[^@]+@/g, 'x-access-token:***@');
|
|
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}`);
|
|
884
944
|
}
|
|
885
945
|
|
|
886
946
|
// Open the cloned workspace
|
|
@@ -889,7 +949,7 @@ app.post("/workspace/clone", async (req, res) => {
|
|
|
889
949
|
try {
|
|
890
950
|
await wsManager.open(wsId, absTarget);
|
|
891
951
|
} catch (err) {
|
|
892
|
-
console.warn(`
|
|
952
|
+
console.warn(`WorkspaceManager.open failed (non-fatal): ${err.message}`);
|
|
893
953
|
}
|
|
894
954
|
|
|
895
955
|
const meta = await detectProject(absTarget);
|
|
@@ -905,7 +965,7 @@ app.post("/workspace/clone", async (req, res) => {
|
|
|
905
965
|
});
|
|
906
966
|
|
|
907
967
|
} catch (err) {
|
|
908
|
-
console.error(`
|
|
968
|
+
console.error(`Clone failed: ${err.message}`);
|
|
909
969
|
const hint = err.message.includes('Authentication') || err.message.includes('could not read')
|
|
910
970
|
? "Authentication failed. Ensure the repo is public or GitHub integration is configured."
|
|
911
971
|
: err.message.includes('not found') || err.message.includes('does not exist')
|
|
@@ -987,6 +1047,35 @@ app.get("/workspace/file-content", async (req, res) => {
|
|
|
987
1047
|
}
|
|
988
1048
|
});
|
|
989
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
|
+
|
|
990
1079
|
/** L mltiplos arquivos */
|
|
991
1080
|
app.post("/workspace/batch-read", async (req, res) => {
|
|
992
1081
|
const wsRoot = resolveWorkspaceRoot(req);
|
|
@@ -1029,7 +1118,7 @@ app.get("/workspace/file-exists", async (req, res) => {
|
|
|
1029
1118
|
const fullPath = path.join(wsRoot, relativePath);
|
|
1030
1119
|
const fileExists = await exists(fullPath);
|
|
1031
1120
|
|
|
1032
|
-
console.log(`
|
|
1121
|
+
console.log(`[file-exists] ${relativePath} -> ${fileExists ? 'EXISTS' : 'NOT FOUND'}`);
|
|
1033
1122
|
|
|
1034
1123
|
res.json({
|
|
1035
1124
|
ok: true,
|
|
@@ -1038,7 +1127,7 @@ app.get("/workspace/file-exists", async (req, res) => {
|
|
|
1038
1127
|
fullPath: fullPath
|
|
1039
1128
|
});
|
|
1040
1129
|
} catch (err) {
|
|
1041
|
-
console.error(`
|
|
1130
|
+
console.error(`[file-exists] Error:`, err.message);
|
|
1042
1131
|
res.status(500).json({ ok: false, error: err.message });
|
|
1043
1132
|
}
|
|
1044
1133
|
});
|
|
@@ -1070,7 +1159,7 @@ app.post("/workspace/validate-paths", async (req, res) => {
|
|
|
1070
1159
|
const allExist = results.every(r => r.exists);
|
|
1071
1160
|
const missingPaths = results.filter(r => !r.exists).map(r => r.path);
|
|
1072
1161
|
|
|
1073
|
-
console.log(`
|
|
1162
|
+
console.log(`[validate-paths] Checked ${pathList.length} paths, ${missingPaths.length} missing`);
|
|
1074
1163
|
|
|
1075
1164
|
res.json({
|
|
1076
1165
|
ok: true,
|
|
@@ -1080,7 +1169,7 @@ app.post("/workspace/validate-paths", async (req, res) => {
|
|
|
1080
1169
|
totalChecked: pathList.length
|
|
1081
1170
|
});
|
|
1082
1171
|
} catch (err) {
|
|
1083
|
-
console.error(`
|
|
1172
|
+
console.error(`[validate-paths] Error:`, err.message);
|
|
1084
1173
|
res.status(500).json({ ok: false, error: err.message });
|
|
1085
1174
|
}
|
|
1086
1175
|
});
|
|
@@ -1103,7 +1192,7 @@ app.post("/workspace/search-file", async (req, res) => {
|
|
|
1103
1192
|
}
|
|
1104
1193
|
|
|
1105
1194
|
try {
|
|
1106
|
-
console.log(`
|
|
1195
|
+
console.log(`[search-file] Searching for: ${fileName}`);
|
|
1107
1196
|
|
|
1108
1197
|
const rawFiles = await listRecursive(wsRoot, {
|
|
1109
1198
|
maxDepth: 15,
|
|
@@ -1120,7 +1209,7 @@ app.post("/workspace/search-file", async (req, res) => {
|
|
|
1120
1209
|
return String(f);
|
|
1121
1210
|
}).filter(f => f && typeof f === 'string');
|
|
1122
1211
|
|
|
1123
|
-
console.log(`
|
|
1212
|
+
console.log(`[search-file] Scanning ${allFiles.length} files`);
|
|
1124
1213
|
|
|
1125
1214
|
// Strategy 1: Exact path match
|
|
1126
1215
|
let foundPath = allFiles.find(f => f.endsWith('/' + fileName) || f === fileName);
|
|
@@ -1150,14 +1239,14 @@ app.post("/workspace/search-file", async (req, res) => {
|
|
|
1150
1239
|
}
|
|
1151
1240
|
|
|
1152
1241
|
if (foundPath) {
|
|
1153
|
-
console.log(`
|
|
1242
|
+
console.log(`[search-file] Found: ${foundPath}`);
|
|
1154
1243
|
res.json({ ok: true, found: true, path: foundPath, fileName });
|
|
1155
1244
|
} else {
|
|
1156
|
-
console.log(`
|
|
1245
|
+
console.log(`[search-file] Not found: ${fileName}`);
|
|
1157
1246
|
res.json({ ok: true, found: false, fileName, searchedFiles: allFiles.length });
|
|
1158
1247
|
}
|
|
1159
1248
|
} catch (err) {
|
|
1160
|
-
console.error(`
|
|
1249
|
+
console.error(`[search-file] Error:`, err.message);
|
|
1161
1250
|
res.status(500).json({ ok: false, error: err.message });
|
|
1162
1251
|
}
|
|
1163
1252
|
});
|
|
@@ -1181,7 +1270,7 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1181
1270
|
}
|
|
1182
1271
|
|
|
1183
1272
|
try {
|
|
1184
|
-
console.log(`
|
|
1273
|
+
console.log(`[search-by-content] Searching for terms: ${terms.join(', ')}`);
|
|
1185
1274
|
|
|
1186
1275
|
const rawFiles = await listRecursive(wsRoot, {
|
|
1187
1276
|
maxDepth: 15,
|
|
@@ -1201,7 +1290,7 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1201
1290
|
return extensions.some(ext => filePath.endsWith(ext));
|
|
1202
1291
|
});
|
|
1203
1292
|
|
|
1204
|
-
console.log(`
|
|
1293
|
+
console.log(`[search-by-content] Scanning ${filteredFiles.length} files`);
|
|
1205
1294
|
|
|
1206
1295
|
const results = [];
|
|
1207
1296
|
|
|
@@ -1246,7 +1335,7 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1246
1335
|
return true;
|
|
1247
1336
|
}).slice(0, maxResults);
|
|
1248
1337
|
|
|
1249
|
-
console.log(`
|
|
1338
|
+
console.log(`[search-by-content] Found ${dedupedResults.length} matching files`);
|
|
1250
1339
|
|
|
1251
1340
|
res.json({
|
|
1252
1341
|
ok: true,
|
|
@@ -1255,7 +1344,7 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1255
1344
|
termsSearched: terms
|
|
1256
1345
|
});
|
|
1257
1346
|
} catch (err) {
|
|
1258
|
-
console.error(`
|
|
1347
|
+
console.error(`[search-by-content] Error:`, err.message);
|
|
1259
1348
|
res.status(500).json({ ok: false, error: err.message });
|
|
1260
1349
|
}
|
|
1261
1350
|
});
|
|
@@ -1279,7 +1368,7 @@ app.post("/workspace/find-field-definition", async (req, res) => {
|
|
|
1279
1368
|
}
|
|
1280
1369
|
|
|
1281
1370
|
try {
|
|
1282
|
-
console.log(`
|
|
1371
|
+
console.log(`[find-field] Searching for field: ${fieldName}`);
|
|
1283
1372
|
|
|
1284
1373
|
const rawFiles = await listRecursive(wsRoot, {
|
|
1285
1374
|
maxDepth: 15,
|
|
@@ -1358,7 +1447,7 @@ app.post("/workspace/find-field-definition", async (req, res) => {
|
|
|
1358
1447
|
return 0;
|
|
1359
1448
|
});
|
|
1360
1449
|
|
|
1361
|
-
console.log(`
|
|
1450
|
+
console.log(`[find-field] Found ${definitions.length} definitions for '${fieldName}'`);
|
|
1362
1451
|
|
|
1363
1452
|
res.json({
|
|
1364
1453
|
ok: true,
|
|
@@ -1367,7 +1456,7 @@ app.post("/workspace/find-field-definition", async (req, res) => {
|
|
|
1367
1456
|
totalSearched: targetFiles.length
|
|
1368
1457
|
});
|
|
1369
1458
|
} catch (err) {
|
|
1370
|
-
console.error(`
|
|
1459
|
+
console.error(`[find-field] Error:`, err.message);
|
|
1371
1460
|
res.status(500).json({ ok: false, error: err.message });
|
|
1372
1461
|
}
|
|
1373
1462
|
});
|
|
@@ -1581,7 +1670,7 @@ app.post("/workspace/patch", async (req, res) => {
|
|
|
1581
1670
|
if (!diff) return res.status(400).json({ error: "diff is required" });
|
|
1582
1671
|
|
|
1583
1672
|
try {
|
|
1584
|
-
console.log(`
|
|
1673
|
+
console.log(`Applying patch for incident: ${incidentId || 'unknown'}`);
|
|
1585
1674
|
const out = await applyUnifiedDiff(wsRoot, diff);
|
|
1586
1675
|
|
|
1587
1676
|
// CRITICAL FIX: Format response as expected by Gateway
|
|
@@ -1596,7 +1685,7 @@ app.post("/workspace/patch", async (req, res) => {
|
|
|
1596
1685
|
incidentId: incidentId
|
|
1597
1686
|
};
|
|
1598
1687
|
|
|
1599
|
-
console.log(`
|
|
1688
|
+
console.log(`Patch applied successfully:`, {
|
|
1600
1689
|
target: out.target,
|
|
1601
1690
|
bytes: out.bytes,
|
|
1602
1691
|
incident: incidentId
|
|
@@ -1604,7 +1693,7 @@ app.post("/workspace/patch", async (req, res) => {
|
|
|
1604
1693
|
|
|
1605
1694
|
res.json(response);
|
|
1606
1695
|
} catch (e) {
|
|
1607
|
-
console.error(`
|
|
1696
|
+
console.error(`Patch failed:`, e.message);
|
|
1608
1697
|
res.status(400).json({
|
|
1609
1698
|
ok: false,
|
|
1610
1699
|
error: "patch failed",
|
|
@@ -1656,7 +1745,7 @@ let TEST_LOCAL_STATE = {
|
|
|
1656
1745
|
* Returns current test local state with auto-detected port
|
|
1657
1746
|
*/
|
|
1658
1747
|
app.get("/workspace/test-local/state", async (req, res) => {
|
|
1659
|
-
console.log("
|
|
1748
|
+
console.log("[TEST-LOCAL] Getting state:", TEST_LOCAL_STATE.status);
|
|
1660
1749
|
|
|
1661
1750
|
// Auto-detect port if not set in config
|
|
1662
1751
|
let port = TEST_LOCAL_STATE.config?.server?.port;
|
|
@@ -1695,7 +1784,7 @@ app.post("/workspace/test-local/compile", async (req, res) => {
|
|
|
1695
1784
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1696
1785
|
|
|
1697
1786
|
try {
|
|
1698
|
-
console.log("
|
|
1787
|
+
console.log("[TEST-LOCAL] Starting compilation...");
|
|
1699
1788
|
TEST_LOCAL_STATE.status = "compiling";
|
|
1700
1789
|
|
|
1701
1790
|
const meta = await detectProject(wsRoot);
|
|
@@ -1730,7 +1819,7 @@ app.post("/workspace/test-local/compile", async (req, res) => {
|
|
|
1730
1819
|
duration: compileResult.duration
|
|
1731
1820
|
};
|
|
1732
1821
|
|
|
1733
|
-
console.log("
|
|
1822
|
+
console.log("[TEST-LOCAL] Compilation successful");
|
|
1734
1823
|
|
|
1735
1824
|
res.json({
|
|
1736
1825
|
ok: true,
|
|
@@ -1740,7 +1829,7 @@ app.post("/workspace/test-local/compile", async (req, res) => {
|
|
|
1740
1829
|
stdout: compileResult.stdout
|
|
1741
1830
|
});
|
|
1742
1831
|
} catch (err) {
|
|
1743
|
-
console.error("
|
|
1832
|
+
console.error("[TEST-LOCAL] Compilation failed:", err.message);
|
|
1744
1833
|
TEST_LOCAL_STATE.status = "error";
|
|
1745
1834
|
res.status(500).json({ ok: false, error: err.message });
|
|
1746
1835
|
}
|
|
@@ -1793,7 +1882,7 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1793
1882
|
const command = 'java';
|
|
1794
1883
|
const args = ['-jar', jarPath, `--server.port=${serverPort}`];
|
|
1795
1884
|
|
|
1796
|
-
console.log(`
|
|
1885
|
+
console.log(`Command: ${command} ${args.join(' ')}`);
|
|
1797
1886
|
|
|
1798
1887
|
// Env LIMPO - remover TODAS as variveis Spring que podem interferir
|
|
1799
1888
|
const cleanEnv = { ...process.env };
|
|
@@ -1823,7 +1912,7 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1823
1912
|
TEST_LOCAL_STATE.status = "running";
|
|
1824
1913
|
TEST_LOCAL_STATE.config = { port: serverPort };
|
|
1825
1914
|
|
|
1826
|
-
console.log(`
|
|
1915
|
+
console.log(`[TEST-LOCAL] Server started on port ${serverPort}`);
|
|
1827
1916
|
|
|
1828
1917
|
res.json({
|
|
1829
1918
|
ok: true,
|
|
@@ -1873,7 +1962,7 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1873
1962
|
});
|
|
1874
1963
|
}
|
|
1875
1964
|
} catch (err) {
|
|
1876
|
-
console.error("
|
|
1965
|
+
console.error("[TEST-LOCAL] Server start failed:", err.message);
|
|
1877
1966
|
TEST_LOCAL_STATE.status = "error";
|
|
1878
1967
|
res.status(500).json({ ok: false, error: err.message });
|
|
1879
1968
|
}
|
|
@@ -1885,19 +1974,19 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1885
1974
|
*/
|
|
1886
1975
|
app.post("/workspace/test-local/stop", async (req, res) => {
|
|
1887
1976
|
try {
|
|
1888
|
-
console.log("
|
|
1977
|
+
console.log("[TEST-LOCAL] Stopping server...");
|
|
1889
1978
|
|
|
1890
1979
|
await processManager.stop('test-local');
|
|
1891
1980
|
TEST_LOCAL_STATE.status = "stopped";
|
|
1892
1981
|
|
|
1893
|
-
console.log("
|
|
1982
|
+
console.log("[TEST-LOCAL] Server stopped");
|
|
1894
1983
|
|
|
1895
1984
|
res.json({
|
|
1896
1985
|
ok: true,
|
|
1897
1986
|
status: "stopped"
|
|
1898
1987
|
});
|
|
1899
1988
|
} catch (err) {
|
|
1900
|
-
console.error("
|
|
1989
|
+
console.error("[TEST-LOCAL] Server stop failed:", err.message);
|
|
1901
1990
|
res.status(500).json({ ok: false, error: err.message });
|
|
1902
1991
|
}
|
|
1903
1992
|
});
|
|
@@ -1911,7 +2000,7 @@ app.get("/workspace/test-local/endpoints", async (req, res) => {
|
|
|
1911
2000
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1912
2001
|
|
|
1913
2002
|
try {
|
|
1914
|
-
console.log("
|
|
2003
|
+
console.log("[TEST-LOCAL] Getting endpoints...");
|
|
1915
2004
|
|
|
1916
2005
|
// If endpoints are cached, return them
|
|
1917
2006
|
if (TEST_LOCAL_STATE.endpoints && TEST_LOCAL_STATE.endpoints.length > 0) {
|
|
@@ -1934,7 +2023,7 @@ app.get("/workspace/test-local/endpoints", async (req, res) => {
|
|
|
1934
2023
|
|
|
1935
2024
|
TEST_LOCAL_STATE.endpoints = payloadDocs.endpoints;
|
|
1936
2025
|
|
|
1937
|
-
console.log(`
|
|
2026
|
+
console.log(`[TEST-LOCAL] Discovered ${payloadDocs.endpoints.length} endpoints`);
|
|
1938
2027
|
|
|
1939
2028
|
res.json({
|
|
1940
2029
|
ok: true,
|
|
@@ -1942,7 +2031,7 @@ app.get("/workspace/test-local/endpoints", async (req, res) => {
|
|
|
1942
2031
|
cached: false
|
|
1943
2032
|
});
|
|
1944
2033
|
} catch (err) {
|
|
1945
|
-
console.error("
|
|
2034
|
+
console.error("[TEST-LOCAL] Failed to get endpoints:", err.message);
|
|
1946
2035
|
res.status(500).json({ ok: false, error: err.message });
|
|
1947
2036
|
}
|
|
1948
2037
|
});
|
|
@@ -1962,7 +2051,7 @@ app.post("/workspace/test-local/execute", async (req, res) => {
|
|
|
1962
2051
|
const serverPort = port || TEST_LOCAL_STATE.config?.server?.port || 8080;
|
|
1963
2052
|
const url = `http://localhost:${serverPort}${reqPath}`;
|
|
1964
2053
|
|
|
1965
|
-
console.log(`
|
|
2054
|
+
console.log(`[TEST-LOCAL] Executing: ${method} ${url}`);
|
|
1966
2055
|
|
|
1967
2056
|
const startTime = Date.now();
|
|
1968
2057
|
|
|
@@ -2011,14 +2100,14 @@ app.post("/workspace/test-local/execute", async (req, res) => {
|
|
|
2011
2100
|
TEST_LOCAL_STATE.testResults.shift();
|
|
2012
2101
|
}
|
|
2013
2102
|
|
|
2014
|
-
console.log(`
|
|
2103
|
+
console.log(`[TEST-LOCAL] Test result: ${response.status} (${duration}ms)`);
|
|
2015
2104
|
|
|
2016
2105
|
res.json({
|
|
2017
2106
|
ok: true,
|
|
2018
2107
|
result
|
|
2019
2108
|
});
|
|
2020
2109
|
} catch (err) {
|
|
2021
|
-
console.error("
|
|
2110
|
+
console.error("[TEST-LOCAL] Test execution failed:", err.message);
|
|
2022
2111
|
res.json({
|
|
2023
2112
|
ok: false,
|
|
2024
2113
|
error: err.message,
|
|
@@ -2037,7 +2126,7 @@ app.post("/workspace/test-local/execute", async (req, res) => {
|
|
|
2037
2126
|
* Returns stored test results
|
|
2038
2127
|
*/
|
|
2039
2128
|
app.get("/workspace/test-local/results", (req, res) => {
|
|
2040
|
-
console.log("
|
|
2129
|
+
console.log("[TEST-LOCAL] Getting test results...");
|
|
2041
2130
|
|
|
2042
2131
|
res.json({
|
|
2043
2132
|
ok: true,
|
|
@@ -2051,7 +2140,7 @@ app.get("/workspace/test-local/results", (req, res) => {
|
|
|
2051
2140
|
* Clears stored test results
|
|
2052
2141
|
*/
|
|
2053
2142
|
app.post("/workspace/test-local/clear-results", (req, res) => {
|
|
2054
|
-
console.log("
|
|
2143
|
+
console.log("[TEST-LOCAL] Clearing test results...");
|
|
2055
2144
|
|
|
2056
2145
|
TEST_LOCAL_STATE.testResults = [];
|
|
2057
2146
|
|
|
@@ -2068,7 +2157,7 @@ app.post("/workspace/test-local/clear-results", (req, res) => {
|
|
|
2068
2157
|
app.get("/workspace/test-local/logs", (req, res) => {
|
|
2069
2158
|
const limit = parseInt(req.query.limit) || 500;
|
|
2070
2159
|
|
|
2071
|
-
console.log(`
|
|
2160
|
+
console.log(`[TEST-LOCAL] Getting logs (limit: ${limit})`);
|
|
2072
2161
|
|
|
2073
2162
|
const logs = TEST_LOCAL_STATE.serverLogs.slice(-limit);
|
|
2074
2163
|
|
|
@@ -2087,7 +2176,7 @@ app.get("/workspace/controllers", async (req, res) => {
|
|
|
2087
2176
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
2088
2177
|
|
|
2089
2178
|
try {
|
|
2090
|
-
console.log("
|
|
2179
|
+
console.log("[CONTROLLERS] Discovering controllers...");
|
|
2091
2180
|
|
|
2092
2181
|
const scanner = new WorkspaceScanner(wsRoot);
|
|
2093
2182
|
const structure = await scanner.scan();
|
|
@@ -2095,14 +2184,14 @@ app.get("/workspace/controllers", async (req, res) => {
|
|
|
2095
2184
|
const controllerAnalyzer = new ControllerAnalyzer(wsRoot);
|
|
2096
2185
|
const apiDocs = await controllerAnalyzer.generateApiDocs(structure.files);
|
|
2097
2186
|
|
|
2098
|
-
console.log(`
|
|
2187
|
+
console.log(`[CONTROLLERS] Found ${apiDocs.totalEndpoints} endpoints in ${apiDocs.totalControllers} controllers`);
|
|
2099
2188
|
|
|
2100
2189
|
res.json({
|
|
2101
2190
|
ok: true,
|
|
2102
2191
|
...apiDocs
|
|
2103
2192
|
});
|
|
2104
2193
|
} catch (err) {
|
|
2105
|
-
console.error("
|
|
2194
|
+
console.error("[CONTROLLERS] Failed:", err.message);
|
|
2106
2195
|
res.status(500).json({ ok: false, error: err.message });
|
|
2107
2196
|
}
|
|
2108
2197
|
});
|
|
@@ -2113,7 +2202,7 @@ app.get("/workspace/dtos", async (req, res) => {
|
|
|
2113
2202
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
2114
2203
|
|
|
2115
2204
|
try {
|
|
2116
|
-
console.log("
|
|
2205
|
+
console.log("[DTOS] Analyzing DTOs...");
|
|
2117
2206
|
|
|
2118
2207
|
const scanner = new WorkspaceScanner(wsRoot);
|
|
2119
2208
|
const structure = await scanner.scan();
|
|
@@ -2124,14 +2213,14 @@ app.get("/workspace/dtos", async (req, res) => {
|
|
|
2124
2213
|
const dtoAnalyzer = new DTOAnalyzer(wsRoot);
|
|
2125
2214
|
const payloadDocs = await dtoAnalyzer.generatePayloadDocs(structure.files, apiDocs.endpoints);
|
|
2126
2215
|
|
|
2127
|
-
console.log(`
|
|
2216
|
+
console.log(`[DTOS] Found ${payloadDocs.totalDtos} DTOs`);
|
|
2128
2217
|
|
|
2129
2218
|
res.json({
|
|
2130
2219
|
ok: true,
|
|
2131
2220
|
...payloadDocs
|
|
2132
2221
|
});
|
|
2133
2222
|
} catch (err) {
|
|
2134
|
-
console.error("
|
|
2223
|
+
console.error("[DTOS] Failed:", err.message);
|
|
2135
2224
|
res.status(500).json({ ok: false, error: err.message });
|
|
2136
2225
|
}
|
|
2137
2226
|
});
|
|
@@ -2142,19 +2231,19 @@ app.get("/workspace/config", async (req, res) => {
|
|
|
2142
2231
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
2143
2232
|
|
|
2144
2233
|
try {
|
|
2145
|
-
console.log("
|
|
2234
|
+
console.log("[CONFIG] Analyzing configuration...");
|
|
2146
2235
|
|
|
2147
2236
|
const configAnalyzer = new ConfigAnalyzer(wsRoot);
|
|
2148
2237
|
const config = await configAnalyzer.analyze();
|
|
2149
2238
|
|
|
2150
|
-
console.log(`
|
|
2239
|
+
console.log(`[CONFIG] Server port: ${config.server.port}`);
|
|
2151
2240
|
|
|
2152
2241
|
res.json({
|
|
2153
2242
|
ok: true,
|
|
2154
2243
|
...config
|
|
2155
2244
|
});
|
|
2156
2245
|
} catch (err) {
|
|
2157
|
-
console.error("
|
|
2246
|
+
console.error("[CONFIG] Failed:", err.message);
|
|
2158
2247
|
res.status(500).json({ ok: false, error: err.message });
|
|
2159
2248
|
}
|
|
2160
2249
|
});
|
|
@@ -2223,7 +2312,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2223
2312
|
const { diff, incidentId } = req.body || {};
|
|
2224
2313
|
if (!diff) return res.status(400).json({ error: "diff is required" });
|
|
2225
2314
|
|
|
2226
|
-
console.log(`
|
|
2315
|
+
console.log(`Safe patch requested for incident: ${incidentId || 'unknown'}`);
|
|
2227
2316
|
|
|
2228
2317
|
try {
|
|
2229
2318
|
// 1. Validate diff
|
|
@@ -2238,7 +2327,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2238
2327
|
|
|
2239
2328
|
// 2. Extract target files
|
|
2240
2329
|
const targetFiles = extractTargetFiles(diff);
|
|
2241
|
-
console.log(`
|
|
2330
|
+
console.log(`Target files: ${targetFiles.join(', ')}`);
|
|
2242
2331
|
|
|
2243
2332
|
// 3. Create backup
|
|
2244
2333
|
const backupId = `backup-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
@@ -2269,23 +2358,23 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2269
2358
|
}
|
|
2270
2359
|
saveBackupIndex();
|
|
2271
2360
|
} catch (e) {
|
|
2272
|
-
console.warn(`
|
|
2361
|
+
console.warn(`Could not persist backup to disk: ${e.message}`);
|
|
2273
2362
|
}
|
|
2274
2363
|
|
|
2275
2364
|
// Cleanup old backups if exceeded max
|
|
2276
2365
|
if (BACKUPS.size > MAX_BACKUPS) {
|
|
2277
2366
|
const oldest = Array.from(BACKUPS.keys())[0];
|
|
2278
2367
|
BACKUPS.delete(oldest);
|
|
2279
|
-
console.log(`
|
|
2368
|
+
console.log(`Removed old backup: ${oldest}`);
|
|
2280
2369
|
saveBackupIndex();
|
|
2281
2370
|
}
|
|
2282
2371
|
|
|
2283
|
-
console.log(`
|
|
2372
|
+
console.log(`Backup created: ${backupId} (${backupFiles.length} files)`);
|
|
2284
2373
|
|
|
2285
2374
|
// 4. Apply patch
|
|
2286
2375
|
try {
|
|
2287
2376
|
const result = await applyUnifiedDiff(wsRoot, diff);
|
|
2288
|
-
console.log(`
|
|
2377
|
+
console.log(`Patch applied successfully: ${result.target}`);
|
|
2289
2378
|
|
|
2290
2379
|
res.json({
|
|
2291
2380
|
ok: true,
|
|
@@ -2296,7 +2385,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2296
2385
|
});
|
|
2297
2386
|
} catch (patchError) {
|
|
2298
2387
|
// 5. Rollback on failure
|
|
2299
|
-
console.error(`
|
|
2388
|
+
console.error(`Patch failed, rolling back: ${patchError.message}`);
|
|
2300
2389
|
|
|
2301
2390
|
for (const file of backupFiles) {
|
|
2302
2391
|
const fullPath = path.join(wsRoot, file.path);
|
|
@@ -2313,7 +2402,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2313
2402
|
});
|
|
2314
2403
|
}
|
|
2315
2404
|
} catch (err) {
|
|
2316
|
-
console.error(`
|
|
2405
|
+
console.error(`Safe patch error: ${err.message}`);
|
|
2317
2406
|
res.status(500).json({ ok: false, error: err.message });
|
|
2318
2407
|
}
|
|
2319
2408
|
});
|
|
@@ -2334,7 +2423,7 @@ app.post("/workspace/patch/dry-run", async (req, res) => {
|
|
|
2334
2423
|
const { diff } = req.body || {};
|
|
2335
2424
|
if (!diff) return res.status(400).json({ error: "diff is required" });
|
|
2336
2425
|
|
|
2337
|
-
console.log(`
|
|
2426
|
+
console.log(`Dry-run patch validation requested`);
|
|
2338
2427
|
|
|
2339
2428
|
try {
|
|
2340
2429
|
// 1. Validate diff format
|
|
@@ -2385,7 +2474,7 @@ app.post("/workspace/patch/dry-run", async (req, res) => {
|
|
|
2385
2474
|
const allFilesExist = fileChecks.every(f => f.exists || diff.includes('--- /dev/null'));
|
|
2386
2475
|
const missingFiles = fileChecks.filter(f => !f.exists && !diff.includes('--- /dev/null'));
|
|
2387
2476
|
|
|
2388
|
-
console.log(`
|
|
2477
|
+
console.log(`Dry-run complete: ${targetFiles.length} files, ${hunkCount} hunks`);
|
|
2389
2478
|
|
|
2390
2479
|
res.json({
|
|
2391
2480
|
ok: true,
|
|
@@ -2398,7 +2487,7 @@ app.post("/workspace/patch/dry-run", async (req, res) => {
|
|
|
2398
2487
|
warnings: missingFiles.length > 0 ? [`${missingFiles.length} file(s) not found`] : []
|
|
2399
2488
|
});
|
|
2400
2489
|
} catch (err) {
|
|
2401
|
-
console.error(`
|
|
2490
|
+
console.error(`Dry-run error: ${err.message}`);
|
|
2402
2491
|
res.status(500).json({ ok: false, error: err.message });
|
|
2403
2492
|
}
|
|
2404
2493
|
});
|
|
@@ -2466,7 +2555,7 @@ app.post("/workspace/backup", async (req, res) => {
|
|
|
2466
2555
|
incidentId: incidentId || null
|
|
2467
2556
|
});
|
|
2468
2557
|
|
|
2469
|
-
console.log(`
|
|
2558
|
+
console.log(`Manual backup created: ${backupId}`);
|
|
2470
2559
|
|
|
2471
2560
|
res.json({
|
|
2472
2561
|
ok: true,
|
|
@@ -2475,7 +2564,7 @@ app.post("/workspace/backup", async (req, res) => {
|
|
|
2475
2564
|
timestamp: new Date().toISOString()
|
|
2476
2565
|
});
|
|
2477
2566
|
} catch (err) {
|
|
2478
|
-
console.error(`
|
|
2567
|
+
console.error(`Backup error: ${err.message}`);
|
|
2479
2568
|
res.status(500).json({ ok: false, error: err.message });
|
|
2480
2569
|
}
|
|
2481
2570
|
});
|
|
@@ -2507,14 +2596,14 @@ app.post("/workspace/rollback", async (req, res) => {
|
|
|
2507
2596
|
}
|
|
2508
2597
|
|
|
2509
2598
|
try {
|
|
2510
|
-
console.log(`
|
|
2599
|
+
console.log(`Rolling back to backup: ${backupId}`);
|
|
2511
2600
|
|
|
2512
2601
|
for (const file of backup.files) {
|
|
2513
2602
|
const fullPath = path.join(wsRoot, file.path);
|
|
2514
2603
|
await writeFile(fullPath, file.content, 'utf8');
|
|
2515
2604
|
}
|
|
2516
2605
|
|
|
2517
|
-
console.log(`
|
|
2606
|
+
console.log(`Rollback completed: ${backup.files.length} files restored`);
|
|
2518
2607
|
|
|
2519
2608
|
res.json({
|
|
2520
2609
|
ok: true,
|
|
@@ -2523,7 +2612,7 @@ app.post("/workspace/rollback", async (req, res) => {
|
|
|
2523
2612
|
timestamp: backup.timestamp
|
|
2524
2613
|
});
|
|
2525
2614
|
} catch (err) {
|
|
2526
|
-
console.error(`
|
|
2615
|
+
console.error(`Rollback error: ${err.message}`);
|
|
2527
2616
|
res.status(500).json({ ok: false, error: err.message });
|
|
2528
2617
|
}
|
|
2529
2618
|
});
|
|
@@ -2563,7 +2652,7 @@ app.delete("/workspace/backups/:backupId", (req, res) => {
|
|
|
2563
2652
|
}
|
|
2564
2653
|
|
|
2565
2654
|
BACKUPS.delete(backupId);
|
|
2566
|
-
console.log(`
|
|
2655
|
+
console.log(`Backup deleted: ${backupId}`);
|
|
2567
2656
|
|
|
2568
2657
|
res.json({
|
|
2569
2658
|
ok: true,
|
|
@@ -2622,7 +2711,7 @@ app.get("/workspace/diff/by-incident/:incidentId", async (req, res) => {
|
|
|
2622
2711
|
}
|
|
2623
2712
|
|
|
2624
2713
|
if (!matchedBackup) {
|
|
2625
|
-
console.log(`
|
|
2714
|
+
console.log(`No backup found for incident ${incidentId}. Available backups:`, Array.from(BACKUPS.keys()));
|
|
2626
2715
|
return res.status(404).json({
|
|
2627
2716
|
ok: false,
|
|
2628
2717
|
error: "No backup found for this incident",
|
|
@@ -2631,7 +2720,7 @@ app.get("/workspace/diff/by-incident/:incidentId", async (req, res) => {
|
|
|
2631
2720
|
});
|
|
2632
2721
|
}
|
|
2633
2722
|
|
|
2634
|
-
console.log(`
|
|
2723
|
+
console.log(`Found backup ${matchedBackupId} for incident ${incidentId}`);
|
|
2635
2724
|
|
|
2636
2725
|
// Reuse the diff logic
|
|
2637
2726
|
try {
|
|
@@ -2668,7 +2757,7 @@ app.get("/workspace/diff/by-incident/:incidentId", async (req, res) => {
|
|
|
2668
2757
|
files: diffs
|
|
2669
2758
|
});
|
|
2670
2759
|
} catch (err) {
|
|
2671
|
-
console.error(`
|
|
2760
|
+
console.error(`Diff error: ${err.message}`);
|
|
2672
2761
|
res.status(500).json({ ok: false, error: err.message });
|
|
2673
2762
|
}
|
|
2674
2763
|
});
|
|
@@ -2740,7 +2829,7 @@ app.get("/workspace/diff/:backupId", async (req, res) => {
|
|
|
2740
2829
|
}
|
|
2741
2830
|
}
|
|
2742
2831
|
|
|
2743
|
-
console.log(`
|
|
2832
|
+
console.log(`Diff for ${backupId}: ${diffs.length} file(s) changed`);
|
|
2744
2833
|
|
|
2745
2834
|
res.json({
|
|
2746
2835
|
ok: true,
|
|
@@ -2752,7 +2841,7 @@ app.get("/workspace/diff/:backupId", async (req, res) => {
|
|
|
2752
2841
|
files: diffs
|
|
2753
2842
|
});
|
|
2754
2843
|
} catch (err) {
|
|
2755
|
-
console.error(`
|
|
2844
|
+
console.error(`Diff error: ${err.message}`);
|
|
2756
2845
|
res.status(500).json({ ok: false, error: err.message });
|
|
2757
2846
|
}
|
|
2758
2847
|
});
|
|
@@ -2785,7 +2874,7 @@ app.get("/workspace/detect-port", async (req, res) => {
|
|
|
2785
2874
|
|
|
2786
2875
|
try {
|
|
2787
2876
|
const fullPath = path.join(wsRoot, servicePath);
|
|
2788
|
-
console.log(`
|
|
2877
|
+
console.log(`Detecting port for service at: ${fullPath}`);
|
|
2789
2878
|
|
|
2790
2879
|
let port = null;
|
|
2791
2880
|
let detectionMethod = null;
|
|
@@ -2820,7 +2909,7 @@ app.get("/workspace/detect-port", async (req, res) => {
|
|
|
2820
2909
|
detectionMethod = 'global-detection';
|
|
2821
2910
|
}
|
|
2822
2911
|
|
|
2823
|
-
console.log(`
|
|
2912
|
+
console.log(`Detected port: ${port || 'default'} via ${detectionMethod}`);
|
|
2824
2913
|
|
|
2825
2914
|
res.json({
|
|
2826
2915
|
ok: true,
|
|
@@ -2831,7 +2920,7 @@ app.get("/workspace/detect-port", async (req, res) => {
|
|
|
2831
2920
|
servicePath: servicePath || '/'
|
|
2832
2921
|
});
|
|
2833
2922
|
} catch (err) {
|
|
2834
|
-
console.error('
|
|
2923
|
+
console.error('Error detecting port:', err.message);
|
|
2835
2924
|
res.status(500).json({ ok: false, error: err.message });
|
|
2836
2925
|
}
|
|
2837
2926
|
});
|
|
@@ -3042,7 +3131,7 @@ app.post("/workspace/test-local/prepare", async (req, res) => {
|
|
|
3042
3131
|
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
3043
3132
|
|
|
3044
3133
|
try {
|
|
3045
|
-
console.log("
|
|
3134
|
+
console.log("[TEST-LOCAL] Preparing test environment...");
|
|
3046
3135
|
TEST_LOCAL_STATE.status = "compiling";
|
|
3047
3136
|
|
|
3048
3137
|
// Step 1: Compile
|
|
@@ -3082,7 +3171,7 @@ app.post("/workspace/test-local/prepare", async (req, res) => {
|
|
|
3082
3171
|
const config = await configAnalyzer.analyze();
|
|
3083
3172
|
TEST_LOCAL_STATE.config = config;
|
|
3084
3173
|
|
|
3085
|
-
console.log(`
|
|
3174
|
+
console.log(`[TEST-LOCAL] Prepared: ${payloadDocs.endpoints.length} endpoints discovered`);
|
|
3086
3175
|
|
|
3087
3176
|
res.json({
|
|
3088
3177
|
ok: true,
|
|
@@ -3105,7 +3194,7 @@ app.post("/workspace/test-local/prepare", async (req, res) => {
|
|
|
3105
3194
|
endpoints: payloadDocs.endpoints
|
|
3106
3195
|
});
|
|
3107
3196
|
} catch (err) {
|
|
3108
|
-
console.error("
|
|
3197
|
+
console.error("[TEST-LOCAL] Prepare failed:", err.message);
|
|
3109
3198
|
TEST_LOCAL_STATE.status = "error";
|
|
3110
3199
|
res.status(500).json({ ok: false, error: err.message });
|
|
3111
3200
|
}
|
|
@@ -3307,9 +3396,9 @@ app.post("/workspace/scan-files", async (req, res) => {
|
|
|
3307
3396
|
});
|
|
3308
3397
|
}
|
|
3309
3398
|
|
|
3310
|
-
console.log(`
|
|
3311
|
-
console.log(`
|
|
3312
|
-
console.log(`
|
|
3399
|
+
console.log(`[SCAN-FILES] Scanning: ${workspaceRoot}`);
|
|
3400
|
+
console.log(`Extensions: ${includeExtensions.join(", ")}`);
|
|
3401
|
+
console.log(`Exclude: ${excludePatterns.join(", ")}`);
|
|
3313
3402
|
|
|
3314
3403
|
try {
|
|
3315
3404
|
const files = [];
|
|
@@ -3359,13 +3448,13 @@ app.post("/workspace/scan-files", async (req, res) => {
|
|
|
3359
3448
|
}
|
|
3360
3449
|
}
|
|
3361
3450
|
} catch (error) {
|
|
3362
|
-
console.warn(`
|
|
3451
|
+
console.warn(`Cannot read directory ${dir}: ${error.message}`);
|
|
3363
3452
|
}
|
|
3364
3453
|
}
|
|
3365
3454
|
|
|
3366
3455
|
await scanDir(workspaceRoot);
|
|
3367
3456
|
|
|
3368
|
-
console.log(`
|
|
3457
|
+
console.log(`[SCAN-FILES] Found ${files.length} files`);
|
|
3369
3458
|
|
|
3370
3459
|
res.json({
|
|
3371
3460
|
success: true,
|
|
@@ -3375,7 +3464,7 @@ app.post("/workspace/scan-files", async (req, res) => {
|
|
|
3375
3464
|
});
|
|
3376
3465
|
|
|
3377
3466
|
} catch (error) {
|
|
3378
|
-
console.error(`
|
|
3467
|
+
console.error(`[SCAN-FILES] Error: ${error.message}`);
|
|
3379
3468
|
res.status(500).json({
|
|
3380
3469
|
success: false,
|
|
3381
3470
|
error: error.message,
|
|
@@ -3404,7 +3493,7 @@ app.post("/workspace/read-file", async (req, res) => {
|
|
|
3404
3493
|
});
|
|
3405
3494
|
}
|
|
3406
3495
|
|
|
3407
|
-
console.log(`
|
|
3496
|
+
console.log(`[READ-FILE] Reading: ${filePath}`);
|
|
3408
3497
|
|
|
3409
3498
|
try {
|
|
3410
3499
|
const fs = await import("fs/promises");
|
|
@@ -3435,7 +3524,7 @@ app.post("/workspace/read-file", async (req, res) => {
|
|
|
3435
3524
|
});
|
|
3436
3525
|
|
|
3437
3526
|
} catch (error) {
|
|
3438
|
-
console.error(`
|
|
3527
|
+
console.error(`[READ-FILE] Error: ${error.message}`);
|
|
3439
3528
|
res.status(404).json({
|
|
3440
3529
|
success: false,
|
|
3441
3530
|
error: error.message,
|
|
@@ -3464,7 +3553,7 @@ app.post("/workspace/read-files", async (req, res) => {
|
|
|
3464
3553
|
});
|
|
3465
3554
|
}
|
|
3466
3555
|
|
|
3467
|
-
console.log(`
|
|
3556
|
+
console.log(`[READ-FILES] Reading ${filePaths.length} files`);
|
|
3468
3557
|
|
|
3469
3558
|
try {
|
|
3470
3559
|
const fs = await import("fs/promises");
|
|
@@ -3491,7 +3580,7 @@ app.post("/workspace/read-files", async (req, res) => {
|
|
|
3491
3580
|
}
|
|
3492
3581
|
}
|
|
3493
3582
|
|
|
3494
|
-
console.log(`
|
|
3583
|
+
console.log(`[READ-FILES] Read ${Object.keys(files).length} files, ${errors.length} errors`);
|
|
3495
3584
|
|
|
3496
3585
|
res.json({
|
|
3497
3586
|
success: true,
|
|
@@ -3501,7 +3590,7 @@ app.post("/workspace/read-files", async (req, res) => {
|
|
|
3501
3590
|
});
|
|
3502
3591
|
|
|
3503
3592
|
} catch (error) {
|
|
3504
|
-
console.error(`
|
|
3593
|
+
console.error(`[READ-FILES] Error: ${error.message}`);
|
|
3505
3594
|
res.status(500).json({
|
|
3506
3595
|
success: false,
|
|
3507
3596
|
error: error.message
|
|
@@ -3519,7 +3608,7 @@ app.post("/workspace/read-files", async (req, res) => {
|
|
|
3519
3608
|
* Abre o file picker nativo do sistema operacional
|
|
3520
3609
|
*/
|
|
3521
3610
|
app.get("/system/folder-picker", async (req, res) => {
|
|
3522
|
-
console.log(`
|
|
3611
|
+
console.log(`[FOLDER-PICKER] Opening native folder picker...`);
|
|
3523
3612
|
|
|
3524
3613
|
try {
|
|
3525
3614
|
const platform = process.platform;
|
|
@@ -3602,7 +3691,7 @@ app.get("/system/folder-picker", async (req, res) => {
|
|
|
3602
3691
|
selectedPath = selectedPath.slice(0, -1);
|
|
3603
3692
|
}
|
|
3604
3693
|
|
|
3605
|
-
console.log(`
|
|
3694
|
+
console.log(`[FOLDER-PICKER] Selected: ${selectedPath}`);
|
|
3606
3695
|
|
|
3607
3696
|
res.json({
|
|
3608
3697
|
success: true,
|
|
@@ -3611,7 +3700,7 @@ app.get("/system/folder-picker", async (req, res) => {
|
|
|
3611
3700
|
});
|
|
3612
3701
|
|
|
3613
3702
|
} catch (error) {
|
|
3614
|
-
console.error(`
|
|
3703
|
+
console.error(`[FOLDER-PICKER] Error:`, error.message);
|
|
3615
3704
|
res.status(500).json({
|
|
3616
3705
|
success: false,
|
|
3617
3706
|
error: error.message,
|
|
@@ -3653,7 +3742,7 @@ app.get("/workspace/api-docs", async (req, res) => {
|
|
|
3653
3742
|
});
|
|
3654
3743
|
}
|
|
3655
3744
|
|
|
3656
|
-
console.log(`
|
|
3745
|
+
console.log(`[API-DOCS] Analyzing controllers in ${wsRoot}`);
|
|
3657
3746
|
|
|
3658
3747
|
try {
|
|
3659
3748
|
// Primeiro, escanear arquivos do workspace
|
|
@@ -3692,13 +3781,13 @@ app.get("/workspace/api-docs", async (req, res) => {
|
|
|
3692
3781
|
|
|
3693
3782
|
await scanDir(wsRoot);
|
|
3694
3783
|
|
|
3695
|
-
console.log(`
|
|
3784
|
+
console.log(`[API-DOCS] Found ${files.length} Java files`);
|
|
3696
3785
|
|
|
3697
3786
|
// Agora analisar os controllers
|
|
3698
3787
|
const controllerAnalyzer = new ControllerAnalyzer(wsRoot);
|
|
3699
3788
|
const apiDocs = await controllerAnalyzer.generateApiDocs(files);
|
|
3700
3789
|
|
|
3701
|
-
console.log(`
|
|
3790
|
+
console.log(`[API-DOCS] Found ${apiDocs.totalEndpoints} endpoints in ${apiDocs.totalControllers} controllers`);
|
|
3702
3791
|
|
|
3703
3792
|
res.json({
|
|
3704
3793
|
success: true,
|
|
@@ -3710,7 +3799,7 @@ app.get("/workspace/api-docs", async (req, res) => {
|
|
|
3710
3799
|
});
|
|
3711
3800
|
|
|
3712
3801
|
} catch (error) {
|
|
3713
|
-
console.error(`
|
|
3802
|
+
console.error(`[API-DOCS] Error:`, error.message);
|
|
3714
3803
|
|
|
3715
3804
|
res.json({
|
|
3716
3805
|
success: false,
|
|
@@ -3737,7 +3826,7 @@ app.get("/workspace/api-docs/:controller", async (req, res) => {
|
|
|
3737
3826
|
}
|
|
3738
3827
|
|
|
3739
3828
|
const { controller } = req.params;
|
|
3740
|
-
console.log(`
|
|
3829
|
+
console.log(`[API-DOCS] Getting endpoints for controller: ${controller}`);
|
|
3741
3830
|
|
|
3742
3831
|
try {
|
|
3743
3832
|
// Primeiro, escanear arquivos do workspace
|
|
@@ -3812,7 +3901,7 @@ app.get("/workspace/api-docs/:controller", async (req, res) => {
|
|
|
3812
3901
|
});
|
|
3813
3902
|
|
|
3814
3903
|
} catch (error) {
|
|
3815
|
-
console.error(`
|
|
3904
|
+
console.error(`[API-DOCS] Error:`, error.message);
|
|
3816
3905
|
res.status(500).json({
|
|
3817
3906
|
success: false,
|
|
3818
3907
|
error: error.message
|
|
@@ -3902,7 +3991,7 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3902
3991
|
return res.status(400).json({ error: "workspace not set" });
|
|
3903
3992
|
}
|
|
3904
3993
|
|
|
3905
|
-
console.log("
|
|
3994
|
+
console.log("[SMART-CONFIG] Collecting configuration files...");
|
|
3906
3995
|
|
|
3907
3996
|
try {
|
|
3908
3997
|
const configPatterns = [
|
|
@@ -3936,9 +4025,9 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3936
4025
|
: content,
|
|
3937
4026
|
size: content.length
|
|
3938
4027
|
});
|
|
3939
|
-
console.log(`
|
|
4028
|
+
console.log(`Found: ${pattern}`);
|
|
3940
4029
|
} catch (err) {
|
|
3941
|
-
console.error(`
|
|
4030
|
+
console.error(`Error reading ${pattern}: ${err.message}`);
|
|
3942
4031
|
}
|
|
3943
4032
|
}
|
|
3944
4033
|
}
|
|
@@ -3954,7 +4043,7 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3954
4043
|
});
|
|
3955
4044
|
}
|
|
3956
4045
|
|
|
3957
|
-
console.log(`
|
|
4046
|
+
console.log(`Collected ${collectedFiles.length} files, profiles: ${availableProfiles.join(', ')}`);
|
|
3958
4047
|
|
|
3959
4048
|
res.json({
|
|
3960
4049
|
ok: true,
|
|
@@ -3968,7 +4057,7 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3968
4057
|
});
|
|
3969
4058
|
|
|
3970
4059
|
} catch (err) {
|
|
3971
|
-
console.error("
|
|
4060
|
+
console.error("[SMART-CONFIG] Error:", err.message);
|
|
3972
4061
|
res.status(500).json({ ok: false, error: err.message });
|
|
3973
4062
|
}
|
|
3974
4063
|
});
|
|
@@ -3979,7 +4068,7 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3979
4068
|
*/
|
|
3980
4069
|
app.post("/workspace/test-local/restart", async (req, res) => {
|
|
3981
4070
|
try {
|
|
3982
|
-
console.log("
|
|
4071
|
+
console.log("[TEST-LOCAL] Restarting server...");
|
|
3983
4072
|
|
|
3984
4073
|
// Stop
|
|
3985
4074
|
await processManager.stop('test-local');
|
|
@@ -4024,7 +4113,7 @@ app.post("/workspace/test-local/restart", async (req, res) => {
|
|
|
4024
4113
|
}
|
|
4025
4114
|
|
|
4026
4115
|
} catch (err) {
|
|
4027
|
-
console.error("
|
|
4116
|
+
console.error("[TEST-LOCAL] Restart failed:", err.message);
|
|
4028
4117
|
res.status(500).json({ ok: false, error: err.message });
|
|
4029
4118
|
}
|
|
4030
4119
|
});
|
|
@@ -4058,7 +4147,7 @@ app.post("/workspace/search", async (req, res) => {
|
|
|
4058
4147
|
return res.status(400).json({ ok: false, error: "pattern is required" });
|
|
4059
4148
|
}
|
|
4060
4149
|
|
|
4061
|
-
console.log(`
|
|
4150
|
+
console.log(`[workspace/search] pattern="${pattern}" filter="${fileFilter || '*'}"`);
|
|
4062
4151
|
|
|
4063
4152
|
try {
|
|
4064
4153
|
const { execSync } = await import('child_process');
|
|
@@ -4116,7 +4205,7 @@ app.post("/workspace/search", async (req, res) => {
|
|
|
4116
4205
|
.join('\n');
|
|
4117
4206
|
|
|
4118
4207
|
const count = results.split('\n').filter(l => l.trim()).length;
|
|
4119
|
-
console.log(`
|
|
4208
|
+
console.log(`Found ${count} matches`);
|
|
4120
4209
|
|
|
4121
4210
|
res.json({
|
|
4122
4211
|
ok: true,
|
|
@@ -4124,7 +4213,7 @@ app.post("/workspace/search", async (req, res) => {
|
|
|
4124
4213
|
count: count
|
|
4125
4214
|
});
|
|
4126
4215
|
} catch (err) {
|
|
4127
|
-
console.error(`
|
|
4216
|
+
console.error(`[workspace/search] Error:`, err.message);
|
|
4128
4217
|
res.status(500).json({ ok: false, error: err.message });
|
|
4129
4218
|
}
|
|
4130
4219
|
});
|
|
@@ -4164,14 +4253,14 @@ app.post("/workspace/exec", async (req, res) => {
|
|
|
4164
4253
|
];
|
|
4165
4254
|
const lowerCmd = command.toLowerCase().trim();
|
|
4166
4255
|
if (dangerous.some(d => lowerCmd.includes(d))) {
|
|
4167
|
-
console.warn(`
|
|
4256
|
+
console.warn(`[workspace/exec] BLOCKED dangerous command: ${command}`);
|
|
4168
4257
|
return res.status(403).json({
|
|
4169
4258
|
ok: false,
|
|
4170
4259
|
error: "Command blocked for security reasons"
|
|
4171
4260
|
});
|
|
4172
4261
|
}
|
|
4173
4262
|
|
|
4174
|
-
console.log(`
|
|
4263
|
+
console.log(`[workspace/exec] Running: ${command.substring(0, 120)}...`);
|
|
4175
4264
|
|
|
4176
4265
|
try {
|
|
4177
4266
|
const { exec: execCb } = await import('child_process');
|
|
@@ -4185,7 +4274,7 @@ app.post("/workspace/exec", async (req, res) => {
|
|
|
4185
4274
|
env: { ...process.env, FORCE_COLOR: '0' }
|
|
4186
4275
|
});
|
|
4187
4276
|
|
|
4188
|
-
console.log(`
|
|
4277
|
+
console.log(`Command completed (stdout: ${result.stdout.length} chars)`);
|
|
4189
4278
|
|
|
4190
4279
|
res.json({
|
|
4191
4280
|
ok: true,
|
|
@@ -4194,7 +4283,7 @@ app.post("/workspace/exec", async (req, res) => {
|
|
|
4194
4283
|
exitCode: 0
|
|
4195
4284
|
});
|
|
4196
4285
|
} catch (err) {
|
|
4197
|
-
console.error(`
|
|
4286
|
+
console.error(`Command failed (exit: ${err.code}):`, err.message?.substring(0, 200));
|
|
4198
4287
|
res.json({
|
|
4199
4288
|
ok: false,
|
|
4200
4289
|
stdout: err.stdout || '',
|
|
@@ -4227,7 +4316,7 @@ app.post("/workspace/test-endpoint", async (req, res) => {
|
|
|
4227
4316
|
return res.status(403).json({ error: "Only localhost URLs allowed for security" });
|
|
4228
4317
|
}
|
|
4229
4318
|
|
|
4230
|
-
console.log(`
|
|
4319
|
+
console.log(`Testing: ${method} ${url} (${testName || 'unnamed'})`);
|
|
4231
4320
|
|
|
4232
4321
|
const startTime = Date.now();
|
|
4233
4322
|
|
|
@@ -4277,7 +4366,7 @@ app.post("/workspace/test-endpoint", async (req, res) => {
|
|
|
4277
4366
|
: response.status >= 200 && response.status < 400
|
|
4278
4367
|
};
|
|
4279
4368
|
|
|
4280
|
-
console.log(
|
|
4369
|
+
console.log(`${response.status} ${response.statusText} (${durationMs}ms) ${result.passed ? '' : ''}`);
|
|
4281
4370
|
res.json(result);
|
|
4282
4371
|
|
|
4283
4372
|
} catch (err) {
|
|
@@ -4353,7 +4442,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4353
4442
|
try {
|
|
4354
4443
|
const stashResult = execSync('git stash --include-untracked 2>&1', opts).trim();
|
|
4355
4444
|
hadStash = !stashResult.includes('No local changes');
|
|
4356
|
-
if (hadStash) console.log('
|
|
4445
|
+
if (hadStash) console.log('Stashed uncommitted changes');
|
|
4357
4446
|
} catch {}
|
|
4358
4447
|
|
|
4359
4448
|
// 4. Create and checkout new branch (with unique name if exists)
|
|
@@ -4377,7 +4466,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4377
4466
|
try {
|
|
4378
4467
|
execSync('git stash pop', opts);
|
|
4379
4468
|
} catch (e) {
|
|
4380
|
-
console.warn('
|
|
4469
|
+
console.warn('Stash pop had conflicts, trying apply:', e.message);
|
|
4381
4470
|
try { execSync('git stash apply', opts); } catch {}
|
|
4382
4471
|
}
|
|
4383
4472
|
}
|
|
@@ -4399,6 +4488,13 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4399
4488
|
}
|
|
4400
4489
|
|
|
4401
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
|
+
}
|
|
4402
4498
|
const safeMsg = commitMessage.replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
4403
4499
|
execSync(`git commit -m "${safeMsg}"`, opts);
|
|
4404
4500
|
|
|
@@ -4406,7 +4502,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4406
4502
|
let pushResult = '';
|
|
4407
4503
|
let pushed = false;
|
|
4408
4504
|
|
|
4409
|
-
console.log(`
|
|
4505
|
+
console.log(`Git push gitToken provided: ${!!gitToken}, repoUrl provided: ${!!repoUrl}`);
|
|
4410
4506
|
|
|
4411
4507
|
// If gitToken provided, set up authenticated remote URL for push
|
|
4412
4508
|
if (gitToken) {
|
|
@@ -4418,11 +4514,11 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4418
4514
|
// No remote configured use repoUrl from integration if available
|
|
4419
4515
|
if (repoUrl) {
|
|
4420
4516
|
remoteUrlRaw = repoUrl;
|
|
4421
|
-
console.log(`
|
|
4517
|
+
console.log(`No git remote, using repoUrl from integration: ${repoUrl}`);
|
|
4422
4518
|
}
|
|
4423
4519
|
}
|
|
4424
4520
|
|
|
4425
|
-
console.log(`
|
|
4521
|
+
console.log(`Remote URL: ${remoteUrlRaw.replace(gitToken, '***')}`);
|
|
4426
4522
|
let authenticatedUrl = '';
|
|
4427
4523
|
|
|
4428
4524
|
if (remoteUrlRaw.includes('github.com')) {
|
|
@@ -4431,7 +4527,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4431
4527
|
.replace(/^git@github\.com:/, '')
|
|
4432
4528
|
.replace(/\.git$/, '');
|
|
4433
4529
|
authenticatedUrl = `https://x-access-token:${gitToken}@github.com/${repoPath}.git`;
|
|
4434
|
-
console.log(`
|
|
4530
|
+
console.log(`GitHub auth URL: https://x-access-token:***@github.com/${repoPath}.git`);
|
|
4435
4531
|
} else if (remoteUrlRaw.includes('gitlab.com') || remoteUrlRaw.includes('gitlab')) {
|
|
4436
4532
|
const repoPath = remoteUrlRaw
|
|
4437
4533
|
.replace(/^https?:\/\/(.*@)?[^\/]+\//, '')
|
|
@@ -4439,7 +4535,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4439
4535
|
.replace(/\.git$/, '');
|
|
4440
4536
|
const host = remoteUrlRaw.match(/https?:\/\/([^\/]+)/)?.[1] || 'gitlab.com';
|
|
4441
4537
|
authenticatedUrl = `https://oauth2:${gitToken}@${host}/${repoPath}.git`;
|
|
4442
|
-
console.log(`
|
|
4538
|
+
console.log(`GitLab auth URL: https://oauth2:***@${host}/${repoPath}.git`);
|
|
4443
4539
|
} else if (remoteUrlRaw.includes('bitbucket.org') || remoteUrlRaw.includes('bitbucket')) {
|
|
4444
4540
|
const repoPath = remoteUrlRaw
|
|
4445
4541
|
.replace(/^https?:\/\/(.*@)?bitbucket\.org\//, '')
|
|
@@ -4453,11 +4549,11 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4453
4549
|
const urlObj = new URL(remoteUrlRaw.replace(/^git@([^:]+):/, 'https://$1/'));
|
|
4454
4550
|
authenticatedUrl = `https://oauth2:${gitToken}@${urlObj.host}${urlObj.pathname}`;
|
|
4455
4551
|
if (!authenticatedUrl.endsWith('.git')) authenticatedUrl += '.git';
|
|
4456
|
-
console.log(`
|
|
4552
|
+
console.log(`Generic auth URL for ${urlObj.host}`);
|
|
4457
4553
|
}
|
|
4458
4554
|
|
|
4459
4555
|
if (authenticatedUrl) {
|
|
4460
|
-
console.log(`
|
|
4556
|
+
console.log(`Pushing ${finalBranchName} with token auth...`);
|
|
4461
4557
|
try {
|
|
4462
4558
|
// Don't use 2>&1 let execSync capture stderr separately
|
|
4463
4559
|
pushResult = execSync(`git push ${authenticatedUrl} ${finalBranchName}`, {
|
|
@@ -4466,20 +4562,20 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4466
4562
|
stdio: ['pipe', 'pipe', 'pipe'] // capture stdin, stdout, stderr separately
|
|
4467
4563
|
}).toString();
|
|
4468
4564
|
pushed = true;
|
|
4469
|
-
console.log(`
|
|
4565
|
+
console.log(`Pushed with token authentication`);
|
|
4470
4566
|
} catch (innerErr) {
|
|
4471
4567
|
// Mask token in error messages before logging
|
|
4472
4568
|
const maskToken = (str) => str ? str.replace(gitToken, '***TOKEN***') : '';
|
|
4473
4569
|
const errMsg = maskToken(innerErr.stderr?.toString() || innerErr.stdout?.toString() || innerErr.message || '');
|
|
4474
|
-
console.warn(`
|
|
4570
|
+
console.warn(`Authenticated push failed: ${errMsg}`);
|
|
4475
4571
|
pushResult = errMsg;
|
|
4476
4572
|
}
|
|
4477
4573
|
} else {
|
|
4478
|
-
console.warn(`
|
|
4574
|
+
console.warn(`Could not construct authenticated URL from: ${remoteUrlRaw}`);
|
|
4479
4575
|
}
|
|
4480
4576
|
} catch (pushErr) {
|
|
4481
4577
|
const maskToken = (str) => str ? str.replace(gitToken, '***TOKEN***') : '';
|
|
4482
|
-
console.warn(`
|
|
4578
|
+
console.warn(`Git auth setup failed: ${maskToken(pushErr.message)}`);
|
|
4483
4579
|
pushResult = maskToken(pushErr.message) || 'Push setup failed';
|
|
4484
4580
|
}
|
|
4485
4581
|
}
|
|
@@ -4494,7 +4590,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4494
4590
|
pushResult = execSync(`git push origin ${finalBranchName} 2>&1`, opts).toString();
|
|
4495
4591
|
pushed = true;
|
|
4496
4592
|
} catch (pushErr2) {
|
|
4497
|
-
console.warn(`
|
|
4593
|
+
console.warn(`Git push failed: ${pushErr2.message}`);
|
|
4498
4594
|
pushResult = pushErr2.message || 'Push failed no remote configured or auth required';
|
|
4499
4595
|
}
|
|
4500
4596
|
}
|
|
@@ -4553,8 +4649,8 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4553
4649
|
console.warn('Could not detect remote URL:', e.message);
|
|
4554
4650
|
}
|
|
4555
4651
|
|
|
4556
|
-
console.log(`
|
|
4557
|
-
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}`);
|
|
4558
4654
|
|
|
4559
4655
|
// 10. Switch back to original branch and merge fix to keep changes on disk
|
|
4560
4656
|
try {
|
|
@@ -4563,7 +4659,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4563
4659
|
// This keeps the diff viewer working (disk has patched version)
|
|
4564
4660
|
try {
|
|
4565
4661
|
execSync(`git merge ${finalBranchName} --no-edit`, opts);
|
|
4566
|
-
console.log(`
|
|
4662
|
+
console.log(`Merged ${finalBranchName} into ${currentBranch}`);
|
|
4567
4663
|
} catch (mergeErr) {
|
|
4568
4664
|
// If merge fails (conflicts), just cherry-pick the changes without committing
|
|
4569
4665
|
try {
|
|
@@ -4577,7 +4673,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4577
4673
|
execSync(`git checkout ${finalBranchName} -- ${f}`, opts);
|
|
4578
4674
|
} catch {}
|
|
4579
4675
|
}
|
|
4580
|
-
console.log(`
|
|
4676
|
+
console.log(`Cherry-picked ${changedFiles.length} file(s) from ${finalBranchName}`);
|
|
4581
4677
|
} catch {}
|
|
4582
4678
|
}
|
|
4583
4679
|
} catch {}
|
|
@@ -4624,14 +4720,14 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4624
4720
|
if (response.ok) {
|
|
4625
4721
|
const prData = await response.json();
|
|
4626
4722
|
prUrl = prData.html_url;
|
|
4627
|
-
console.log(`
|
|
4723
|
+
console.log(`GitHub PR created: ${prUrl}`);
|
|
4628
4724
|
} else {
|
|
4629
4725
|
const errText = await response.text();
|
|
4630
|
-
console.warn(`
|
|
4726
|
+
console.warn(`GitHub PR creation failed (${response.status}): ${errText.substring(0, 200)}`);
|
|
4631
4727
|
prUrl = mrUrl;
|
|
4632
4728
|
}
|
|
4633
4729
|
} catch (prErr) {
|
|
4634
|
-
console.warn(`
|
|
4730
|
+
console.warn(`GitHub PR error: ${prErr.message}`);
|
|
4635
4731
|
prUrl = mrUrl;
|
|
4636
4732
|
}
|
|
4637
4733
|
|
|
@@ -4665,14 +4761,14 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4665
4761
|
if (response.ok) {
|
|
4666
4762
|
const mrData = await response.json();
|
|
4667
4763
|
prUrl = mrData.web_url;
|
|
4668
|
-
console.log(`
|
|
4764
|
+
console.log(`GitLab MR created: ${prUrl}`);
|
|
4669
4765
|
} else {
|
|
4670
4766
|
const errText = await response.text();
|
|
4671
|
-
console.warn(`
|
|
4767
|
+
console.warn(`GitLab MR creation failed (${response.status}): ${errText.substring(0, 200)}`);
|
|
4672
4768
|
prUrl = mrUrl;
|
|
4673
4769
|
}
|
|
4674
4770
|
} catch (glErr) {
|
|
4675
|
-
console.warn(`
|
|
4771
|
+
console.warn(`GitLab MR error: ${glErr.message}`);
|
|
4676
4772
|
prUrl = mrUrl;
|
|
4677
4773
|
}
|
|
4678
4774
|
|
|
@@ -4725,14 +4821,14 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4725
4821
|
if (response.ok) {
|
|
4726
4822
|
const prData = await response.json();
|
|
4727
4823
|
prUrl = prData.links && prData.links.html ? prData.links.html.href : mrUrl;
|
|
4728
|
-
console.log(`
|
|
4824
|
+
console.log(`Bitbucket PR created: ${prUrl}`);
|
|
4729
4825
|
} else {
|
|
4730
4826
|
const errText = await response.text();
|
|
4731
|
-
console.warn(`
|
|
4827
|
+
console.warn(`Bitbucket PR creation failed (${response.status}): ${errText.substring(0, 200)}`);
|
|
4732
4828
|
prUrl = mrUrl;
|
|
4733
4829
|
}
|
|
4734
4830
|
} catch (bbErr) {
|
|
4735
|
-
console.warn(`
|
|
4831
|
+
console.warn(`Bitbucket PR error: ${bbErr.message}`);
|
|
4736
4832
|
prUrl = mrUrl;
|
|
4737
4833
|
}
|
|
4738
4834
|
}
|
|
@@ -4755,7 +4851,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4755
4851
|
});
|
|
4756
4852
|
|
|
4757
4853
|
} catch (err) {
|
|
4758
|
-
console.error(`
|
|
4854
|
+
console.error(`Git branch creation failed: ${err.message}`);
|
|
4759
4855
|
res.status(500).json({ ok: false, error: err.message });
|
|
4760
4856
|
}
|
|
4761
4857
|
});
|
|
@@ -4867,7 +4963,7 @@ app.get("/workspace/git/pr/comments", async (req, res) => {
|
|
|
4867
4963
|
unresolvedCount: comments.filter(c => c.resolved === false || c.resolved === undefined).length
|
|
4868
4964
|
});
|
|
4869
4965
|
} catch (err) {
|
|
4870
|
-
console.error('
|
|
4966
|
+
console.error('Failed to fetch PR comments:', err.message);
|
|
4871
4967
|
res.status(500).json({ ok: false, error: err.message });
|
|
4872
4968
|
}
|
|
4873
4969
|
});
|
|
@@ -4904,7 +5000,7 @@ app.get("/workspace/git/pr/comments/:commentId", async (req, res) => {
|
|
|
4904
5000
|
|
|
4905
5001
|
res.json({ ok: true, comment });
|
|
4906
5002
|
} catch (err) {
|
|
4907
|
-
console.error('
|
|
5003
|
+
console.error('Failed to fetch PR comment:', err.message);
|
|
4908
5004
|
res.status(500).json({ ok: false, error: err.message });
|
|
4909
5005
|
}
|
|
4910
5006
|
});
|
|
@@ -4966,10 +5062,10 @@ app.post("/workspace/git/pr/comments", async (req, res) => {
|
|
|
4966
5062
|
comment = await provider.instance.addPRComment(owner, repo, Number(prNumber), body);
|
|
4967
5063
|
}
|
|
4968
5064
|
|
|
4969
|
-
console.log(`
|
|
5065
|
+
console.log(`PR comment added to PR #${prNumber}: ${body.substring(0, 50)}...`);
|
|
4970
5066
|
res.json({ ok: true, comment });
|
|
4971
5067
|
} catch (err) {
|
|
4972
|
-
console.error('
|
|
5068
|
+
console.error('Failed to add PR comment:', err.message);
|
|
4973
5069
|
res.status(500).json({ ok: false, error: err.message });
|
|
4974
5070
|
}
|
|
4975
5071
|
});
|
|
@@ -5004,10 +5100,10 @@ app.post("/workspace/git/pr/comments/:commentId/resolve", async (req, res) => {
|
|
|
5004
5100
|
const { owner, repo } = provider.info;
|
|
5005
5101
|
const result = await provider.instance.resolvePRComment(owner, repo, Number(prNumber), commentId);
|
|
5006
5102
|
|
|
5007
|
-
console.log(`
|
|
5103
|
+
console.log(`PR comment ${commentId} resolved on PR #${prNumber}`);
|
|
5008
5104
|
res.json({ ok: true, commentId, ...result });
|
|
5009
5105
|
} catch (err) {
|
|
5010
|
-
console.error('
|
|
5106
|
+
console.error('Failed to resolve PR comment:', err.message);
|
|
5011
5107
|
res.status(500).json({ ok: false, error: err.message });
|
|
5012
5108
|
}
|
|
5013
5109
|
});
|
|
@@ -5042,10 +5138,10 @@ app.post("/workspace/git/pr/comments/:commentId/unresolve", async (req, res) =>
|
|
|
5042
5138
|
const { owner, repo } = provider.info;
|
|
5043
5139
|
const result = await provider.instance.unresolvePRComment(owner, repo, Number(prNumber), commentId);
|
|
5044
5140
|
|
|
5045
|
-
console.log(`
|
|
5141
|
+
console.log(`PR comment ${commentId} unresolve on PR #${prNumber}`);
|
|
5046
5142
|
res.json({ ok: true, commentId, ...result });
|
|
5047
5143
|
} catch (err) {
|
|
5048
|
-
console.error('
|
|
5144
|
+
console.error('Failed to unresolve PR comment:', err.message);
|
|
5049
5145
|
res.status(500).json({ ok: false, error: err.message });
|
|
5050
5146
|
}
|
|
5051
5147
|
});
|
|
@@ -5090,9 +5186,9 @@ app.post("/workspace/git/pr/comments/:commentId/fix-and-resolve", async (req, re
|
|
|
5090
5186
|
return res.status(404).json({ error: "Comment not found" });
|
|
5091
5187
|
}
|
|
5092
5188
|
|
|
5093
|
-
console.log(`
|
|
5094
|
-
console.log(`
|
|
5095
|
-
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)}...`);
|
|
5096
5192
|
|
|
5097
5193
|
// 2. Return the comment info for the Gateway to orchestrate the AI fix
|
|
5098
5194
|
// The actual AI analysis + patch is handled by the Gateway's AI service,
|
|
@@ -5122,7 +5218,7 @@ app.post("/workspace/git/pr/comments/:commentId/fix-and-resolve", async (req, re
|
|
|
5122
5218
|
message: 'Comment fetched. Gateway should now: 1) Read file, 2) AI analyze, 3) Apply patch, 4) Commit, 5) Reply, 6) Resolve'
|
|
5123
5219
|
});
|
|
5124
5220
|
} catch (err) {
|
|
5125
|
-
console.error('
|
|
5221
|
+
console.error('Failed to prepare fix-and-resolve:', err.message);
|
|
5126
5222
|
res.status(500).json({ ok: false, error: err.message });
|
|
5127
5223
|
}
|
|
5128
5224
|
});
|
|
@@ -5145,7 +5241,7 @@ async function _getGitProvider() {
|
|
|
5145
5241
|
try {
|
|
5146
5242
|
remoteUrl = execSync('git remote get-url origin', opts).trim();
|
|
5147
5243
|
} catch {
|
|
5148
|
-
console.warn('
|
|
5244
|
+
console.warn('No git remote found');
|
|
5149
5245
|
return null;
|
|
5150
5246
|
}
|
|
5151
5247
|
|
|
@@ -5155,7 +5251,7 @@ async function _getGitProvider() {
|
|
|
5155
5251
|
const providerId = registry.detectProviderFromUrl(remoteUrl);
|
|
5156
5252
|
|
|
5157
5253
|
if (!providerId) {
|
|
5158
|
-
console.warn(`
|
|
5254
|
+
console.warn(`Unknown git provider for URL: ${remoteUrl}`);
|
|
5159
5255
|
return null;
|
|
5160
5256
|
}
|
|
5161
5257
|
|
|
@@ -5170,7 +5266,7 @@ async function _getGitProvider() {
|
|
|
5170
5266
|
}
|
|
5171
5267
|
|
|
5172
5268
|
if (!token) {
|
|
5173
|
-
console.warn(`
|
|
5269
|
+
console.warn(`No token found for ${providerId}. Set ${providerId.toUpperCase()}_TOKEN or GIT_TOKEN env var.`);
|
|
5174
5270
|
return null;
|
|
5175
5271
|
}
|
|
5176
5272
|
|
|
@@ -5190,7 +5286,7 @@ async function _getGitProvider() {
|
|
|
5190
5286
|
|
|
5191
5287
|
return { instance, info, providerId, remoteUrl };
|
|
5192
5288
|
} catch (err) {
|
|
5193
|
-
console.error('
|
|
5289
|
+
console.error('Failed to initialize git provider:', err.message);
|
|
5194
5290
|
return null;
|
|
5195
5291
|
}
|
|
5196
5292
|
}
|
|
@@ -5280,7 +5376,7 @@ app.post("/workspace/:workspaceId/run", async (req, res) => {
|
|
|
5280
5376
|
// ============================================
|
|
5281
5377
|
async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
5282
5378
|
if (!gatewayUrl || !apiKey || !tenantId) {
|
|
5283
|
-
console.log('
|
|
5379
|
+
console.log('WebSocket tunnel skipped: missing gatewayUrl, apiKey or tenantId in ~/.deepdebug/config.json');
|
|
5284
5380
|
return;
|
|
5285
5381
|
}
|
|
5286
5382
|
|
|
@@ -5298,7 +5394,7 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5298
5394
|
ws = new WebSocket(fullUrl);
|
|
5299
5395
|
|
|
5300
5396
|
ws.on('open', () => {
|
|
5301
|
-
console.log(`
|
|
5397
|
+
console.log(`WebSocket tunnel connected to Gateway`);
|
|
5302
5398
|
reconnectDelay = 2000;
|
|
5303
5399
|
ws.send(JSON.stringify({ type: 'register', tenantId, port, workspacePath: WORKSPACE_ROOT || null }));
|
|
5304
5400
|
const hb = setInterval(() => {
|
|
@@ -5314,7 +5410,7 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5314
5410
|
try { request = JSON.parse(text); } catch { return; }
|
|
5315
5411
|
const { requestId, command, params } = request;
|
|
5316
5412
|
if (!requestId || !command) return;
|
|
5317
|
-
console.log(`
|
|
5413
|
+
console.log(`WS command: ${command}`);
|
|
5318
5414
|
let result;
|
|
5319
5415
|
try { result = await handleWsCommand(command, params || {}); }
|
|
5320
5416
|
catch (err) { result = { error: err.message }; }
|
|
@@ -5323,19 +5419,19 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5323
5419
|
|
|
5324
5420
|
ws.on('close', () => {
|
|
5325
5421
|
if (!isShuttingDown) {
|
|
5326
|
-
console.log(`
|
|
5422
|
+
console.log(`WS disconnected. Reconnecting in ${reconnectDelay/1000}s...`);
|
|
5327
5423
|
setTimeout(connect, reconnectDelay);
|
|
5328
5424
|
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
|
|
5329
5425
|
}
|
|
5330
5426
|
});
|
|
5331
5427
|
|
|
5332
|
-
ws.on('error', (err) => console.warn(`
|
|
5428
|
+
ws.on('error', (err) => console.warn(`WS error: ${err.message}`));
|
|
5333
5429
|
|
|
5334
5430
|
} catch (err) {
|
|
5335
5431
|
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
5336
|
-
console.warn('
|
|
5432
|
+
console.warn('WebSocket tunnel requires "ws" package. Run: npm install ws');
|
|
5337
5433
|
} else {
|
|
5338
|
-
console.warn(`
|
|
5434
|
+
console.warn(`WS tunnel error: ${err.message}`);
|
|
5339
5435
|
setTimeout(connect, reconnectDelay);
|
|
5340
5436
|
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
|
|
5341
5437
|
}
|
|
@@ -5406,7 +5502,7 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5406
5502
|
return { error: 'backupId or incidentId is required' };
|
|
5407
5503
|
}
|
|
5408
5504
|
|
|
5409
|
-
console.log(`
|
|
5505
|
+
console.log(`WS workspace.diff ${endpoint}`);
|
|
5410
5506
|
const res = await fetch(`${localBase}${endpoint}`, {
|
|
5411
5507
|
method: 'GET',
|
|
5412
5508
|
headers: { 'Content-Type': 'application/json' }
|
|
@@ -5540,7 +5636,7 @@ async function startWebSocketTunnel(port, gatewayUrl, apiKey, tenantId) {
|
|
|
5540
5636
|
}
|
|
5541
5637
|
|
|
5542
5638
|
// ============================================
|
|
5543
|
-
//
|
|
5639
|
+
// SETUP AGENT ROUTES
|
|
5544
5640
|
// Supports the AI Assistant chat commands
|
|
5545
5641
|
// ============================================
|
|
5546
5642
|
|
|
@@ -5557,7 +5653,7 @@ app.post("/setup/run-command", async (req, res) => {
|
|
|
5557
5653
|
return res.status(403).json({ ok: false, error: "Command blocked for safety" });
|
|
5558
5654
|
}
|
|
5559
5655
|
|
|
5560
|
-
console.log(
|
|
5656
|
+
console.log(`[SetupAgent] Running: ${command.substring(0, 100)}`);
|
|
5561
5657
|
const { exec: execCb } = await import('child_process');
|
|
5562
5658
|
const { promisify } = await import('util');
|
|
5563
5659
|
const execAsync = promisify(execCb);
|
|
@@ -5655,10 +5751,10 @@ app.post("/setup/run-auth-command", async (req, res) => {
|
|
|
5655
5751
|
|
|
5656
5752
|
const { exec: execCb } = await import('child_process');
|
|
5657
5753
|
const bgCmd = process.platform === 'win32' ? `start cmd /c "${command}"` : `${command} &`;
|
|
5658
|
-
console.log(
|
|
5754
|
+
console.log(`[SetupAgent] Starting auth: ${command.substring(0, 80)}`);
|
|
5659
5755
|
|
|
5660
5756
|
execCb(bgCmd, { shell: true }, (err) => {
|
|
5661
|
-
if (err) console.warn(
|
|
5757
|
+
if (err) console.warn(`Auth command warning: ${err.message}`);
|
|
5662
5758
|
});
|
|
5663
5759
|
|
|
5664
5760
|
res.json({ ok: true, status: "started", session_id, message: "Auth command started, browser should open" });
|
|
@@ -5702,13 +5798,13 @@ app.get("/setup/auth-status", async (req, res) => {
|
|
|
5702
5798
|
// ============================================
|
|
5703
5799
|
app.listen(PORT, '0.0.0.0', async () => {
|
|
5704
5800
|
console.log(`\n DeepDebug Local Agent listening on port ${PORT}`);
|
|
5705
|
-
console.log(`
|
|
5706
|
-
console.log(`
|
|
5707
|
-
console.log(`
|
|
5708
|
-
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'}`);
|
|
5709
5805
|
if (aiEngine) {
|
|
5710
|
-
console.log(`
|
|
5711
|
-
console.log(`
|
|
5806
|
+
console.log(`Gateway URL: ${aiEngine.gatewayUrl}`);
|
|
5807
|
+
console.log(`Max Retries: ${aiEngine.maxRetries}`);
|
|
5712
5808
|
}
|
|
5713
5809
|
console.log(`\n Ready to receive requests!\n`);
|
|
5714
5810
|
|
|
@@ -5720,7 +5816,7 @@ app.listen(PORT, '0.0.0.0', async () => {
|
|
|
5720
5816
|
|
|
5721
5817
|
const gwUrl = cfg.gatewayUrl || process.env.GATEWAY_URL;
|
|
5722
5818
|
const apiKey = cfg.apiKey || process.env.DEEPDEBUG_API_KEY;
|
|
5723
|
-
let tenantId = cfg.tenantId;
|
|
5819
|
+
let tenantId = cfg.tenantId || process.env.TENANT_ID;
|
|
5724
5820
|
if (!tenantId && apiKey) {
|
|
5725
5821
|
try {
|
|
5726
5822
|
const payload = JSON.parse(Buffer.from(apiKey.split('.')[1], 'base64').toString());
|
|
@@ -5737,8 +5833,8 @@ app.listen(PORT, '0.0.0.0', async () => {
|
|
|
5737
5833
|
try {
|
|
5738
5834
|
startMCPHttpServer(wsManager, parseInt(MCP_PORT));
|
|
5739
5835
|
} catch (err) {
|
|
5740
|
-
console.warn(`
|
|
5741
|
-
console.warn(`
|
|
5836
|
+
console.warn(`MCP HTTP Server failed to start: ${err.message}`);
|
|
5837
|
+
console.warn(`(MCP features disabled, REST API continues normally)`);
|
|
5742
5838
|
}
|
|
5743
5839
|
|
|
5744
5840
|
// Auto-register default workspace in WorkspaceManager
|