@axhub/genie 0.2.2 → 0.2.4

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.
Files changed (72) hide show
  1. package/dist/assets/App-BxazfNJn.js +484 -0
  2. package/dist/assets/{ReviewApp-Bp_y3xff.js → ReviewApp-CsqTAlGU.js} +1 -1
  3. package/dist/assets/{_basePickBy-C5f221Kr.js → _basePickBy-CFRQvihx.js} +1 -1
  4. package/dist/assets/{_baseUniq-CeEXFlBh.js → _baseUniq-Dhh8nCvs.js} +1 -1
  5. package/dist/assets/{arc-CZVQXROF.js → arc-DQ0v3dU4.js} +1 -1
  6. package/dist/assets/{architectureDiagram-2XIMDMQ5-D91MBXeh.js → architectureDiagram-2XIMDMQ5-DmUHdvQH.js} +1 -1
  7. package/dist/assets/{blockDiagram-WCTKOSBZ-CsHP3zT2.js → blockDiagram-WCTKOSBZ-Bbxhj5KC.js} +1 -1
  8. package/dist/assets/{c4Diagram-IC4MRINW-DakKlk21.js → c4Diagram-IC4MRINW-BOivDlQU.js} +1 -1
  9. package/dist/assets/channel-Cj8xVD0X.js +1 -0
  10. package/dist/assets/{chunk-4BX2VUAB-BXZoxrtv.js → chunk-4BX2VUAB-DlvtrM0q.js} +1 -1
  11. package/dist/assets/{chunk-55IACEB6-V9_WXk3w.js → chunk-55IACEB6-DJUSHyTa.js} +1 -1
  12. package/dist/assets/{chunk-FMBD7UC4-IgdHo6Dd.js → chunk-FMBD7UC4-C6Ch-htf.js} +1 -1
  13. package/dist/assets/{chunk-JSJVCQXG-CnaAsDTd.js → chunk-JSJVCQXG-DzQIht58.js} +1 -1
  14. package/dist/assets/{chunk-KX2RTZJC-D0qksU2H.js → chunk-KX2RTZJC-C05jARMH.js} +1 -1
  15. package/dist/assets/{chunk-NQ4KR5QH-rd6KG4-c.js → chunk-NQ4KR5QH-Ci-n7jfu.js} +1 -1
  16. package/dist/assets/{chunk-QZHKN3VN-Cyltgv4l.js → chunk-QZHKN3VN-jxti9HTX.js} +1 -1
  17. package/dist/assets/{chunk-WL4C6EOR-DkNtSo86.js → chunk-WL4C6EOR-C559Mk71.js} +1 -1
  18. package/dist/assets/classDiagram-VBA2DB6C-CI2zklxw.js +1 -0
  19. package/dist/assets/classDiagram-v2-RAHNMMFH-CI2zklxw.js +1 -0
  20. package/dist/assets/clone-BEVqubrI.js +1 -0
  21. package/dist/assets/{cose-bilkent-S5V4N54A-D_Wsd3iv.js → cose-bilkent-S5V4N54A-DNO9ncXL.js} +1 -1
  22. package/dist/assets/{dagre-KLK3FWXG-CrrmZeGu.js → dagre-KLK3FWXG-DJ3dNSYk.js} +1 -1
  23. package/dist/assets/{diagram-E7M64L7V-WzO26pGn.js → diagram-E7M64L7V-Ba_LGLun.js} +1 -1
  24. package/dist/assets/{diagram-IFDJBPK2-COynsdO3.js → diagram-IFDJBPK2-Da6K4aP-.js} +1 -1
  25. package/dist/assets/{diagram-P4PSJMXO-CSqD5HJx.js → diagram-P4PSJMXO-vZZKB92A.js} +1 -1
  26. package/dist/assets/{erDiagram-INFDFZHY-BiFhS6xi.js → erDiagram-INFDFZHY-Csb8dFdP.js} +1 -1
  27. package/dist/assets/{flowDiagram-PKNHOUZH-9jdAJSs0.js → flowDiagram-PKNHOUZH-DUV13pHi.js} +1 -1
  28. package/dist/assets/{ganttDiagram-A5KZAMGK-pGyMKWCo.js → ganttDiagram-A5KZAMGK-B5Kv9Wfz.js} +1 -1
  29. package/dist/assets/{gitGraphDiagram-K3NZZRJ6-D4jvwoW1.js → gitGraphDiagram-K3NZZRJ6-BZ5gW69I.js} +1 -1
  30. package/dist/assets/{graph-DMknFkkX.js → graph-BbvHswRd.js} +1 -1
  31. package/dist/assets/{highlighted-body-TPN3WLV5-DhMGh1O7.js → highlighted-body-TPN3WLV5-DZJajMGm.js} +1 -1
  32. package/dist/assets/index-BFX9lxRB.css +1 -0
  33. package/dist/assets/index-BiErUGrv.js +2 -0
  34. package/dist/assets/{infoDiagram-LFFYTUFH-CP5zYiVP.js → infoDiagram-LFFYTUFH-8auUIPKW.js} +1 -1
  35. package/dist/assets/{ishikawaDiagram-PHBUUO56-BplLZAQ5.js → ishikawaDiagram-PHBUUO56-JmsNlo2I.js} +1 -1
  36. package/dist/assets/{journeyDiagram-4ABVD52K-CYVgkl-y.js → journeyDiagram-4ABVD52K-Cuudv7Vv.js} +1 -1
  37. package/dist/assets/{kanban-definition-K7BYSVSG-D3on0q66.js → kanban-definition-K7BYSVSG-Bappd2YO.js} +1 -1
  38. package/dist/assets/{layout-DoKWZNVk.js → layout-BmbfFZKy.js} +1 -1
  39. package/dist/assets/{linear-D4YTLdon.js → linear-WZnF-PT6.js} +1 -1
  40. package/dist/assets/{mermaid-O7DHMXV3-KLW3VWsF.js → mermaid-O7DHMXV3-D-2fQRvw.js} +5 -5
  41. package/dist/assets/{mindmap-definition-YRQLILUH-EEAggxM3.js → mindmap-definition-YRQLILUH-BQHnzzud.js} +1 -1
  42. package/dist/assets/{pieDiagram-SKSYHLDU-Da-_fmYg.js → pieDiagram-SKSYHLDU-uxjlAy1t.js} +1 -1
  43. package/dist/assets/{quadrantDiagram-337W2JSQ-Dq4gr7Sw.js → quadrantDiagram-337W2JSQ-DpwZU-f_.js} +1 -1
  44. package/dist/assets/{requirementDiagram-Z7DCOOCP-DNSXyCNU.js → requirementDiagram-Z7DCOOCP-C_9ClOWm.js} +1 -1
  45. package/dist/assets/{sankeyDiagram-WA2Y5GQK-CT4ST2HW.js → sankeyDiagram-WA2Y5GQK-2-FHHM-R.js} +1 -1
  46. package/dist/assets/{sequenceDiagram-2WXFIKYE-CshVYjrF.js → sequenceDiagram-2WXFIKYE-egns-0XI.js} +1 -1
  47. package/dist/assets/{stateDiagram-RAJIS63D-hsG5Yi2A.js → stateDiagram-RAJIS63D-DoW8U53H.js} +1 -1
  48. package/dist/assets/stateDiagram-v2-FVOUBMTO-BoFZZ4Ds.js +1 -0
  49. package/dist/assets/{timeline-definition-YZTLITO2-EztFFK5F.js → timeline-definition-YZTLITO2-chPa8ppH.js} +1 -1
  50. package/dist/assets/{treemap-KZPCXAKY-D5UZCcq_.js → treemap-KZPCXAKY-ajdAP-72.js} +1 -1
  51. package/dist/assets/{vennDiagram-LZ73GAT5-9Qwv8UTR.js → vennDiagram-LZ73GAT5-C9If0AT0.js} +1 -1
  52. package/dist/assets/{xychartDiagram-JWTSCODW-BBjpC1Qv.js → xychartDiagram-JWTSCODW-DD42U6Or.js} +1 -1
  53. package/dist/index.html +2 -2
  54. package/package.json +2 -2
  55. package/server/database/db.js +0 -45
  56. package/server/external-agent/service.js +39 -490
  57. package/server/external-agent/service.test.js +12 -0
  58. package/server/index.js +21 -43
  59. package/server/middleware/auth.js +15 -1
  60. package/server/routes/agent.js +15 -1136
  61. package/server/routes/cli-auth.js +60 -52
  62. package/server/routes/projects.js +10 -304
  63. package/server/routes/settings.js +4 -0
  64. package/server/routes/user.js +0 -102
  65. package/dist/assets/App-Cay5kE3A.js +0 -504
  66. package/dist/assets/channel-CnzaP2H9.js +0 -1
  67. package/dist/assets/classDiagram-VBA2DB6C-CUcYxMZ8.js +0 -1
  68. package/dist/assets/classDiagram-v2-RAHNMMFH-CUcYxMZ8.js +0 -1
  69. package/dist/assets/clone-DJXJGSg2.js +0 -1
  70. package/dist/assets/index-2198VgsK.css +0 -1
  71. package/dist/assets/index-EMGPq9Uy.js +0 -2
  72. package/dist/assets/stateDiagram-v2-FVOUBMTO-BQa1Ppoo.js +0 -1
@@ -1,5 +1,5 @@
1
1
  import express from 'express';
2
- import { spawn } from 'child_process';
2
+ import { constants as fsConstants } from 'fs';
3
3
  import fs from 'fs/promises';
4
4
  import path from 'path';
5
5
  import os from 'os';
@@ -26,64 +26,72 @@ function getLocatorCommand() {
26
26
  return process.platform === 'win32' ? 'where' : 'which';
27
27
  }
28
28
 
29
- function resolveCommandPath(command) {
30
- return new Promise((resolve) => {
31
- let childProcess;
32
- let stdout = '';
33
- let stderr = '';
34
-
35
- try {
36
- childProcess = spawn(getLocatorCommand(), [command], {
37
- stdio: ['ignore', 'pipe', 'pipe']
38
- });
39
- } catch (error) {
40
- resolve({
41
- found: false,
42
- resolvedPath: null,
43
- reason: `Failed to run command locator: ${error.message}`
44
- });
45
- return;
29
+ async function isRunnableCommand(candidatePath) {
30
+ try {
31
+ if (process.platform === 'win32') {
32
+ await fs.access(candidatePath, fsConstants.F_OK);
33
+ return true;
46
34
  }
47
35
 
48
- childProcess.stdout.on('data', (data) => {
49
- stdout += data.toString();
50
- });
36
+ await fs.access(candidatePath, fsConstants.X_OK);
37
+ return true;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
51
42
 
52
- childProcess.stderr.on('data', (data) => {
53
- stderr += data.toString();
54
- });
43
+ async function resolveCommandPath(command) {
44
+ const normalizedCommand = String(command || '').trim();
45
+ if (!normalizedCommand) {
46
+ return {
47
+ found: false,
48
+ resolvedPath: null,
49
+ reason: 'Command name is empty'
50
+ };
51
+ }
55
52
 
56
- childProcess.on('close', (code) => {
57
- const lines = stdout
58
- .split(/\r?\n/)
59
- .map(line => line.trim())
60
- .filter(Boolean);
61
- const resolvedPath = lines[0] || null;
62
-
63
- if (code === 0 && resolvedPath) {
64
- resolve({
65
- found: true,
66
- resolvedPath,
67
- reason: null
68
- });
69
- return;
53
+ const isDirectPath = normalizedCommand.includes(path.sep) || (process.platform === 'win32' && normalizedCommand.includes('/'));
54
+ const pathEntries = isDirectPath
55
+ ? ['']
56
+ : String(process.env.PATH || '')
57
+ .split(path.delimiter)
58
+ .map((entry) => entry.trim())
59
+ .filter(Boolean);
60
+
61
+ const windowsExtensions = process.platform === 'win32'
62
+ ? String(process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM')
63
+ .split(';')
64
+ .map((ext) => ext.trim())
65
+ .filter(Boolean)
66
+ : [''];
67
+
68
+ const candidatePaths = [];
69
+ for (const baseDir of pathEntries) {
70
+ const baseCandidate = isDirectPath ? normalizedCommand : path.join(baseDir, normalizedCommand);
71
+ candidatePaths.push(baseCandidate);
72
+
73
+ if (process.platform === 'win32' && !path.extname(baseCandidate)) {
74
+ for (const ext of windowsExtensions) {
75
+ candidatePaths.push(`${baseCandidate}${ext}`);
70
76
  }
77
+ }
78
+ }
71
79
 
72
- resolve({
73
- found: false,
74
- resolvedPath: null,
75
- reason: `${command} not found in PATH${stderr?.trim() ? ` (${stderr.trim()})` : ''}`
76
- });
77
- });
80
+ for (const candidatePath of candidatePaths) {
81
+ if (await isRunnableCommand(candidatePath)) {
82
+ return {
83
+ found: true,
84
+ resolvedPath: candidatePath,
85
+ reason: null
86
+ };
87
+ }
88
+ }
78
89
 
79
- childProcess.on('error', (error) => {
80
- resolve({
81
- found: false,
82
- resolvedPath: null,
83
- reason: `Failed to detect ${command}: ${error.message}`
84
- });
85
- });
86
- });
90
+ return {
91
+ found: false,
92
+ resolvedPath: null,
93
+ reason: `${normalizedCommand} not found in PATH`
94
+ };
87
95
  }
88
96
 
89
97
  function buildInstallationStatus(provider, command, installed, resolvedPath, reason, cacheHit = false) {
@@ -1,18 +1,11 @@
1
1
  import express from 'express';
2
2
  import { promises as fs } from 'fs';
3
3
  import path from 'path';
4
- import { spawn } from 'child_process';
5
4
  import os from 'os';
6
5
  import { addProjectManually } from '../projects.js';
7
- import { githubTokensDb } from '../database/db.js';
8
6
 
9
7
  const router = express.Router();
10
8
 
11
- function sanitizeGitError(message, token) {
12
- if (!message || !token) return message;
13
- return message.replace(new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), '***');
14
- }
15
-
16
9
  // Configure allowed workspace root (defaults to user's home directory)
17
10
  export const WORKSPACES_ROOT = process.env.WORKSPACES_ROOT || os.homedir();
18
11
 
@@ -169,9 +162,6 @@ export async function validateWorkspacePath(requestedPath) {
169
162
  * Body:
170
163
  * - workspaceType: 'existing' | 'new'
171
164
  * - path: string (workspace path)
172
- * - githubUrl?: string (optional, for new workspaces)
173
- * - githubTokenId?: number (optional, ID of stored token)
174
- * - newGithubToken?: string (optional, one-time token)
175
165
  */
176
166
  router.post('/create-workspace', async (req, res) => {
177
167
  try {
@@ -182,6 +172,16 @@ router.post('/create-workspace', async (req, res) => {
182
172
  return res.status(400).json({ error: 'workspaceType and path are required' });
183
173
  }
184
174
 
175
+ if (
176
+ githubUrl !== undefined
177
+ || githubTokenId !== undefined
178
+ || newGithubToken !== undefined
179
+ ) {
180
+ return res.status(400).json({
181
+ error: 'GitHub-based workspace creation has been removed. Create an empty workspace or add an existing local workspace instead.'
182
+ });
183
+ }
184
+
185
185
  if (!['existing', 'new'].includes(workspaceType)) {
186
186
  return res.status(400).json({ error: 'workspaceType must be "existing" or "new"' });
187
187
  }
@@ -229,66 +229,6 @@ router.post('/create-workspace', async (req, res) => {
229
229
  // Create the directory if it doesn't exist
230
230
  await fs.mkdir(absolutePath, { recursive: true });
231
231
 
232
- // If GitHub URL is provided, clone the repository
233
- if (githubUrl) {
234
- let githubToken = null;
235
-
236
- // Get GitHub token if needed
237
- if (githubTokenId) {
238
- // Fetch token from database
239
- const token = await getGithubTokenById(githubTokenId, req.user.id);
240
- if (!token) {
241
- // Clean up created directory
242
- await fs.rm(absolutePath, { recursive: true, force: true });
243
- return res.status(404).json({ error: 'GitHub token not found' });
244
- }
245
- githubToken = token.github_token;
246
- } else if (newGithubToken) {
247
- githubToken = newGithubToken;
248
- }
249
-
250
- // Extract repo name from URL for the clone destination
251
- const normalizedUrl = githubUrl.replace(/\/+$/, '').replace(/\.git$/, '');
252
- const repoName = normalizedUrl.split('/').pop() || 'repository';
253
- const clonePath = path.join(absolutePath, repoName);
254
-
255
- // Check if clone destination already exists to prevent data loss
256
- try {
257
- await fs.access(clonePath);
258
- return res.status(409).json({
259
- error: 'Directory already exists',
260
- details: `The destination path "${clonePath}" already exists. Please choose a different location or remove the existing directory.`
261
- });
262
- } catch (err) {
263
- // Directory doesn't exist, which is what we want
264
- }
265
-
266
- // Clone the repository into a subfolder
267
- try {
268
- await cloneGitHubRepository(githubUrl, clonePath, githubToken);
269
- } catch (error) {
270
- // Only clean up if clone created partial data (check if dir exists and is empty or partial)
271
- try {
272
- const stats = await fs.stat(clonePath);
273
- if (stats.isDirectory()) {
274
- await fs.rm(clonePath, { recursive: true, force: true });
275
- }
276
- } catch (cleanupError) {
277
- // Directory doesn't exist or cleanup failed - ignore
278
- }
279
- throw new Error(`Failed to clone repository: ${error.message}`);
280
- }
281
-
282
- // Add the cloned repo path to the project list
283
- const project = await addProjectManually(clonePath);
284
-
285
- return res.json({
286
- success: true,
287
- project,
288
- message: 'New workspace created and repository cloned successfully'
289
- });
290
- }
291
-
292
232
  // Add the new workspace to the project list (no clone)
293
233
  const project = await addProjectManually(absolutePath);
294
234
 
@@ -308,238 +248,4 @@ router.post('/create-workspace', async (req, res) => {
308
248
  }
309
249
  });
310
250
 
311
- /**
312
- * Helper function to get GitHub token from database
313
- */
314
- async function getGithubTokenById(tokenId, userId) {
315
- const credential = githubTokensDb.getGithubTokenById(userId, tokenId);
316
-
317
- // Return in the expected format (github_token field for compatibility)
318
- if (credential) {
319
- return {
320
- ...credential,
321
- github_token: credential.credential_value
322
- };
323
- }
324
-
325
- return null;
326
- }
327
-
328
- /**
329
- * Clone repository with progress streaming (SSE)
330
- * GET /api/projects/clone-progress
331
- */
332
- router.get('/clone-progress', async (req, res) => {
333
- const { path: workspacePath, githubUrl, githubTokenId, newGithubToken } = req.query;
334
-
335
- res.setHeader('Content-Type', 'text/event-stream');
336
- res.setHeader('Cache-Control', 'no-cache');
337
- res.setHeader('Connection', 'keep-alive');
338
- res.flushHeaders();
339
-
340
- const sendEvent = (type, data) => {
341
- res.write(`data: ${JSON.stringify({ type, ...data })}\n\n`);
342
- };
343
-
344
- try {
345
- if (!workspacePath || !githubUrl) {
346
- sendEvent('error', { message: 'workspacePath and githubUrl are required' });
347
- res.end();
348
- return;
349
- }
350
-
351
- const validation = await validateWorkspacePath(workspacePath);
352
- if (!validation.valid) {
353
- sendEvent('error', { message: validation.error });
354
- res.end();
355
- return;
356
- }
357
-
358
- const absolutePath = validation.resolvedPath;
359
-
360
- await fs.mkdir(absolutePath, { recursive: true });
361
-
362
- let githubToken = null;
363
- if (githubTokenId) {
364
- const token = await getGithubTokenById(parseInt(githubTokenId), req.user.id);
365
- if (!token) {
366
- await fs.rm(absolutePath, { recursive: true, force: true });
367
- sendEvent('error', { message: 'GitHub token not found' });
368
- res.end();
369
- return;
370
- }
371
- githubToken = token.github_token;
372
- } else if (newGithubToken) {
373
- githubToken = newGithubToken;
374
- }
375
-
376
- const normalizedUrl = githubUrl.replace(/\/+$/, '').replace(/\.git$/, '');
377
- const repoName = normalizedUrl.split('/').pop() || 'repository';
378
- const clonePath = path.join(absolutePath, repoName);
379
-
380
- // Check if clone destination already exists to prevent data loss
381
- try {
382
- await fs.access(clonePath);
383
- sendEvent('error', { message: `Directory "${repoName}" already exists. Please choose a different location or remove the existing directory.` });
384
- res.end();
385
- return;
386
- } catch (err) {
387
- // Directory doesn't exist, which is what we want
388
- }
389
-
390
- let cloneUrl = githubUrl;
391
- if (githubToken) {
392
- try {
393
- const url = new URL(githubUrl);
394
- url.username = githubToken;
395
- url.password = '';
396
- cloneUrl = url.toString();
397
- } catch (error) {
398
- // SSH URL or invalid - use as-is
399
- }
400
- }
401
-
402
- sendEvent('progress', { message: `Cloning into '${repoName}'...` });
403
-
404
- const gitProcess = spawn('git', ['clone', '--progress', cloneUrl, clonePath], {
405
- stdio: ['ignore', 'pipe', 'pipe'],
406
- env: {
407
- ...process.env,
408
- GIT_TERMINAL_PROMPT: '0'
409
- }
410
- });
411
-
412
- let lastError = '';
413
-
414
- gitProcess.stdout.on('data', (data) => {
415
- const message = data.toString().trim();
416
- if (message) {
417
- sendEvent('progress', { message });
418
- }
419
- });
420
-
421
- gitProcess.stderr.on('data', (data) => {
422
- const message = data.toString().trim();
423
- lastError = message;
424
- if (message) {
425
- sendEvent('progress', { message });
426
- }
427
- });
428
-
429
- gitProcess.on('close', async (code) => {
430
- if (code === 0) {
431
- try {
432
- const project = await addProjectManually(clonePath);
433
- sendEvent('complete', { project, message: 'Repository cloned successfully' });
434
- } catch (error) {
435
- sendEvent('error', { message: `Clone succeeded but failed to add project: ${error.message}` });
436
- }
437
- } else {
438
- const sanitizedError = sanitizeGitError(lastError, githubToken);
439
- let errorMessage = 'Git clone failed';
440
- if (lastError.includes('Authentication failed') || lastError.includes('could not read Username')) {
441
- errorMessage = 'Authentication failed. Please check your credentials.';
442
- } else if (lastError.includes('Repository not found')) {
443
- errorMessage = 'Repository not found. Please check the URL and ensure you have access.';
444
- } else if (lastError.includes('already exists')) {
445
- errorMessage = 'Directory already exists';
446
- } else if (sanitizedError) {
447
- errorMessage = sanitizedError;
448
- }
449
- try {
450
- await fs.rm(clonePath, { recursive: true, force: true });
451
- } catch (cleanupError) {
452
- console.error('Failed to clean up after clone failure:', sanitizeGitError(cleanupError.message, githubToken));
453
- }
454
- sendEvent('error', { message: errorMessage });
455
- }
456
- res.end();
457
- });
458
-
459
- gitProcess.on('error', (error) => {
460
- if (error.code === 'ENOENT') {
461
- sendEvent('error', { message: 'Git is not installed or not in PATH' });
462
- } else {
463
- sendEvent('error', { message: error.message });
464
- }
465
- res.end();
466
- });
467
-
468
- req.on('close', () => {
469
- gitProcess.kill();
470
- });
471
-
472
- } catch (error) {
473
- sendEvent('error', { message: error.message });
474
- res.end();
475
- }
476
- });
477
-
478
- /**
479
- * Helper function to clone a GitHub repository
480
- */
481
- function cloneGitHubRepository(githubUrl, destinationPath, githubToken = null) {
482
- return new Promise((resolve, reject) => {
483
- let cloneUrl = githubUrl;
484
-
485
- if (githubToken) {
486
- try {
487
- const url = new URL(githubUrl);
488
- url.username = githubToken;
489
- url.password = '';
490
- cloneUrl = url.toString();
491
- } catch (error) {
492
- // SSH URL - use as-is
493
- }
494
- }
495
-
496
- const gitProcess = spawn('git', ['clone', '--progress', cloneUrl, destinationPath], {
497
- stdio: ['ignore', 'pipe', 'pipe'],
498
- env: {
499
- ...process.env,
500
- GIT_TERMINAL_PROMPT: '0'
501
- }
502
- });
503
-
504
- let stdout = '';
505
- let stderr = '';
506
-
507
- gitProcess.stdout.on('data', (data) => {
508
- stdout += data.toString();
509
- });
510
-
511
- gitProcess.stderr.on('data', (data) => {
512
- stderr += data.toString();
513
- });
514
-
515
- gitProcess.on('close', (code) => {
516
- if (code === 0) {
517
- resolve({ stdout, stderr });
518
- } else {
519
- let errorMessage = 'Git clone failed';
520
-
521
- if (stderr.includes('Authentication failed') || stderr.includes('could not read Username')) {
522
- errorMessage = 'Authentication failed. Please check your GitHub token.';
523
- } else if (stderr.includes('Repository not found')) {
524
- errorMessage = 'Repository not found. Please check the URL and ensure you have access.';
525
- } else if (stderr.includes('already exists')) {
526
- errorMessage = 'Directory already exists';
527
- } else if (stderr) {
528
- errorMessage = stderr;
529
- }
530
-
531
- reject(new Error(errorMessage));
532
- }
533
- });
534
-
535
- gitProcess.on('error', (error) => {
536
- if (error.code === 'ENOENT') {
537
- reject(new Error('Git is not installed or not in PATH'));
538
- } else {
539
- reject(error);
540
- }
541
- });
542
- });
543
- }
544
-
545
251
  export default router;
@@ -117,6 +117,10 @@ router.post('/credentials', async (req, res) => {
117
117
  return res.status(400).json({ error: 'Credential value is required' });
118
118
  }
119
119
 
120
+ if (credentialType.trim() === 'github_token') {
121
+ return res.status(400).json({ error: 'GitHub tokens are no longer supported.' });
122
+ }
123
+
120
124
  const result = credentialsDb.createCredential(
121
125
  req.user.id,
122
126
  credentialName.trim(),
@@ -1,106 +1,4 @@
1
1
  import express from 'express';
2
- import { userDb } from '../database/db.js';
3
- import { authenticateToken } from '../middleware/auth.js';
4
- import { getSystemGitConfig } from '../utils/gitConfig.js';
5
- import { exec } from 'child_process';
6
- import { promisify } from 'util';
7
-
8
- const execAsync = promisify(exec);
9
2
  const router = express.Router();
10
3
 
11
- router.get('/git-config', authenticateToken, async (req, res) => {
12
- try {
13
- const userId = req.user.id;
14
- let gitConfig = userDb.getGitConfig(userId);
15
-
16
- // If database is empty, try to get from system git config
17
- if (!gitConfig || (!gitConfig.git_name && !gitConfig.git_email)) {
18
- const systemConfig = await getSystemGitConfig();
19
-
20
- // If system has values, save them to database for this user
21
- if (systemConfig.git_name || systemConfig.git_email) {
22
- userDb.updateGitConfig(userId, systemConfig.git_name, systemConfig.git_email);
23
- gitConfig = systemConfig;
24
- console.log(`Auto-populated git config from system for user ${userId}: ${systemConfig.git_name} <${systemConfig.git_email}>`);
25
- }
26
- }
27
-
28
- res.json({
29
- success: true,
30
- gitName: gitConfig?.git_name || null,
31
- gitEmail: gitConfig?.git_email || null
32
- });
33
- } catch (error) {
34
- console.error('Error getting git config:', error);
35
- res.status(500).json({ error: 'Failed to get git configuration' });
36
- }
37
- });
38
-
39
- // Apply git config globally via git config --global
40
- router.post('/git-config', authenticateToken, async (req, res) => {
41
- try {
42
- const userId = req.user.id;
43
- const { gitName, gitEmail } = req.body;
44
-
45
- if (!gitName || !gitEmail) {
46
- return res.status(400).json({ error: 'Git name and email are required' });
47
- }
48
-
49
- // Validate email format
50
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
51
- if (!emailRegex.test(gitEmail)) {
52
- return res.status(400).json({ error: 'Invalid email format' });
53
- }
54
-
55
- userDb.updateGitConfig(userId, gitName, gitEmail);
56
-
57
- try {
58
- await execAsync(`git config --global user.name "${gitName.replace(/"/g, '\\"')}"`);
59
- await execAsync(`git config --global user.email "${gitEmail.replace(/"/g, '\\"')}"`);
60
- console.log(`Applied git config globally: ${gitName} <${gitEmail}>`);
61
- } catch (gitError) {
62
- console.error('Error applying git config:', gitError);
63
- }
64
-
65
- res.json({
66
- success: true,
67
- gitName,
68
- gitEmail
69
- });
70
- } catch (error) {
71
- console.error('Error updating git config:', error);
72
- res.status(500).json({ error: 'Failed to update git configuration' });
73
- }
74
- });
75
-
76
- router.post('/complete-onboarding', authenticateToken, async (req, res) => {
77
- try {
78
- const userId = req.user.id;
79
- userDb.completeOnboarding(userId);
80
-
81
- res.json({
82
- success: true,
83
- message: 'Onboarding completed successfully'
84
- });
85
- } catch (error) {
86
- console.error('Error completing onboarding:', error);
87
- res.status(500).json({ error: 'Failed to complete onboarding' });
88
- }
89
- });
90
-
91
- router.get('/onboarding-status', authenticateToken, async (req, res) => {
92
- try {
93
- const userId = req.user.id;
94
- const hasCompleted = userDb.hasCompletedOnboarding(userId);
95
-
96
- res.json({
97
- success: true,
98
- hasCompletedOnboarding: hasCompleted
99
- });
100
- } catch (error) {
101
- console.error('Error checking onboarding status:', error);
102
- res.status(500).json({ error: 'Failed to check onboarding status' });
103
- }
104
- });
105
-
106
4
  export default router;