archicore 0.3.2 → 0.3.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.
@@ -554,7 +554,7 @@ async function handleIndexCommand() {
554
554
  });
555
555
  if (response.ok) {
556
556
  const data = await response.json();
557
- state.projectId = data.id || data.project?.id ?? null;
557
+ state.projectId = data.id || (data.project && data.project.id) || null;
558
558
  registerSpinner.succeed('Project registered');
559
559
  }
560
560
  else {
@@ -693,7 +693,7 @@ async function handleIndexCommand() {
693
693
  });
694
694
  if (reRegisterResponse.ok) {
695
695
  const reData = await reRegisterResponse.json();
696
- state.projectId = reData.id || reData.project?.id ?? null;
696
+ state.projectId = reData.id || (reData.project && reData.project.id) || null;
697
697
  printInfo('Project re-registered. Run /index again to complete indexing.');
698
698
  // Обновляем локальный конфиг
699
699
  const localProjectRetry = await getLocalProject(state.projectPath);
@@ -21,6 +21,140 @@ export const gitlabRouter = Router();
21
21
  const gitlabService = new GitLabService();
22
22
  const projectService = new ProjectService();
23
23
  const authService = AuthService.getInstance();
24
+ // ===== SIMPLIFIED CONNECT (for frontend) =====
25
+ /**
26
+ * POST /api/gitlab/connect
27
+ * Simplified endpoint to connect a GitLab repository in one step
28
+ * Creates instance if needed, then connects repository
29
+ */
30
+ gitlabRouter.post('/connect', authMiddleware, async (req, res) => {
31
+ try {
32
+ if (!req.user) {
33
+ res.status(401).json({ error: 'Not authenticated' });
34
+ return;
35
+ }
36
+ const { gitlabUrl, repoPath, branch, token, autoAnalyze, analyzeMRs } = req.body;
37
+ if (!repoPath) {
38
+ res.status(400).json({ error: 'Repository path is required (e.g., owner/repo)' });
39
+ return;
40
+ }
41
+ const instanceUrl = gitlabUrl || 'https://gitlab.com';
42
+ // Check if we have an existing instance for this URL
43
+ let instances = await gitlabService.getInstances(req.user.id);
44
+ let instance = instances.find(i => i.instanceUrl === instanceUrl);
45
+ // If no instance and no token provided, return error
46
+ if (!instance && !token) {
47
+ res.status(400).json({
48
+ error: 'GitLab access token required for first connection',
49
+ message: 'Please provide a Personal Access Token with api scope'
50
+ });
51
+ return;
52
+ }
53
+ // Create instance if needed
54
+ if (!instance && token) {
55
+ try {
56
+ instance = await gitlabService.addInstance(req.user.id, instanceUrl, token, { name: instanceUrl.replace('https://', '').replace('http://', '') });
57
+ Logger.info(`Created GitLab instance for ${instanceUrl}`);
58
+ }
59
+ catch (e) {
60
+ Logger.error('Failed to create GitLab instance:', e);
61
+ res.status(400).json({
62
+ error: 'Failed to connect to GitLab',
63
+ message: e instanceof Error ? e.message : 'Invalid token or GitLab URL'
64
+ });
65
+ return;
66
+ }
67
+ }
68
+ if (!instance) {
69
+ res.status(400).json({ error: 'No GitLab instance available' });
70
+ return;
71
+ }
72
+ // Find the project by path
73
+ const projects = await gitlabService.listProjects(req.user.id, instance.id, { search: repoPath });
74
+ const project = projects.find(p => p.path_with_namespace === repoPath);
75
+ if (!project) {
76
+ res.status(404).json({
77
+ error: 'Repository not found',
78
+ message: `Could not find repository "${repoPath}" on ${instanceUrl}`
79
+ });
80
+ return;
81
+ }
82
+ // Check usage limit
83
+ const usageResult = await authService.checkAndUpdateUsage(req.user.id, 'project');
84
+ if (!usageResult.allowed) {
85
+ res.status(429).json({
86
+ error: 'Project limit reached',
87
+ message: `You have reached your daily project limit (${usageResult.limit})`,
88
+ usage: { used: usageResult.limit, limit: usageResult.limit, remaining: 0 }
89
+ });
90
+ return;
91
+ }
92
+ // Connect repository
93
+ const connectedRepo = await gitlabService.connectRepository(req.user.id, instance.id, project.id, { autoAnalyze: autoAnalyze !== false, analyzeMRs: analyzeMRs !== false });
94
+ // Download and create ArchiCore project
95
+ const targetBranch = branch || project.default_branch || 'main';
96
+ Logger.progress(`Downloading GitLab repository: ${project.path_with_namespace} (branch: ${targetBranch})`);
97
+ const zipBuffer = await gitlabService.downloadRepository(req.user.id, instance.id, project.id, targetBranch);
98
+ // Create projects directory
99
+ const projectsDir = process.env.PROJECTS_DIR || join('.archicore', 'projects');
100
+ await mkdir(projectsDir, { recursive: true });
101
+ // Extract to project directory
102
+ const projectPath = join(projectsDir, project.name);
103
+ await mkdir(projectPath, { recursive: true });
104
+ const zip = new AdmZip(zipBuffer);
105
+ const zipEntries = zip.getEntries();
106
+ // Extract files
107
+ for (const entry of zipEntries) {
108
+ if (entry.isDirectory) {
109
+ const dirPath = join(projectPath, entry.entryName);
110
+ await mkdir(dirPath, { recursive: true });
111
+ }
112
+ else {
113
+ const filePath = join(projectPath, entry.entryName);
114
+ const fileDir = join(projectPath, entry.entryName.split('/').slice(0, -1).join('/'));
115
+ await mkdir(fileDir, { recursive: true });
116
+ const content = entry.getData();
117
+ const { writeFile } = await import('fs/promises');
118
+ await writeFile(filePath, content);
119
+ }
120
+ }
121
+ // GitLab creates a folder like "project-branch" inside, find it
122
+ const { readdir, stat } = await import('fs/promises');
123
+ const contents = await readdir(projectPath);
124
+ let actualPath = projectPath;
125
+ if (contents.length === 1) {
126
+ const innerPath = join(projectPath, contents[0]);
127
+ const stats = await stat(innerPath);
128
+ if (stats.isDirectory()) {
129
+ actualPath = innerPath;
130
+ }
131
+ }
132
+ Logger.success(`Downloaded and extracted to: ${actualPath}`);
133
+ // Create ArchiCore project
134
+ const archiProject = await projectService.createProject(project.name, actualPath, req.user.id);
135
+ // Update connected repo with project ID
136
+ await gitlabService.updateRepositoryProjectId(connectedRepo.id, archiProject.id);
137
+ await gitlabService.updateRepositoryStatus(connectedRepo.id, 'active');
138
+ res.json({
139
+ success: true,
140
+ project: {
141
+ id: archiProject.id,
142
+ name: archiProject.name,
143
+ path: actualPath
144
+ },
145
+ repository: {
146
+ id: connectedRepo.id,
147
+ fullPath: project.path_with_namespace,
148
+ branch: targetBranch
149
+ }
150
+ });
151
+ }
152
+ catch (error) {
153
+ Logger.error('GitLab connect error:', error);
154
+ const message = error instanceof Error ? error.message : 'Failed to connect GitLab repository';
155
+ res.status(500).json({ error: message });
156
+ }
157
+ });
24
158
  // ===== INSTANCE MANAGEMENT =====
25
159
  /**
26
160
  * POST /api/gitlab/instances
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archicore",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "AI Software Architect - code analysis, impact prediction, semantic search",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",