deepdebug-local-agent 1.0.11 → 1.0.13
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/git/github-provider.js +1 -2
- package/src/server.js +246 -148
package/package.json
CHANGED
|
@@ -780,7 +780,6 @@ export class GitHubProvider extends BaseGitProvider {
|
|
|
780
780
|
// - git@github.com:owner/repo.git
|
|
781
781
|
|
|
782
782
|
let match = url.match(/github\.com[/:]([\w-]+)\/([\w-]+?)(?:\.git)?$/);
|
|
783
|
-
|
|
784
783
|
if (match) {
|
|
785
784
|
return {
|
|
786
785
|
owner: match[1],
|
|
@@ -792,4 +791,4 @@ export class GitHubProvider extends BaseGitProvider {
|
|
|
792
791
|
}
|
|
793
792
|
}
|
|
794
793
|
|
|
795
|
-
export default GitHubProvider
|
|
794
|
+
export default GitHubProvider
|
package/src/server.js
CHANGED
|
@@ -583,6 +583,14 @@ if (WORKSPACE_ROOT) {
|
|
|
583
583
|
let DETECTED_SERVICES = [];
|
|
584
584
|
const processManager = new ProcessManager();
|
|
585
585
|
const wsManager = new WorkspaceManager();
|
|
586
|
+
|
|
587
|
+
function resolveWorkspaceRoot(req) {
|
|
588
|
+
const wsId = req.headers['x-workspace-id']
|
|
589
|
+
|| (req.body && req.body.workspaceId)
|
|
590
|
+
|| req.query.workspaceId;
|
|
591
|
+
if (wsId && wsManager.isOpen(wsId)) return wsManager.getRoot(wsId);
|
|
592
|
+
return WORKSPACE_ROOT;
|
|
593
|
+
}
|
|
586
594
|
const MCP_PORT = process.env.MCP_PORT || 5056;
|
|
587
595
|
|
|
588
596
|
// 🧠 Inicializar AI Vibe Coding Engine
|
|
@@ -702,7 +710,7 @@ function updateServiceStatus(serviceId, status) {
|
|
|
702
710
|
}
|
|
703
711
|
|
|
704
712
|
/** Health */
|
|
705
|
-
app.get("/health", (
|
|
713
|
+
app.get("/health", (req, res) => {
|
|
706
714
|
res.json({
|
|
707
715
|
status: "ok",
|
|
708
716
|
workspace: WORKSPACE_ROOT || null,
|
|
@@ -741,9 +749,24 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
741
749
|
await fsPromises.mkdir(cacheDir, { recursive: true });
|
|
742
750
|
|
|
743
751
|
// Build authenticated URL
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
752
|
+
// Token formats:
|
|
753
|
+
// GitHub: TOKEN (uses x-access-token:TOKEN@)
|
|
754
|
+
// Bitbucket: x-token-auth:TOKEN or username:appPassword
|
|
755
|
+
// GitLab: oauth2:TOKEN
|
|
756
|
+
let authUrl;
|
|
757
|
+
if (token) {
|
|
758
|
+
// Strip any existing username@ from URL first
|
|
759
|
+
let cleanUrl = repoUrl.replace(/https:\/\/[^@]+@/, 'https://');
|
|
760
|
+
if (token.includes(':')) {
|
|
761
|
+
// Already in user:pass format (Bitbucket/GitLab)
|
|
762
|
+
authUrl = cleanUrl.replace('https://', `https://${token}@`);
|
|
763
|
+
} else {
|
|
764
|
+
// Plain token — GitHub style
|
|
765
|
+
authUrl = cleanUrl.replace('https://', `https://x-access-token:${token}@`);
|
|
766
|
+
}
|
|
767
|
+
} else {
|
|
768
|
+
authUrl = repoUrl;
|
|
769
|
+
}
|
|
747
770
|
|
|
748
771
|
const gitDir = path.join(clonePath, '.git');
|
|
749
772
|
const alreadyCloned = await exists(gitDir);
|
|
@@ -770,7 +793,10 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
770
793
|
}
|
|
771
794
|
|
|
772
795
|
// Set as active workspace
|
|
773
|
-
|
|
796
|
+
// TENANT ISOLATION: Only set global for default
|
|
797
|
+
if (!workspaceId || workspaceId === "default") {
|
|
798
|
+
WORKSPACE_ROOT = clonePath;
|
|
799
|
+
}
|
|
774
800
|
const wsId = workspaceId || "default";
|
|
775
801
|
try { await wsManager.open(wsId, clonePath); } catch (err) {
|
|
776
802
|
console.warn(`⚠️ WorkspaceManager.open failed (non-fatal): ${err.message}`);
|
|
@@ -806,7 +832,10 @@ app.post("/workspace/open", async (req, res) => {
|
|
|
806
832
|
const abs = path.resolve(root);
|
|
807
833
|
if (!(await exists(abs))) return res.status(404).json({ error: "path not found" });
|
|
808
834
|
|
|
809
|
-
|
|
835
|
+
// TENANT ISOLATION: Only set global for default
|
|
836
|
+
if (wsId === "default") {
|
|
837
|
+
WORKSPACE_ROOT = abs;
|
|
838
|
+
}
|
|
810
839
|
|
|
811
840
|
// Registar no WorkspaceManager (multi-workspace support)
|
|
812
841
|
const wsId = workspaceId || "default";
|
|
@@ -887,19 +916,21 @@ app.post("/workspace/clone", async (req, res) => {
|
|
|
887
916
|
});
|
|
888
917
|
|
|
889
918
|
/** Info do workspace */
|
|
890
|
-
app.get("/workspace/info", async (
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
const
|
|
894
|
-
|
|
919
|
+
app.get("/workspace/info", async (req, res) => {
|
|
920
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
921
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
922
|
+
const meta = await detectProject(wsRoot);
|
|
923
|
+
const port = await detectPort(wsRoot);
|
|
924
|
+
res.json({ root: wsRoot, meta, port });
|
|
895
925
|
});
|
|
896
926
|
|
|
897
927
|
/** Scan completo do workspace */
|
|
898
|
-
app.get("/workspace/scan", async (
|
|
899
|
-
|
|
928
|
+
app.get("/workspace/scan", async (req, res) => {
|
|
929
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
930
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
900
931
|
|
|
901
932
|
try {
|
|
902
|
-
const scanner = new WorkspaceScanner(
|
|
933
|
+
const scanner = new WorkspaceScanner(wsRoot);
|
|
903
934
|
const structure = await scanner.scan();
|
|
904
935
|
res.json(structure);
|
|
905
936
|
} catch (err) {
|
|
@@ -908,17 +939,18 @@ app.get("/workspace/scan", async (_req, res) => {
|
|
|
908
939
|
});
|
|
909
940
|
|
|
910
941
|
/** Análise completa: language + framework */
|
|
911
|
-
app.get("/workspace/analyze", async (
|
|
912
|
-
|
|
942
|
+
app.get("/workspace/analyze", async (req, res) => {
|
|
943
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
944
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
913
945
|
|
|
914
946
|
try {
|
|
915
|
-
const scanner = new WorkspaceScanner(
|
|
947
|
+
const scanner = new WorkspaceScanner(wsRoot);
|
|
916
948
|
const structure = await scanner.scan();
|
|
917
949
|
|
|
918
950
|
const languageDetector = new LanguageDetector(structure.files);
|
|
919
951
|
const languageInfo = languageDetector.detect();
|
|
920
952
|
|
|
921
|
-
const fileReader = new FileReader(
|
|
953
|
+
const fileReader = new FileReader(wsRoot);
|
|
922
954
|
const frameworkDetector = new FrameworkDetector(
|
|
923
955
|
languageInfo.primary,
|
|
924
956
|
structure.files,
|
|
@@ -927,7 +959,7 @@ app.get("/workspace/analyze", async (_req, res) => {
|
|
|
927
959
|
const frameworkInfo = await frameworkDetector.detect();
|
|
928
960
|
|
|
929
961
|
res.json({
|
|
930
|
-
workspace:
|
|
962
|
+
workspace: wsRoot,
|
|
931
963
|
language: languageInfo,
|
|
932
964
|
framework: frameworkInfo,
|
|
933
965
|
stats: structure.metadata,
|
|
@@ -940,13 +972,14 @@ app.get("/workspace/analyze", async (_req, res) => {
|
|
|
940
972
|
|
|
941
973
|
/** Lê conteúdo de arquivo específico */
|
|
942
974
|
app.get("/workspace/file-content", async (req, res) => {
|
|
943
|
-
|
|
975
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
976
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
944
977
|
|
|
945
978
|
const { path: relativePath } = req.query;
|
|
946
979
|
if (!relativePath) return res.status(400).json({ error: "path query param required" });
|
|
947
980
|
|
|
948
981
|
try {
|
|
949
|
-
const reader = new FileReader(
|
|
982
|
+
const reader = new FileReader(wsRoot);
|
|
950
983
|
const file = await reader.read(relativePath);
|
|
951
984
|
res.json(file);
|
|
952
985
|
} catch (err) {
|
|
@@ -956,7 +989,8 @@ app.get("/workspace/file-content", async (req, res) => {
|
|
|
956
989
|
|
|
957
990
|
/** Lê múltiplos arquivos */
|
|
958
991
|
app.post("/workspace/batch-read", async (req, res) => {
|
|
959
|
-
|
|
992
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
993
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
960
994
|
|
|
961
995
|
const { paths } = req.body || {};
|
|
962
996
|
if (!paths || !Array.isArray(paths)) {
|
|
@@ -964,7 +998,7 @@ app.post("/workspace/batch-read", async (req, res) => {
|
|
|
964
998
|
}
|
|
965
999
|
|
|
966
1000
|
try {
|
|
967
|
-
const reader = new FileReader(
|
|
1001
|
+
const reader = new FileReader(wsRoot);
|
|
968
1002
|
const files = await reader.readMultiple(paths);
|
|
969
1003
|
res.json(files);
|
|
970
1004
|
} catch (err) {
|
|
@@ -981,7 +1015,8 @@ app.post("/workspace/batch-read", async (req, res) => {
|
|
|
981
1015
|
* Checks if a file exists in the workspace
|
|
982
1016
|
*/
|
|
983
1017
|
app.get("/workspace/file-exists", async (req, res) => {
|
|
984
|
-
|
|
1018
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1019
|
+
if (!wsRoot) {
|
|
985
1020
|
return res.status(400).json({ error: "workspace not set" });
|
|
986
1021
|
}
|
|
987
1022
|
|
|
@@ -991,7 +1026,7 @@ app.get("/workspace/file-exists", async (req, res) => {
|
|
|
991
1026
|
}
|
|
992
1027
|
|
|
993
1028
|
try {
|
|
994
|
-
const fullPath = path.join(
|
|
1029
|
+
const fullPath = path.join(wsRoot, relativePath);
|
|
995
1030
|
const fileExists = await exists(fullPath);
|
|
996
1031
|
|
|
997
1032
|
console.log(`🔍 [file-exists] ${relativePath} -> ${fileExists ? 'EXISTS' : 'NOT FOUND'}`);
|
|
@@ -1013,7 +1048,8 @@ app.get("/workspace/file-exists", async (req, res) => {
|
|
|
1013
1048
|
* Validates multiple file paths at once
|
|
1014
1049
|
*/
|
|
1015
1050
|
app.post("/workspace/validate-paths", async (req, res) => {
|
|
1016
|
-
|
|
1051
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1052
|
+
if (!wsRoot) {
|
|
1017
1053
|
return res.status(400).json({ error: "workspace not set" });
|
|
1018
1054
|
}
|
|
1019
1055
|
|
|
@@ -1025,7 +1061,7 @@ app.post("/workspace/validate-paths", async (req, res) => {
|
|
|
1025
1061
|
try {
|
|
1026
1062
|
const results = await Promise.all(
|
|
1027
1063
|
pathList.map(async (relativePath) => {
|
|
1028
|
-
const fullPath = path.join(
|
|
1064
|
+
const fullPath = path.join(wsRoot, relativePath);
|
|
1029
1065
|
const fileExists = await exists(fullPath);
|
|
1030
1066
|
return { path: relativePath, exists: fileExists };
|
|
1031
1067
|
})
|
|
@@ -1056,7 +1092,8 @@ app.post("/workspace/validate-paths", async (req, res) => {
|
|
|
1056
1092
|
* FIXED: Handle both string arrays and object arrays from listRecursive
|
|
1057
1093
|
*/
|
|
1058
1094
|
app.post("/workspace/search-file", async (req, res) => {
|
|
1059
|
-
|
|
1095
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1096
|
+
if (!wsRoot) {
|
|
1060
1097
|
return res.status(400).json({ error: "workspace not set" });
|
|
1061
1098
|
}
|
|
1062
1099
|
|
|
@@ -1068,7 +1105,7 @@ app.post("/workspace/search-file", async (req, res) => {
|
|
|
1068
1105
|
try {
|
|
1069
1106
|
console.log(`🔍 [search-file] Searching for: ${fileName}`);
|
|
1070
1107
|
|
|
1071
|
-
const rawFiles = await listRecursive(
|
|
1108
|
+
const rawFiles = await listRecursive(wsRoot, {
|
|
1072
1109
|
maxDepth: 15,
|
|
1073
1110
|
includeHidden: false,
|
|
1074
1111
|
extensions: null
|
|
@@ -1132,7 +1169,8 @@ app.post("/workspace/search-file", async (req, res) => {
|
|
|
1132
1169
|
* FIXED: Handle both string arrays and object arrays from listRecursive
|
|
1133
1170
|
*/
|
|
1134
1171
|
app.post("/workspace/search-by-content", async (req, res) => {
|
|
1135
|
-
|
|
1172
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1173
|
+
if (!wsRoot) {
|
|
1136
1174
|
return res.status(400).json({ error: "workspace not set" });
|
|
1137
1175
|
}
|
|
1138
1176
|
|
|
@@ -1145,7 +1183,7 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1145
1183
|
try {
|
|
1146
1184
|
console.log(`🔍 [search-by-content] Searching for terms: ${terms.join(', ')}`);
|
|
1147
1185
|
|
|
1148
|
-
const rawFiles = await listRecursive(
|
|
1186
|
+
const rawFiles = await listRecursive(wsRoot, {
|
|
1149
1187
|
maxDepth: 15,
|
|
1150
1188
|
includeHidden: false
|
|
1151
1189
|
});
|
|
@@ -1171,7 +1209,7 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1171
1209
|
if (results.length >= maxResults * 2) break;
|
|
1172
1210
|
|
|
1173
1211
|
try {
|
|
1174
|
-
const fullPath = path.join(
|
|
1212
|
+
const fullPath = path.join(wsRoot, filePath);
|
|
1175
1213
|
const content = await readFile(fullPath, 'utf8');
|
|
1176
1214
|
const lines = content.split('\n');
|
|
1177
1215
|
|
|
@@ -1229,7 +1267,8 @@ app.post("/workspace/search-by-content", async (req, res) => {
|
|
|
1229
1267
|
* FIXED: Handle both string arrays and object arrays from listRecursive
|
|
1230
1268
|
*/
|
|
1231
1269
|
app.post("/workspace/find-field-definition", async (req, res) => {
|
|
1232
|
-
|
|
1270
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1271
|
+
if (!wsRoot) {
|
|
1233
1272
|
return res.status(400).json({ error: "workspace not set" });
|
|
1234
1273
|
}
|
|
1235
1274
|
|
|
@@ -1242,7 +1281,7 @@ app.post("/workspace/find-field-definition", async (req, res) => {
|
|
|
1242
1281
|
try {
|
|
1243
1282
|
console.log(`🔍 [find-field] Searching for field: ${fieldName}`);
|
|
1244
1283
|
|
|
1245
|
-
const rawFiles = await listRecursive(
|
|
1284
|
+
const rawFiles = await listRecursive(wsRoot, {
|
|
1246
1285
|
maxDepth: 15,
|
|
1247
1286
|
includeHidden: false
|
|
1248
1287
|
});
|
|
@@ -1266,7 +1305,7 @@ app.post("/workspace/find-field-definition", async (req, res) => {
|
|
|
1266
1305
|
|
|
1267
1306
|
for (const filePath of targetFiles) {
|
|
1268
1307
|
try {
|
|
1269
|
-
const fullPath = path.join(
|
|
1308
|
+
const fullPath = path.join(wsRoot, filePath);
|
|
1270
1309
|
const content = await readFile(fullPath, 'utf8');
|
|
1271
1310
|
const lines = content.split('\n');
|
|
1272
1311
|
|
|
@@ -1339,17 +1378,18 @@ app.post("/workspace/find-field-definition", async (req, res) => {
|
|
|
1339
1378
|
// ============================================
|
|
1340
1379
|
|
|
1341
1380
|
/** Detecta serviços no workspace */
|
|
1342
|
-
app.get("/workspace/services/detect", async (
|
|
1343
|
-
|
|
1381
|
+
app.get("/workspace/services/detect", async (req, res) => {
|
|
1382
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1383
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1344
1384
|
|
|
1345
1385
|
try {
|
|
1346
|
-
const scanner = new WorkspaceScanner(
|
|
1386
|
+
const scanner = new WorkspaceScanner(wsRoot);
|
|
1347
1387
|
const structure = await scanner.scan();
|
|
1348
1388
|
|
|
1349
1389
|
const languageDetector = new LanguageDetector(structure.files);
|
|
1350
1390
|
const languageInfo = languageDetector.detect();
|
|
1351
1391
|
|
|
1352
|
-
const fileReader = new FileReader(
|
|
1392
|
+
const fileReader = new FileReader(wsRoot);
|
|
1353
1393
|
const frameworkDetector = new FrameworkDetector(
|
|
1354
1394
|
languageInfo.primary,
|
|
1355
1395
|
structure.files,
|
|
@@ -1358,7 +1398,7 @@ app.get("/workspace/services/detect", async (_req, res) => {
|
|
|
1358
1398
|
const frameworkInfo = await frameworkDetector.detect();
|
|
1359
1399
|
|
|
1360
1400
|
const serviceDetector = new ServiceDetector(
|
|
1361
|
-
|
|
1401
|
+
wsRoot,
|
|
1362
1402
|
languageInfo,
|
|
1363
1403
|
frameworkInfo
|
|
1364
1404
|
);
|
|
@@ -1375,8 +1415,9 @@ app.get("/workspace/services/detect", async (_req, res) => {
|
|
|
1375
1415
|
});
|
|
1376
1416
|
|
|
1377
1417
|
/** Lista todos os serviços */
|
|
1378
|
-
app.get("/workspace/services", (
|
|
1379
|
-
|
|
1418
|
+
app.get("/workspace/services", (req, res) => {
|
|
1419
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1420
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1380
1421
|
|
|
1381
1422
|
// Atualizar status dos serviços com info do ProcessManager
|
|
1382
1423
|
const servicesWithStatus = DETECTED_SERVICES.map(service => {
|
|
@@ -1392,7 +1433,8 @@ app.get("/workspace/services", (_req, res) => {
|
|
|
1392
1433
|
|
|
1393
1434
|
/** Inicia um serviço */
|
|
1394
1435
|
app.post("/workspace/services/:serviceId/start", async (req, res) => {
|
|
1395
|
-
|
|
1436
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1437
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1396
1438
|
|
|
1397
1439
|
const { serviceId } = req.params;
|
|
1398
1440
|
const service = DETECTED_SERVICES.find(s => s.id === serviceId);
|
|
@@ -1418,7 +1460,8 @@ app.post("/workspace/services/:serviceId/start", async (req, res) => {
|
|
|
1418
1460
|
|
|
1419
1461
|
/** Para um serviço */
|
|
1420
1462
|
app.post("/workspace/services/:serviceId/stop", async (req, res) => {
|
|
1421
|
-
|
|
1463
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1464
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1422
1465
|
|
|
1423
1466
|
const { serviceId } = req.params;
|
|
1424
1467
|
|
|
@@ -1432,7 +1475,8 @@ app.post("/workspace/services/:serviceId/stop", async (req, res) => {
|
|
|
1432
1475
|
|
|
1433
1476
|
/** Retorna status de um serviço */
|
|
1434
1477
|
app.get("/workspace/services/:serviceId/status", (req, res) => {
|
|
1435
|
-
|
|
1478
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1479
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1436
1480
|
|
|
1437
1481
|
const { serviceId } = req.params;
|
|
1438
1482
|
const status = processManager.getStatus(serviceId);
|
|
@@ -1442,7 +1486,8 @@ app.get("/workspace/services/:serviceId/status", (req, res) => {
|
|
|
1442
1486
|
|
|
1443
1487
|
/** Retorna logs de um serviço */
|
|
1444
1488
|
app.get("/workspace/services/:serviceId/logs", (req, res) => {
|
|
1445
|
-
|
|
1489
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1490
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1446
1491
|
|
|
1447
1492
|
const { serviceId } = req.params;
|
|
1448
1493
|
const { limit = 100 } = req.query;
|
|
@@ -1457,7 +1502,8 @@ app.get("/workspace/services/:serviceId/logs", (req, res) => {
|
|
|
1457
1502
|
|
|
1458
1503
|
/** Streaming de logs em tempo real (SSE) */
|
|
1459
1504
|
app.get("/workspace/services/:serviceId/logs/stream", (req, res) => {
|
|
1460
|
-
|
|
1505
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1506
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1461
1507
|
|
|
1462
1508
|
const { serviceId } = req.params;
|
|
1463
1509
|
|
|
@@ -1492,18 +1538,20 @@ app.get("/workspace/services/:serviceId/logs/stream", (req, res) => {
|
|
|
1492
1538
|
// ============================================
|
|
1493
1539
|
|
|
1494
1540
|
app.get("/workspace/files", async (req, res) => {
|
|
1495
|
-
|
|
1541
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1542
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1496
1543
|
const { max = 5000 } = req.query;
|
|
1497
|
-
const tree = await listRecursive(
|
|
1498
|
-
res.json({ root:
|
|
1544
|
+
const tree = await listRecursive(wsRoot, { maxFiles: Number(max) });
|
|
1545
|
+
res.json({ root: wsRoot, count: tree.length, tree });
|
|
1499
1546
|
});
|
|
1500
1547
|
|
|
1501
1548
|
app.get("/workspace/file", async (req, res) => {
|
|
1502
|
-
|
|
1549
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1550
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1503
1551
|
const rel = req.query.path;
|
|
1504
1552
|
if (!rel) return res.status(400).json({ error: "path is required" });
|
|
1505
1553
|
try {
|
|
1506
|
-
const content = await readText(
|
|
1554
|
+
const content = await readText(wsRoot, rel);
|
|
1507
1555
|
res.json({ path: rel, content });
|
|
1508
1556
|
} catch (e) {
|
|
1509
1557
|
res.status(404).json({ error: "file not found", details: String(e) });
|
|
@@ -1511,11 +1559,12 @@ app.get("/workspace/file", async (req, res) => {
|
|
|
1511
1559
|
});
|
|
1512
1560
|
|
|
1513
1561
|
app.post("/workspace/write", async (req, res) => {
|
|
1514
|
-
|
|
1562
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1563
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1515
1564
|
const { path: rel, content } = req.body || {};
|
|
1516
1565
|
if (!rel) return res.status(400).json({ error: "path is required" });
|
|
1517
1566
|
try {
|
|
1518
|
-
await writeFile(path.join(
|
|
1567
|
+
await writeFile(path.join(wsRoot, rel), content ?? "", "utf8");
|
|
1519
1568
|
res.json({ ok: true, path: rel, bytes: Buffer.byteLength(content ?? "", "utf8") });
|
|
1520
1569
|
} catch (e) {
|
|
1521
1570
|
res.status(400).json({ error: "write failed", details: String(e) });
|
|
@@ -1526,13 +1575,14 @@ app.post("/workspace/write", async (req, res) => {
|
|
|
1526
1575
|
// ✅ CORRECTED: /workspace/patch endpoint
|
|
1527
1576
|
// ============================================
|
|
1528
1577
|
app.post("/workspace/patch", async (req, res) => {
|
|
1529
|
-
|
|
1578
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1579
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1530
1580
|
const { diff, incidentId } = req.body || {};
|
|
1531
1581
|
if (!diff) return res.status(400).json({ error: "diff is required" });
|
|
1532
1582
|
|
|
1533
1583
|
try {
|
|
1534
1584
|
console.log(`📝 Applying patch for incident: ${incidentId || 'unknown'}`);
|
|
1535
|
-
const out = await applyUnifiedDiff(
|
|
1585
|
+
const out = await applyUnifiedDiff(wsRoot, diff);
|
|
1536
1586
|
|
|
1537
1587
|
// ✅ CRITICAL FIX: Format response as expected by Gateway
|
|
1538
1588
|
const response = {
|
|
@@ -1565,18 +1615,20 @@ app.post("/workspace/patch", async (req, res) => {
|
|
|
1565
1615
|
}
|
|
1566
1616
|
});
|
|
1567
1617
|
|
|
1568
|
-
app.post("/workspace/test", async (
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
const
|
|
1572
|
-
|
|
1618
|
+
app.post("/workspace/test", async (req, res) => {
|
|
1619
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1620
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1621
|
+
const meta = await detectProject(wsRoot);
|
|
1622
|
+
const result = await compileAndTest({ language: meta.language, buildTool: meta.buildTool, cwd: wsRoot });
|
|
1623
|
+
res.json({ root: wsRoot, meta, result });
|
|
1573
1624
|
});
|
|
1574
1625
|
|
|
1575
1626
|
app.post("/workspace/run", async (req, res) => {
|
|
1576
|
-
|
|
1627
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1628
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1577
1629
|
const { cmd, args = [] } = req.body || {};
|
|
1578
1630
|
if (!cmd) return res.status(400).json({ error: "cmd is required" });
|
|
1579
|
-
const out = await run(cmd, args,
|
|
1631
|
+
const out = await run(cmd, args, wsRoot, 5 * 60 * 1000);
|
|
1580
1632
|
res.json(out);
|
|
1581
1633
|
});
|
|
1582
1634
|
|
|
@@ -1639,17 +1691,18 @@ app.get("/workspace/test-local/state", async (req, res) => {
|
|
|
1639
1691
|
* Compiles the project without starting server
|
|
1640
1692
|
*/
|
|
1641
1693
|
app.post("/workspace/test-local/compile", async (req, res) => {
|
|
1642
|
-
|
|
1694
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1695
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1643
1696
|
|
|
1644
1697
|
try {
|
|
1645
1698
|
console.log("🔨 [TEST-LOCAL] Starting compilation...");
|
|
1646
1699
|
TEST_LOCAL_STATE.status = "compiling";
|
|
1647
1700
|
|
|
1648
|
-
const meta = await detectProject(
|
|
1701
|
+
const meta = await detectProject(wsRoot);
|
|
1649
1702
|
const compileResult = await compileAndTest({
|
|
1650
1703
|
language: meta.language,
|
|
1651
1704
|
buildTool: meta.buildTool,
|
|
1652
|
-
cwd:
|
|
1705
|
+
cwd: wsRoot,
|
|
1653
1706
|
skipTests: true
|
|
1654
1707
|
});
|
|
1655
1708
|
|
|
@@ -1700,7 +1753,8 @@ app.post("/workspace/test-local/compile", async (req, res) => {
|
|
|
1700
1753
|
* 🧠 UPDATED: Now uses AI Vibe Coding Engine for auto-healing
|
|
1701
1754
|
*/
|
|
1702
1755
|
app.post("/workspace/test-local/start", async (req, res) => {
|
|
1703
|
-
|
|
1756
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1757
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1704
1758
|
|
|
1705
1759
|
const { port } = req.body || {};
|
|
1706
1760
|
|
|
@@ -1708,14 +1762,14 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1708
1762
|
console.log(`\n🚀 [TEST-LOCAL] Starting server (SIMPLE MODE)...`);
|
|
1709
1763
|
TEST_LOCAL_STATE.status = "starting";
|
|
1710
1764
|
|
|
1711
|
-
const meta = await detectProject(
|
|
1765
|
+
const meta = await detectProject(wsRoot);
|
|
1712
1766
|
|
|
1713
1767
|
// Detect port if not provided
|
|
1714
1768
|
let serverPort = port || 8080;
|
|
1715
1769
|
|
|
1716
1770
|
// Para Java, encontrar o JAR e correr directamente
|
|
1717
1771
|
if (meta.language === 'java') {
|
|
1718
|
-
const targetDir = path.join(
|
|
1772
|
+
const targetDir = path.join(wsRoot, 'target');
|
|
1719
1773
|
|
|
1720
1774
|
if (!fs.existsSync(targetDir)) {
|
|
1721
1775
|
throw new Error('target/ directory not found. Run mvn clean install first.');
|
|
@@ -1759,7 +1813,7 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1759
1813
|
const startConfig = {
|
|
1760
1814
|
command,
|
|
1761
1815
|
args,
|
|
1762
|
-
cwd:
|
|
1816
|
+
cwd: wsRoot,
|
|
1763
1817
|
port: serverPort,
|
|
1764
1818
|
env: cleanEnv
|
|
1765
1819
|
};
|
|
@@ -1802,7 +1856,7 @@ app.post("/workspace/test-local/start", async (req, res) => {
|
|
|
1802
1856
|
await processManager.start('test-local', {
|
|
1803
1857
|
command,
|
|
1804
1858
|
args,
|
|
1805
|
-
cwd:
|
|
1859
|
+
cwd: wsRoot,
|
|
1806
1860
|
port: serverPort,
|
|
1807
1861
|
env: { ...process.env, PORT: String(serverPort) }
|
|
1808
1862
|
});
|
|
@@ -1853,7 +1907,8 @@ app.post("/workspace/test-local/stop", async (req, res) => {
|
|
|
1853
1907
|
* Returns discovered endpoints
|
|
1854
1908
|
*/
|
|
1855
1909
|
app.get("/workspace/test-local/endpoints", async (req, res) => {
|
|
1856
|
-
|
|
1910
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
1911
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
1857
1912
|
|
|
1858
1913
|
try {
|
|
1859
1914
|
console.log("📋 [TEST-LOCAL] Getting endpoints...");
|
|
@@ -1868,13 +1923,13 @@ app.get("/workspace/test-local/endpoints", async (req, res) => {
|
|
|
1868
1923
|
}
|
|
1869
1924
|
|
|
1870
1925
|
// Otherwise discover them
|
|
1871
|
-
const scanner = new WorkspaceScanner(
|
|
1926
|
+
const scanner = new WorkspaceScanner(wsRoot);
|
|
1872
1927
|
const structure = await scanner.scan();
|
|
1873
1928
|
|
|
1874
|
-
const controllerAnalyzer = new ControllerAnalyzer(
|
|
1929
|
+
const controllerAnalyzer = new ControllerAnalyzer(wsRoot);
|
|
1875
1930
|
const apiDocs = await controllerAnalyzer.generateApiDocs(structure.files);
|
|
1876
1931
|
|
|
1877
|
-
const dtoAnalyzer = new DTOAnalyzer(
|
|
1932
|
+
const dtoAnalyzer = new DTOAnalyzer(wsRoot);
|
|
1878
1933
|
const payloadDocs = await dtoAnalyzer.generatePayloadDocs(structure.files, apiDocs.endpoints);
|
|
1879
1934
|
|
|
1880
1935
|
TEST_LOCAL_STATE.endpoints = payloadDocs.endpoints;
|
|
@@ -2027,16 +2082,17 @@ app.get("/workspace/test-local/logs", (req, res) => {
|
|
|
2027
2082
|
|
|
2028
2083
|
|
|
2029
2084
|
/** Discover controllers and endpoints */
|
|
2030
|
-
app.get("/workspace/controllers", async (
|
|
2031
|
-
|
|
2085
|
+
app.get("/workspace/controllers", async (req, res) => {
|
|
2086
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
2087
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
2032
2088
|
|
|
2033
2089
|
try {
|
|
2034
2090
|
console.log("🔍 [CONTROLLERS] Discovering controllers...");
|
|
2035
2091
|
|
|
2036
|
-
const scanner = new WorkspaceScanner(
|
|
2092
|
+
const scanner = new WorkspaceScanner(wsRoot);
|
|
2037
2093
|
const structure = await scanner.scan();
|
|
2038
2094
|
|
|
2039
|
-
const controllerAnalyzer = new ControllerAnalyzer(
|
|
2095
|
+
const controllerAnalyzer = new ControllerAnalyzer(wsRoot);
|
|
2040
2096
|
const apiDocs = await controllerAnalyzer.generateApiDocs(structure.files);
|
|
2041
2097
|
|
|
2042
2098
|
console.log(`✅ [CONTROLLERS] Found ${apiDocs.totalEndpoints} endpoints in ${apiDocs.totalControllers} controllers`);
|
|
@@ -2052,19 +2108,20 @@ app.get("/workspace/controllers", async (_req, res) => {
|
|
|
2052
2108
|
});
|
|
2053
2109
|
|
|
2054
2110
|
/** Analyze DTOs and payloads */
|
|
2055
|
-
app.get("/workspace/dtos", async (
|
|
2056
|
-
|
|
2111
|
+
app.get("/workspace/dtos", async (req, res) => {
|
|
2112
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
2113
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
2057
2114
|
|
|
2058
2115
|
try {
|
|
2059
2116
|
console.log("📦 [DTOS] Analyzing DTOs...");
|
|
2060
2117
|
|
|
2061
|
-
const scanner = new WorkspaceScanner(
|
|
2118
|
+
const scanner = new WorkspaceScanner(wsRoot);
|
|
2062
2119
|
const structure = await scanner.scan();
|
|
2063
2120
|
|
|
2064
|
-
const controllerAnalyzer = new ControllerAnalyzer(
|
|
2121
|
+
const controllerAnalyzer = new ControllerAnalyzer(wsRoot);
|
|
2065
2122
|
const apiDocs = await controllerAnalyzer.generateApiDocs(structure.files);
|
|
2066
2123
|
|
|
2067
|
-
const dtoAnalyzer = new DTOAnalyzer(
|
|
2124
|
+
const dtoAnalyzer = new DTOAnalyzer(wsRoot);
|
|
2068
2125
|
const payloadDocs = await dtoAnalyzer.generatePayloadDocs(structure.files, apiDocs.endpoints);
|
|
2069
2126
|
|
|
2070
2127
|
console.log(`✅ [DTOS] Found ${payloadDocs.totalDtos} DTOs`);
|
|
@@ -2080,13 +2137,14 @@ app.get("/workspace/dtos", async (_req, res) => {
|
|
|
2080
2137
|
});
|
|
2081
2138
|
|
|
2082
2139
|
/** Get server configuration */
|
|
2083
|
-
app.get("/workspace/config", async (
|
|
2084
|
-
|
|
2140
|
+
app.get("/workspace/config", async (req, res) => {
|
|
2141
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
2142
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
2085
2143
|
|
|
2086
2144
|
try {
|
|
2087
2145
|
console.log("⚙️ [CONFIG] Analyzing configuration...");
|
|
2088
2146
|
|
|
2089
|
-
const configAnalyzer = new ConfigAnalyzer(
|
|
2147
|
+
const configAnalyzer = new ConfigAnalyzer(wsRoot);
|
|
2090
2148
|
const config = await configAnalyzer.analyze();
|
|
2091
2149
|
|
|
2092
2150
|
console.log(`✅ [CONFIG] Server port: ${config.server.port}`);
|
|
@@ -2154,7 +2212,8 @@ function extractTargetFiles(diff) {
|
|
|
2154
2212
|
* Applies patch with automatic backup and rollback on failure
|
|
2155
2213
|
*/
|
|
2156
2214
|
app.post("/workspace/safe-patch", async (req, res) => {
|
|
2157
|
-
|
|
2215
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
2216
|
+
if (!wsRoot) {
|
|
2158
2217
|
return res.status(400).json({
|
|
2159
2218
|
error: "workspace not set",
|
|
2160
2219
|
hint: "call POST /workspace/open first"
|
|
@@ -2186,7 +2245,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2186
2245
|
const backupFiles = [];
|
|
2187
2246
|
|
|
2188
2247
|
for (const relPath of targetFiles) {
|
|
2189
|
-
const fullPath = path.join(
|
|
2248
|
+
const fullPath = path.join(wsRoot, relPath);
|
|
2190
2249
|
if (await exists(fullPath)) {
|
|
2191
2250
|
const content = await readFile(fullPath, 'utf8');
|
|
2192
2251
|
backupFiles.push({ path: relPath, content });
|
|
@@ -2225,7 +2284,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2225
2284
|
|
|
2226
2285
|
// 4. Apply patch
|
|
2227
2286
|
try {
|
|
2228
|
-
const result = await applyUnifiedDiff(
|
|
2287
|
+
const result = await applyUnifiedDiff(wsRoot, diff);
|
|
2229
2288
|
console.log(`✅ Patch applied successfully: ${result.target}`);
|
|
2230
2289
|
|
|
2231
2290
|
res.json({
|
|
@@ -2240,7 +2299,7 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2240
2299
|
console.error(`❌ Patch failed, rolling back: ${patchError.message}`);
|
|
2241
2300
|
|
|
2242
2301
|
for (const file of backupFiles) {
|
|
2243
|
-
const fullPath = path.join(
|
|
2302
|
+
const fullPath = path.join(wsRoot, file.path);
|
|
2244
2303
|
await writeFile(fullPath, file.content, 'utf8');
|
|
2245
2304
|
}
|
|
2246
2305
|
|
|
@@ -2264,7 +2323,8 @@ app.post("/workspace/safe-patch", async (req, res) => {
|
|
|
2264
2323
|
* Validates and simulates patch application WITHOUT modifying files
|
|
2265
2324
|
*/
|
|
2266
2325
|
app.post("/workspace/patch/dry-run", async (req, res) => {
|
|
2267
|
-
|
|
2326
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
2327
|
+
if (!wsRoot) {
|
|
2268
2328
|
return res.status(400).json({
|
|
2269
2329
|
error: "workspace not set",
|
|
2270
2330
|
hint: "call POST /workspace/open first"
|
|
@@ -2296,7 +2356,7 @@ app.post("/workspace/patch/dry-run", async (req, res) => {
|
|
|
2296
2356
|
// 3. Check if files exist
|
|
2297
2357
|
const fileChecks = await Promise.all(
|
|
2298
2358
|
targetFiles.map(async (relPath) => {
|
|
2299
|
-
const fullPath = path.join(
|
|
2359
|
+
const fullPath = path.join(wsRoot, relPath);
|
|
2300
2360
|
const fileExists = await exists(fullPath);
|
|
2301
2361
|
let currentContent = null;
|
|
2302
2362
|
let lineCount = 0;
|
|
@@ -2367,7 +2427,8 @@ app.post("/workspace/validate-diff", async (req, res) => {
|
|
|
2367
2427
|
* Creates manual backup of specific files
|
|
2368
2428
|
*/
|
|
2369
2429
|
app.post("/workspace/backup", async (req, res) => {
|
|
2370
|
-
|
|
2430
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
2431
|
+
if (!wsRoot) {
|
|
2371
2432
|
return res.status(400).json({
|
|
2372
2433
|
error: "workspace not set",
|
|
2373
2434
|
hint: "call POST /workspace/open first"
|
|
@@ -2384,7 +2445,7 @@ app.post("/workspace/backup", async (req, res) => {
|
|
|
2384
2445
|
const backupFiles = [];
|
|
2385
2446
|
|
|
2386
2447
|
for (const relPath of files) {
|
|
2387
|
-
const fullPath = path.join(
|
|
2448
|
+
const fullPath = path.join(wsRoot, relPath);
|
|
2388
2449
|
if (await exists(fullPath)) {
|
|
2389
2450
|
const content = await readFile(fullPath, 'utf8');
|
|
2390
2451
|
backupFiles.push({ path: relPath, content });
|
|
@@ -2424,7 +2485,8 @@ app.post("/workspace/backup", async (req, res) => {
|
|
|
2424
2485
|
* Restores files from a backup
|
|
2425
2486
|
*/
|
|
2426
2487
|
app.post("/workspace/rollback", async (req, res) => {
|
|
2427
|
-
|
|
2488
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
2489
|
+
if (!wsRoot) {
|
|
2428
2490
|
return res.status(400).json({
|
|
2429
2491
|
error: "workspace not set",
|
|
2430
2492
|
hint: "call POST /workspace/open first"
|
|
@@ -2448,7 +2510,7 @@ app.post("/workspace/rollback", async (req, res) => {
|
|
|
2448
2510
|
console.log(`♻️ Rolling back to backup: ${backupId}`);
|
|
2449
2511
|
|
|
2450
2512
|
for (const file of backup.files) {
|
|
2451
|
-
const fullPath = path.join(
|
|
2513
|
+
const fullPath = path.join(wsRoot, file.path);
|
|
2452
2514
|
await writeFile(fullPath, file.content, 'utf8');
|
|
2453
2515
|
}
|
|
2454
2516
|
|
|
@@ -2470,7 +2532,7 @@ app.post("/workspace/rollback", async (req, res) => {
|
|
|
2470
2532
|
* GET /workspace/backups
|
|
2471
2533
|
* Lists all available backups
|
|
2472
2534
|
*/
|
|
2473
|
-
app.get("/workspace/backups", (
|
|
2535
|
+
app.get("/workspace/backups", (req, res) => {
|
|
2474
2536
|
const backupList = Array.from(BACKUPS.entries()).map(([id, data]) => ({
|
|
2475
2537
|
backupId: id,
|
|
2476
2538
|
timestamp: data.timestamp,
|
|
@@ -2524,7 +2586,8 @@ app.delete("/workspace/backups/:backupId", (req, res) => {
|
|
|
2524
2586
|
app.get("/workspace/diff/by-incident/:incidentId", async (req, res) => {
|
|
2525
2587
|
const { incidentId } = req.params;
|
|
2526
2588
|
|
|
2527
|
-
|
|
2589
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
2590
|
+
if (!wsRoot) {
|
|
2528
2591
|
return res.status(400).json({ error: "workspace not set" });
|
|
2529
2592
|
}
|
|
2530
2593
|
|
|
@@ -2574,7 +2637,7 @@ app.get("/workspace/diff/by-incident/:incidentId", async (req, res) => {
|
|
|
2574
2637
|
try {
|
|
2575
2638
|
const diffs = [];
|
|
2576
2639
|
for (const file of matchedBackup.files) {
|
|
2577
|
-
const fullPath = path.join(
|
|
2640
|
+
const fullPath = path.join(wsRoot, file.path);
|
|
2578
2641
|
let currentContent = '';
|
|
2579
2642
|
try {
|
|
2580
2643
|
currentContent = await readFile(fullPath, 'utf8');
|
|
@@ -2635,7 +2698,8 @@ app.get("/workspace/diff/by-incident/:incidentId", async (req, res) => {
|
|
|
2635
2698
|
app.get("/workspace/diff/:backupId", async (req, res) => {
|
|
2636
2699
|
const { backupId } = req.params;
|
|
2637
2700
|
|
|
2638
|
-
|
|
2701
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
2702
|
+
if (!wsRoot) {
|
|
2639
2703
|
return res.status(400).json({ error: "workspace not set" });
|
|
2640
2704
|
}
|
|
2641
2705
|
|
|
@@ -2652,7 +2716,7 @@ app.get("/workspace/diff/:backupId", async (req, res) => {
|
|
|
2652
2716
|
const diffs = [];
|
|
2653
2717
|
|
|
2654
2718
|
for (const file of backup.files) {
|
|
2655
|
-
const fullPath = path.join(
|
|
2719
|
+
const fullPath = path.join(wsRoot, file.path);
|
|
2656
2720
|
let currentContent = '';
|
|
2657
2721
|
|
|
2658
2722
|
try {
|
|
@@ -2709,7 +2773,8 @@ app.get("/workspace/diff/:backupId", async (req, res) => {
|
|
|
2709
2773
|
* - framework: spring-boot, express, flask, etc (optional)
|
|
2710
2774
|
*/
|
|
2711
2775
|
app.get("/workspace/detect-port", async (req, res) => {
|
|
2712
|
-
|
|
2776
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
2777
|
+
if (!wsRoot) {
|
|
2713
2778
|
return res.status(400).json({
|
|
2714
2779
|
error: "workspace not set",
|
|
2715
2780
|
hint: "call POST /workspace/open first"
|
|
@@ -2719,7 +2784,7 @@ app.get("/workspace/detect-port", async (req, res) => {
|
|
|
2719
2784
|
const { servicePath = '', language, framework } = req.query;
|
|
2720
2785
|
|
|
2721
2786
|
try {
|
|
2722
|
-
const fullPath = path.join(
|
|
2787
|
+
const fullPath = path.join(wsRoot, servicePath);
|
|
2723
2788
|
console.log(`🔍 Detecting port for service at: ${fullPath}`);
|
|
2724
2789
|
|
|
2725
2790
|
let port = null;
|
|
@@ -2973,18 +3038,19 @@ async function detectDotNetPort(servicePath) {
|
|
|
2973
3038
|
|
|
2974
3039
|
/** Prepare for testing: compile + discover endpoints */
|
|
2975
3040
|
app.post("/workspace/test-local/prepare", async (req, res) => {
|
|
2976
|
-
|
|
3041
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
3042
|
+
if (!wsRoot) return res.status(400).json({ error: "workspace not set" });
|
|
2977
3043
|
|
|
2978
3044
|
try {
|
|
2979
3045
|
console.log("🔧 [TEST-LOCAL] Preparing test environment...");
|
|
2980
3046
|
TEST_LOCAL_STATE.status = "compiling";
|
|
2981
3047
|
|
|
2982
3048
|
// Step 1: Compile
|
|
2983
|
-
const meta = await detectProject(
|
|
3049
|
+
const meta = await detectProject(wsRoot);
|
|
2984
3050
|
const compileResult = await compileAndTest({
|
|
2985
3051
|
language: meta.language,
|
|
2986
3052
|
buildTool: meta.buildTool,
|
|
2987
|
-
cwd:
|
|
3053
|
+
cwd: wsRoot,
|
|
2988
3054
|
skipTests: true
|
|
2989
3055
|
});
|
|
2990
3056
|
|
|
@@ -3000,19 +3066,19 @@ app.post("/workspace/test-local/prepare", async (req, res) => {
|
|
|
3000
3066
|
TEST_LOCAL_STATE.status = "compiled";
|
|
3001
3067
|
|
|
3002
3068
|
// Step 2: Discover endpoints
|
|
3003
|
-
const scanner = new WorkspaceScanner(
|
|
3069
|
+
const scanner = new WorkspaceScanner(wsRoot);
|
|
3004
3070
|
const structure = await scanner.scan();
|
|
3005
3071
|
|
|
3006
|
-
const controllerAnalyzer = new ControllerAnalyzer(
|
|
3072
|
+
const controllerAnalyzer = new ControllerAnalyzer(wsRoot);
|
|
3007
3073
|
const apiDocs = await controllerAnalyzer.generateApiDocs(structure.files);
|
|
3008
3074
|
|
|
3009
|
-
const dtoAnalyzer = new DTOAnalyzer(
|
|
3075
|
+
const dtoAnalyzer = new DTOAnalyzer(wsRoot);
|
|
3010
3076
|
const payloadDocs = await dtoAnalyzer.generatePayloadDocs(structure.files, apiDocs.endpoints);
|
|
3011
3077
|
|
|
3012
3078
|
TEST_LOCAL_STATE.endpoints = payloadDocs.endpoints;
|
|
3013
3079
|
|
|
3014
3080
|
// Step 3: Get config
|
|
3015
|
-
const configAnalyzer = new ConfigAnalyzer(
|
|
3081
|
+
const configAnalyzer = new ConfigAnalyzer(wsRoot);
|
|
3016
3082
|
const config = await configAnalyzer.analyze();
|
|
3017
3083
|
TEST_LOCAL_STATE.config = config;
|
|
3018
3084
|
|
|
@@ -3050,7 +3116,8 @@ app.post("/workspace/test-local/prepare", async (req, res) => {
|
|
|
3050
3116
|
* SSE endpoint for real-time compilation logs
|
|
3051
3117
|
*/
|
|
3052
3118
|
app.get("/workspace/test-local/compile/stream", async (req, res) => {
|
|
3053
|
-
|
|
3119
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
3120
|
+
if (!wsRoot) {
|
|
3054
3121
|
res.status(400).json({ error: "workspace not set" });
|
|
3055
3122
|
return;
|
|
3056
3123
|
}
|
|
@@ -3064,7 +3131,7 @@ app.get("/workspace/test-local/compile/stream", async (req, res) => {
|
|
|
3064
3131
|
res.flushHeaders();
|
|
3065
3132
|
|
|
3066
3133
|
try {
|
|
3067
|
-
const meta = await detectProject(
|
|
3134
|
+
const meta = await detectProject(wsRoot);
|
|
3068
3135
|
|
|
3069
3136
|
// Send initial event
|
|
3070
3137
|
res.write(`event: log\n`);
|
|
@@ -3106,7 +3173,7 @@ app.get("/workspace/test-local/compile/stream", async (req, res) => {
|
|
|
3106
3173
|
|
|
3107
3174
|
const startTime = Date.now();
|
|
3108
3175
|
const proc = spawn(command, args, {
|
|
3109
|
-
cwd:
|
|
3176
|
+
cwd: wsRoot,
|
|
3110
3177
|
shell: true,
|
|
3111
3178
|
env: { ...process.env, MAVEN_OPTS: "-Dorg.slf4j.simpleLogger.defaultLogLevel=info" }
|
|
3112
3179
|
});
|
|
@@ -3578,14 +3645,15 @@ app.get("/system/info", (req, res) => {
|
|
|
3578
3645
|
* Analisa controllers e retorna todos os endpoints da API
|
|
3579
3646
|
*/
|
|
3580
3647
|
app.get("/workspace/api-docs", async (req, res) => {
|
|
3581
|
-
|
|
3648
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
3649
|
+
if (!wsRoot) {
|
|
3582
3650
|
return res.status(400).json({
|
|
3583
3651
|
error: "workspace not set",
|
|
3584
3652
|
hint: "call POST /workspace/open first"
|
|
3585
3653
|
});
|
|
3586
3654
|
}
|
|
3587
3655
|
|
|
3588
|
-
console.log(`📚 [API-DOCS] Analyzing controllers in ${
|
|
3656
|
+
console.log(`📚 [API-DOCS] Analyzing controllers in ${wsRoot}`);
|
|
3589
3657
|
|
|
3590
3658
|
try {
|
|
3591
3659
|
// Primeiro, escanear arquivos do workspace
|
|
@@ -3602,7 +3670,7 @@ app.get("/workspace/api-docs", async (req, res) => {
|
|
|
3602
3670
|
|
|
3603
3671
|
for (const entry of entries) {
|
|
3604
3672
|
const fullPath = pathModule.join(dir, entry.name);
|
|
3605
|
-
const relativePath = pathModule.relative(
|
|
3673
|
+
const relativePath = pathModule.relative(wsRoot, fullPath);
|
|
3606
3674
|
|
|
3607
3675
|
// Skip common ignored directories
|
|
3608
3676
|
if (entry.name === 'node_modules' || entry.name === 'target' ||
|
|
@@ -3622,12 +3690,12 @@ app.get("/workspace/api-docs", async (req, res) => {
|
|
|
3622
3690
|
}
|
|
3623
3691
|
}
|
|
3624
3692
|
|
|
3625
|
-
await scanDir(
|
|
3693
|
+
await scanDir(wsRoot);
|
|
3626
3694
|
|
|
3627
3695
|
console.log(`📁 [API-DOCS] Found ${files.length} Java files`);
|
|
3628
3696
|
|
|
3629
3697
|
// Agora analisar os controllers
|
|
3630
|
-
const controllerAnalyzer = new ControllerAnalyzer(
|
|
3698
|
+
const controllerAnalyzer = new ControllerAnalyzer(wsRoot);
|
|
3631
3699
|
const apiDocs = await controllerAnalyzer.generateApiDocs(files);
|
|
3632
3700
|
|
|
3633
3701
|
console.log(`✅ [API-DOCS] Found ${apiDocs.totalEndpoints} endpoints in ${apiDocs.totalControllers} controllers`);
|
|
@@ -3638,7 +3706,7 @@ app.get("/workspace/api-docs", async (req, res) => {
|
|
|
3638
3706
|
controllers: apiDocs.controllers.map(c => c.className),
|
|
3639
3707
|
totalEndpoints: apiDocs.totalEndpoints,
|
|
3640
3708
|
totalControllers: apiDocs.totalControllers,
|
|
3641
|
-
workspace:
|
|
3709
|
+
workspace: wsRoot
|
|
3642
3710
|
});
|
|
3643
3711
|
|
|
3644
3712
|
} catch (error) {
|
|
@@ -3660,7 +3728,8 @@ app.get("/workspace/api-docs", async (req, res) => {
|
|
|
3660
3728
|
* Retorna endpoints de uma controller específica
|
|
3661
3729
|
*/
|
|
3662
3730
|
app.get("/workspace/api-docs/:controller", async (req, res) => {
|
|
3663
|
-
|
|
3731
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
3732
|
+
if (!wsRoot) {
|
|
3664
3733
|
return res.status(400).json({
|
|
3665
3734
|
error: "workspace not set",
|
|
3666
3735
|
hint: "call POST /workspace/open first"
|
|
@@ -3685,7 +3754,7 @@ app.get("/workspace/api-docs/:controller", async (req, res) => {
|
|
|
3685
3754
|
|
|
3686
3755
|
for (const entry of entries) {
|
|
3687
3756
|
const fullPath = pathModule.join(dir, entry.name);
|
|
3688
|
-
const relativePath = pathModule.relative(
|
|
3757
|
+
const relativePath = pathModule.relative(wsRoot, fullPath);
|
|
3689
3758
|
|
|
3690
3759
|
if (entry.name === 'node_modules' || entry.name === 'target' ||
|
|
3691
3760
|
entry.name === 'build' || entry.name === '.git' ||
|
|
@@ -3704,10 +3773,10 @@ app.get("/workspace/api-docs/:controller", async (req, res) => {
|
|
|
3704
3773
|
}
|
|
3705
3774
|
}
|
|
3706
3775
|
|
|
3707
|
-
await scanDir(
|
|
3776
|
+
await scanDir(wsRoot);
|
|
3708
3777
|
|
|
3709
3778
|
// Analisar os controllers
|
|
3710
|
-
const controllerAnalyzer = new ControllerAnalyzer(
|
|
3779
|
+
const controllerAnalyzer = new ControllerAnalyzer(wsRoot);
|
|
3711
3780
|
const apiDocs = await controllerAnalyzer.generateApiDocs(files);
|
|
3712
3781
|
|
|
3713
3782
|
const found = apiDocs.controllers?.find(c =>
|
|
@@ -3828,7 +3897,8 @@ app.get("/ai-engine/fixes", (req, res) => {
|
|
|
3828
3897
|
* Recolhe ficheiros de configuração para análise AI
|
|
3829
3898
|
*/
|
|
3830
3899
|
app.get("/workspace/smart-config", async (req, res) => {
|
|
3831
|
-
|
|
3900
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
3901
|
+
if (!wsRoot) {
|
|
3832
3902
|
return res.status(400).json({ error: "workspace not set" });
|
|
3833
3903
|
}
|
|
3834
3904
|
|
|
@@ -3851,10 +3921,10 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3851
3921
|
];
|
|
3852
3922
|
|
|
3853
3923
|
const collectedFiles = [];
|
|
3854
|
-
const meta = await detectProject(
|
|
3924
|
+
const meta = await detectProject(wsRoot);
|
|
3855
3925
|
|
|
3856
3926
|
for (const pattern of configPatterns) {
|
|
3857
|
-
const filePath = path.join(
|
|
3927
|
+
const filePath = path.join(wsRoot, pattern);
|
|
3858
3928
|
if (fs.existsSync(filePath)) {
|
|
3859
3929
|
try {
|
|
3860
3930
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
@@ -3875,7 +3945,7 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3875
3945
|
|
|
3876
3946
|
// Detectar profiles disponíveis
|
|
3877
3947
|
const availableProfiles = [];
|
|
3878
|
-
const resourcesDir = path.join(
|
|
3948
|
+
const resourcesDir = path.join(wsRoot, 'src/main/resources');
|
|
3879
3949
|
if (fs.existsSync(resourcesDir)) {
|
|
3880
3950
|
const files = fs.readdirSync(resourcesDir);
|
|
3881
3951
|
files.forEach(file => {
|
|
@@ -3888,7 +3958,7 @@ app.get("/workspace/smart-config", async (req, res) => {
|
|
|
3888
3958
|
|
|
3889
3959
|
res.json({
|
|
3890
3960
|
ok: true,
|
|
3891
|
-
workspace:
|
|
3961
|
+
workspace: wsRoot,
|
|
3892
3962
|
language: meta.language,
|
|
3893
3963
|
buildTool: meta.buildTool,
|
|
3894
3964
|
framework: meta.framework || 'unknown',
|
|
@@ -3974,7 +4044,8 @@ app.post("/workspace/test-local/restart", async (req, res) => {
|
|
|
3974
4044
|
* Response: { "ok": true, "results": "file:line: matching text\n...", "count": 15 }
|
|
3975
4045
|
*/
|
|
3976
4046
|
app.post("/workspace/search", async (req, res) => {
|
|
3977
|
-
|
|
4047
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
4048
|
+
if (!wsRoot) {
|
|
3978
4049
|
return res.status(400).json({
|
|
3979
4050
|
ok: false,
|
|
3980
4051
|
error: "workspace not set",
|
|
@@ -4009,7 +4080,7 @@ app.post("/workspace/search", async (req, res) => {
|
|
|
4009
4080
|
|
|
4010
4081
|
// Escape pattern for shell safety and limit results
|
|
4011
4082
|
const safePattern = pattern.replace(/"/g, '\\"').replace(/`/g, '\\`').replace(/\$/g, '\\$');
|
|
4012
|
-
cmd += `"${safePattern}" "${
|
|
4083
|
+
cmd += `"${safePattern}" "${wsRoot}" | head -100`;
|
|
4013
4084
|
|
|
4014
4085
|
let stdout = '';
|
|
4015
4086
|
try {
|
|
@@ -4041,7 +4112,7 @@ app.post("/workspace/search", async (req, res) => {
|
|
|
4041
4112
|
const results = stdout
|
|
4042
4113
|
.split('\n')
|
|
4043
4114
|
.filter(line => line.trim())
|
|
4044
|
-
.map(line => line.replace(
|
|
4115
|
+
.map(line => line.replace(wsRoot + '/', ''))
|
|
4045
4116
|
.join('\n');
|
|
4046
4117
|
|
|
4047
4118
|
const count = results.split('\n').filter(l => l.trim()).length;
|
|
@@ -4071,7 +4142,8 @@ app.post("/workspace/search", async (req, res) => {
|
|
|
4071
4142
|
* Dangerous commands are blocked.
|
|
4072
4143
|
*/
|
|
4073
4144
|
app.post("/workspace/exec", async (req, res) => {
|
|
4074
|
-
|
|
4145
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
4146
|
+
if (!wsRoot) {
|
|
4075
4147
|
return res.status(400).json({
|
|
4076
4148
|
ok: false,
|
|
4077
4149
|
error: "workspace not set",
|
|
@@ -4107,7 +4179,7 @@ app.post("/workspace/exec", async (req, res) => {
|
|
|
4107
4179
|
const execAsync = promisify(execCb);
|
|
4108
4180
|
|
|
4109
4181
|
const result = await execAsync(command, {
|
|
4110
|
-
cwd:
|
|
4182
|
+
cwd: wsRoot,
|
|
4111
4183
|
timeout: 60000,
|
|
4112
4184
|
maxBuffer: 5 * 1024 * 1024,
|
|
4113
4185
|
env: { ...process.env, FORCE_COLOR: '0' }
|
|
@@ -4238,7 +4310,8 @@ app.post("/workspace/test-endpoint", async (req, res) => {
|
|
|
4238
4310
|
* Body: { branchName, commitMessage, files? }
|
|
4239
4311
|
*/
|
|
4240
4312
|
app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
4241
|
-
|
|
4313
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
4314
|
+
if (!wsRoot) {
|
|
4242
4315
|
return res.status(400).json({ error: "workspace not set" });
|
|
4243
4316
|
}
|
|
4244
4317
|
|
|
@@ -4250,7 +4323,7 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4250
4323
|
try {
|
|
4251
4324
|
const { execSync } = await import('child_process');
|
|
4252
4325
|
const opts = {
|
|
4253
|
-
cwd:
|
|
4326
|
+
cwd: wsRoot,
|
|
4254
4327
|
encoding: 'utf8',
|
|
4255
4328
|
timeout: 30000,
|
|
4256
4329
|
env: {
|
|
@@ -4612,14 +4685,32 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4612
4685
|
.replace(/\.git$/, '');
|
|
4613
4686
|
const [workspace, repoSlug] = repoPath.split('/');
|
|
4614
4687
|
|
|
4615
|
-
// gitToken
|
|
4616
|
-
|
|
4688
|
+
// gitToken may be:
|
|
4689
|
+
// - "x-token-auth:TOKEN" (Repository Access Token from Gateway)
|
|
4690
|
+
// - "username:appPassword" (App Password)
|
|
4691
|
+
// - plain TOKEN (Repository Access Token)
|
|
4692
|
+
let authHeader;
|
|
4693
|
+
if (gitToken.includes(':')) {
|
|
4694
|
+
// Contains colon — could be user:pass (App Password) or x-token-auth:token
|
|
4695
|
+
const [user, pass] = gitToken.split(':', 2);
|
|
4696
|
+
if (user === 'x-token-auth') {
|
|
4697
|
+
// Repository Access Token — use Bearer with the token part
|
|
4698
|
+
authHeader = `Bearer ${pass}`;
|
|
4699
|
+
} else {
|
|
4700
|
+
// App Password — use Basic auth
|
|
4701
|
+
const credentials = Buffer.from(gitToken).toString('base64');
|
|
4702
|
+
authHeader = `Basic ${credentials}`;
|
|
4703
|
+
}
|
|
4704
|
+
} else {
|
|
4705
|
+
// Plain token — use Bearer (Repository Access Token)
|
|
4706
|
+
authHeader = `Bearer ${gitToken}`;
|
|
4707
|
+
}
|
|
4617
4708
|
|
|
4618
4709
|
const response = await fetch(
|
|
4619
4710
|
`https://api.bitbucket.org/2.0/repositories/${workspace}/${repoSlug}/pullrequests`, {
|
|
4620
4711
|
method: 'POST',
|
|
4621
4712
|
headers: {
|
|
4622
|
-
'Authorization':
|
|
4713
|
+
'Authorization': authHeader,
|
|
4623
4714
|
'Content-Type': 'application/json'
|
|
4624
4715
|
},
|
|
4625
4716
|
body: JSON.stringify({
|
|
@@ -4674,13 +4765,14 @@ app.post("/workspace/git/create-fix-branch", async (req, res) => {
|
|
|
4674
4765
|
* Returns current git status (branch, changes, remote)
|
|
4675
4766
|
*/
|
|
4676
4767
|
app.get("/workspace/git/status", async (req, res) => {
|
|
4677
|
-
|
|
4768
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
4769
|
+
if (!wsRoot) {
|
|
4678
4770
|
return res.status(400).json({ error: "workspace not set" });
|
|
4679
4771
|
}
|
|
4680
4772
|
|
|
4681
4773
|
try {
|
|
4682
4774
|
const { execSync } = await import('child_process');
|
|
4683
|
-
const opts = { cwd:
|
|
4775
|
+
const opts = { cwd: wsRoot, encoding: 'utf8', timeout: 10000 };
|
|
4684
4776
|
|
|
4685
4777
|
let branch = '', remoteUrl = '', status = '';
|
|
4686
4778
|
let isGitRepo = false;
|
|
@@ -4723,7 +4815,8 @@ app.get("/workspace/git/status", async (req, res) => {
|
|
|
4723
4815
|
* - filter: 'all' | 'unresolved' | 'review' (default: 'all')
|
|
4724
4816
|
*/
|
|
4725
4817
|
app.get("/workspace/git/pr/comments", async (req, res) => {
|
|
4726
|
-
|
|
4818
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
4819
|
+
if (!wsRoot) {
|
|
4727
4820
|
return res.status(400).json({ error: "workspace not set" });
|
|
4728
4821
|
}
|
|
4729
4822
|
|
|
@@ -4788,7 +4881,8 @@ app.get("/workspace/git/pr/comments", async (req, res) => {
|
|
|
4788
4881
|
* - prNumber (required): PR/MR number
|
|
4789
4882
|
*/
|
|
4790
4883
|
app.get("/workspace/git/pr/comments/:commentId", async (req, res) => {
|
|
4791
|
-
|
|
4884
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
4885
|
+
if (!wsRoot) {
|
|
4792
4886
|
return res.status(400).json({ error: "workspace not set" });
|
|
4793
4887
|
}
|
|
4794
4888
|
|
|
@@ -4831,7 +4925,8 @@ app.get("/workspace/git/pr/comments/:commentId", async (req, res) => {
|
|
|
4831
4925
|
* - inReplyToId: Parent comment ID (for thread replies)
|
|
4832
4926
|
*/
|
|
4833
4927
|
app.post("/workspace/git/pr/comments", async (req, res) => {
|
|
4834
|
-
|
|
4928
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
4929
|
+
if (!wsRoot) {
|
|
4835
4930
|
return res.status(400).json({ error: "workspace not set" });
|
|
4836
4931
|
}
|
|
4837
4932
|
|
|
@@ -4888,7 +4983,8 @@ app.post("/workspace/git/pr/comments", async (req, res) => {
|
|
|
4888
4983
|
* - prNumber (required): PR/MR number
|
|
4889
4984
|
*/
|
|
4890
4985
|
app.post("/workspace/git/pr/comments/:commentId/resolve", async (req, res) => {
|
|
4891
|
-
|
|
4986
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
4987
|
+
if (!wsRoot) {
|
|
4892
4988
|
return res.status(400).json({ error: "workspace not set" });
|
|
4893
4989
|
}
|
|
4894
4990
|
|
|
@@ -4925,7 +5021,8 @@ app.post("/workspace/git/pr/comments/:commentId/resolve", async (req, res) => {
|
|
|
4925
5021
|
* - prNumber (required): PR/MR number
|
|
4926
5022
|
*/
|
|
4927
5023
|
app.post("/workspace/git/pr/comments/:commentId/unresolve", async (req, res) => {
|
|
4928
|
-
|
|
5024
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
5025
|
+
if (!wsRoot) {
|
|
4929
5026
|
return res.status(400).json({ error: "workspace not set" });
|
|
4930
5027
|
}
|
|
4931
5028
|
|
|
@@ -4966,7 +5063,8 @@ app.post("/workspace/git/pr/comments/:commentId/unresolve", async (req, res) =>
|
|
|
4966
5063
|
* - branch: Branch to apply fix on (defaults to current branch)
|
|
4967
5064
|
*/
|
|
4968
5065
|
app.post("/workspace/git/pr/comments/:commentId/fix-and-resolve", async (req, res) => {
|
|
4969
|
-
|
|
5066
|
+
const wsRoot = resolveWorkspaceRoot(req);
|
|
5067
|
+
if (!wsRoot) {
|
|
4970
5068
|
return res.status(400).json({ error: "workspace not set" });
|
|
4971
5069
|
}
|
|
4972
5070
|
|
|
@@ -5101,7 +5199,7 @@ async function _getGitProvider() {
|
|
|
5101
5199
|
// ============================================
|
|
5102
5200
|
|
|
5103
5201
|
/** Lista todos os workspaces abertos */
|
|
5104
|
-
app.get("/workspaces", (
|
|
5202
|
+
app.get("/workspaces", (req, res) => {
|
|
5105
5203
|
res.json({
|
|
5106
5204
|
workspaces: wsManager.list(),
|
|
5107
5205
|
total: wsManager.count,
|