archicore 0.1.0

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 (118) hide show
  1. package/README.md +530 -0
  2. package/dist/analyzers/dead-code.d.ts +95 -0
  3. package/dist/analyzers/dead-code.js +327 -0
  4. package/dist/analyzers/duplication.d.ts +90 -0
  5. package/dist/analyzers/duplication.js +344 -0
  6. package/dist/analyzers/security.d.ts +79 -0
  7. package/dist/analyzers/security.js +484 -0
  8. package/dist/architecture/index.d.ts +35 -0
  9. package/dist/architecture/index.js +249 -0
  10. package/dist/cli/commands/analyzers.d.ts +6 -0
  11. package/dist/cli/commands/analyzers.js +431 -0
  12. package/dist/cli/commands/export.d.ts +6 -0
  13. package/dist/cli/commands/export.js +78 -0
  14. package/dist/cli/commands/index.d.ts +8 -0
  15. package/dist/cli/commands/index.js +8 -0
  16. package/dist/cli/commands/init.d.ts +26 -0
  17. package/dist/cli/commands/init.js +140 -0
  18. package/dist/cli/commands/interactive.d.ts +7 -0
  19. package/dist/cli/commands/interactive.js +522 -0
  20. package/dist/cli/commands/projects.d.ts +6 -0
  21. package/dist/cli/commands/projects.js +249 -0
  22. package/dist/cli/index.d.ts +7 -0
  23. package/dist/cli/index.js +7 -0
  24. package/dist/cli/ui/box.d.ts +17 -0
  25. package/dist/cli/ui/box.js +62 -0
  26. package/dist/cli/ui/colors.d.ts +49 -0
  27. package/dist/cli/ui/colors.js +86 -0
  28. package/dist/cli/ui/index.d.ts +9 -0
  29. package/dist/cli/ui/index.js +9 -0
  30. package/dist/cli/ui/prompt.d.ts +34 -0
  31. package/dist/cli/ui/prompt.js +122 -0
  32. package/dist/cli/ui/spinner.d.ts +29 -0
  33. package/dist/cli/ui/spinner.js +80 -0
  34. package/dist/cli/ui/table.d.ts +33 -0
  35. package/dist/cli/ui/table.js +84 -0
  36. package/dist/cli/utils/config.d.ts +23 -0
  37. package/dist/cli/utils/config.js +73 -0
  38. package/dist/cli/utils/index.d.ts +6 -0
  39. package/dist/cli/utils/index.js +6 -0
  40. package/dist/cli/utils/session.d.ts +27 -0
  41. package/dist/cli/utils/session.js +117 -0
  42. package/dist/cli.d.ts +8 -0
  43. package/dist/cli.js +295 -0
  44. package/dist/code-index/ast-parser.d.ts +16 -0
  45. package/dist/code-index/ast-parser.js +330 -0
  46. package/dist/code-index/dependency-graph.d.ts +16 -0
  47. package/dist/code-index/dependency-graph.js +161 -0
  48. package/dist/code-index/index.d.ts +44 -0
  49. package/dist/code-index/index.js +124 -0
  50. package/dist/code-index/symbol-extractor.d.ts +13 -0
  51. package/dist/code-index/symbol-extractor.js +150 -0
  52. package/dist/export/index.d.ts +92 -0
  53. package/dist/export/index.js +676 -0
  54. package/dist/github/github-service.d.ts +146 -0
  55. package/dist/github/github-service.js +609 -0
  56. package/dist/impact-engine/index.d.ts +25 -0
  57. package/dist/impact-engine/index.js +284 -0
  58. package/dist/index.d.ts +60 -0
  59. package/dist/index.js +149 -0
  60. package/dist/metrics/index.d.ts +136 -0
  61. package/dist/metrics/index.js +525 -0
  62. package/dist/orchestrator/deepseek-optimizer.d.ts +67 -0
  63. package/dist/orchestrator/deepseek-optimizer.js +320 -0
  64. package/dist/orchestrator/index.d.ts +34 -0
  65. package/dist/orchestrator/index.js +305 -0
  66. package/dist/pr-guardian/index.d.ts +143 -0
  67. package/dist/pr-guardian/index.js +553 -0
  68. package/dist/refactoring/index.d.ts +108 -0
  69. package/dist/refactoring/index.js +580 -0
  70. package/dist/rules-engine/index.d.ts +129 -0
  71. package/dist/rules-engine/index.js +482 -0
  72. package/dist/semantic-memory/embedding-service.d.ts +24 -0
  73. package/dist/semantic-memory/embedding-service.js +120 -0
  74. package/dist/semantic-memory/index.d.ts +45 -0
  75. package/dist/semantic-memory/index.js +206 -0
  76. package/dist/semantic-memory/vector-store.d.ts +27 -0
  77. package/dist/semantic-memory/vector-store.js +166 -0
  78. package/dist/server/index.d.ts +28 -0
  79. package/dist/server/index.js +141 -0
  80. package/dist/server/middleware/api-auth.d.ts +43 -0
  81. package/dist/server/middleware/api-auth.js +256 -0
  82. package/dist/server/routes/admin.d.ts +5 -0
  83. package/dist/server/routes/admin.js +123 -0
  84. package/dist/server/routes/api.d.ts +7 -0
  85. package/dist/server/routes/api.js +362 -0
  86. package/dist/server/routes/auth.d.ts +16 -0
  87. package/dist/server/routes/auth.js +191 -0
  88. package/dist/server/routes/developer.d.ts +8 -0
  89. package/dist/server/routes/developer.js +439 -0
  90. package/dist/server/routes/github.d.ts +7 -0
  91. package/dist/server/routes/github.js +495 -0
  92. package/dist/server/routes/upload.d.ts +7 -0
  93. package/dist/server/routes/upload.js +196 -0
  94. package/dist/server/services/api-key-service.d.ts +81 -0
  95. package/dist/server/services/api-key-service.js +281 -0
  96. package/dist/server/services/auth-service.d.ts +40 -0
  97. package/dist/server/services/auth-service.js +315 -0
  98. package/dist/server/services/project-service.d.ts +123 -0
  99. package/dist/server/services/project-service.js +533 -0
  100. package/dist/server/services/token-service.d.ts +107 -0
  101. package/dist/server/services/token-service.js +416 -0
  102. package/dist/server/services/upload-service.d.ts +93 -0
  103. package/dist/server/services/upload-service.js +464 -0
  104. package/dist/types/api.d.ts +188 -0
  105. package/dist/types/api.js +86 -0
  106. package/dist/types/github.d.ts +335 -0
  107. package/dist/types/github.js +5 -0
  108. package/dist/types/index.d.ts +265 -0
  109. package/dist/types/index.js +32 -0
  110. package/dist/types/user.d.ts +69 -0
  111. package/dist/types/user.js +42 -0
  112. package/dist/utils/file-utils.d.ts +20 -0
  113. package/dist/utils/file-utils.js +163 -0
  114. package/dist/utils/logger.d.ts +17 -0
  115. package/dist/utils/logger.js +41 -0
  116. package/dist/watcher/index.d.ts +125 -0
  117. package/dist/watcher/index.js +397 -0
  118. package/package.json +71 -0
@@ -0,0 +1,533 @@
1
+ /**
2
+ * Project Service
3
+ *
4
+ * Сервис для управления проектами в ArchiCore.
5
+ * Связывает API с основными модулями системы.
6
+ */
7
+ import { v4 as uuidv4 } from 'uuid';
8
+ import { CodeIndex } from '../../code-index/index.js';
9
+ import { SemanticMemory } from '../../semantic-memory/index.js';
10
+ import { ImpactEngine } from '../../impact-engine/index.js';
11
+ import { LLMOrchestrator } from '../../orchestrator/index.js';
12
+ import { ArchitectureKnowledge } from '../../architecture/index.js';
13
+ import { Logger } from '../../utils/logger.js';
14
+ import { readFile, writeFile, mkdir } from 'fs/promises';
15
+ import { existsSync } from 'fs';
16
+ import path from 'path';
17
+ // New analyzers
18
+ import { RulesEngine } from '../../rules-engine/index.js';
19
+ import { MetricsCalculator } from '../../metrics/index.js';
20
+ import { DeadCodeDetector } from '../../analyzers/dead-code.js';
21
+ import { DuplicationDetector } from '../../analyzers/duplication.js';
22
+ import { SecurityAnalyzer } from '../../analyzers/security.js';
23
+ import { RefactoringEngine } from '../../refactoring/index.js';
24
+ // Singleton instance
25
+ let instance = null;
26
+ export class ProjectService {
27
+ projects = new Map();
28
+ projectData = new Map();
29
+ dataDir;
30
+ constructor() {
31
+ // Singleton pattern
32
+ if (instance) {
33
+ return instance;
34
+ }
35
+ instance = this;
36
+ this.dataDir = process.env.ARCHICORE_DATA_DIR || '.archicore';
37
+ this.loadProjects();
38
+ }
39
+ async loadProjects() {
40
+ try {
41
+ const projectsFile = path.join(this.dataDir, 'projects.json');
42
+ if (existsSync(projectsFile)) {
43
+ const content = await readFile(projectsFile, 'utf-8');
44
+ const data = JSON.parse(content);
45
+ for (const project of data.projects) {
46
+ this.projects.set(project.id, project);
47
+ }
48
+ Logger.info(`Loaded ${this.projects.size} projects`);
49
+ }
50
+ }
51
+ catch (error) {
52
+ Logger.warn('Could not load projects:', error);
53
+ }
54
+ }
55
+ async saveProjects() {
56
+ try {
57
+ if (!existsSync(this.dataDir)) {
58
+ await mkdir(this.dataDir, { recursive: true });
59
+ }
60
+ const projectsFile = path.join(this.dataDir, 'projects.json');
61
+ const data = { projects: Array.from(this.projects.values()) };
62
+ await writeFile(projectsFile, JSON.stringify(data, null, 2));
63
+ }
64
+ catch (error) {
65
+ Logger.error('Could not save projects:', error);
66
+ }
67
+ }
68
+ async listProjects() {
69
+ return Array.from(this.projects.values());
70
+ }
71
+ async createProject(name, projectPath) {
72
+ const id = uuidv4();
73
+ const project = {
74
+ id,
75
+ name,
76
+ path: projectPath,
77
+ createdAt: new Date().toISOString(),
78
+ status: 'pending'
79
+ };
80
+ this.projects.set(id, project);
81
+ await this.saveProjects();
82
+ // Инициализируем данные проекта
83
+ await this.initializeProjectData(id);
84
+ Logger.success(`Created project: ${name} (${id})`);
85
+ return project;
86
+ }
87
+ async getProject(id) {
88
+ return this.projects.get(id) || null;
89
+ }
90
+ async initializeProjectData(projectId) {
91
+ const project = this.projects.get(projectId);
92
+ if (!project) {
93
+ throw new Error(`Project not found: ${projectId}`);
94
+ }
95
+ // Конфигурация для DeepSeek
96
+ const llmConfig = {
97
+ provider: 'deepseek',
98
+ model: process.env.DEEPSEEK_MODEL || 'deepseek-chat',
99
+ temperature: 0.1,
100
+ maxTokens: 4096,
101
+ baseURL: 'https://api.deepseek.com'
102
+ };
103
+ // SemanticMemory опционален - DeepSeek не поддерживает embeddings
104
+ let semanticMemory = null;
105
+ // Только если есть OpenAI ключ - используем embeddings
106
+ if (process.env.OPENAI_API_KEY) {
107
+ const embeddingConfig = {
108
+ provider: 'openai',
109
+ model: 'text-embedding-3-small'
110
+ };
111
+ const vectorStoreConfig = {
112
+ url: process.env.QDRANT_URL || 'http://localhost:6333',
113
+ apiKey: process.env.QDRANT_API_KEY,
114
+ collectionName: `archicore_${projectId.replace(/-/g, '_')}`
115
+ };
116
+ semanticMemory = new SemanticMemory(embeddingConfig, vectorStoreConfig);
117
+ Logger.info('SemanticMemory enabled (OpenAI embeddings)');
118
+ }
119
+ else {
120
+ Logger.warn('SemanticMemory disabled - no OPENAI_API_KEY. Semantic search unavailable.');
121
+ }
122
+ const data = {
123
+ codeIndex: new CodeIndex(project.path),
124
+ semanticMemory,
125
+ impactEngine: new ImpactEngine(),
126
+ orchestrator: new LLMOrchestrator(llmConfig),
127
+ architecture: new ArchitectureKnowledge(path.join(project.path, '.aiarhitector/architecture.json'))
128
+ };
129
+ this.projectData.set(projectId, data);
130
+ return data;
131
+ }
132
+ async getProjectData(projectId) {
133
+ let data = this.projectData.get(projectId);
134
+ if (!data) {
135
+ data = await this.initializeProjectData(projectId);
136
+ }
137
+ return data;
138
+ }
139
+ async indexProject(projectId) {
140
+ const project = this.projects.get(projectId);
141
+ if (!project) {
142
+ throw new Error(`Project not found: ${projectId}`);
143
+ }
144
+ project.status = 'indexing';
145
+ await this.saveProjects();
146
+ try {
147
+ const data = await this.getProjectData(projectId);
148
+ Logger.progress(`Indexing project: ${project.name}`);
149
+ // 1. Парсинг AST
150
+ const asts = await data.codeIndex.parseProject();
151
+ data.asts = asts;
152
+ // 2. Извлечение символов
153
+ const symbols = data.codeIndex.extractSymbols(asts);
154
+ data.symbols = symbols;
155
+ // 3. Построение графа зависимостей
156
+ const graph = data.codeIndex.buildDependencyGraph(asts, symbols);
157
+ data.graph = graph;
158
+ // 4. Индексация в семантическую память (если доступна)
159
+ if (data.semanticMemory) {
160
+ await data.semanticMemory.initialize();
161
+ await data.semanticMemory.indexSymbols(symbols, asts);
162
+ }
163
+ // 5. Загрузка архитектурных знаний
164
+ await data.architecture.load();
165
+ // Обновляем статус и статистику
166
+ const stats = {
167
+ filesCount: asts.size,
168
+ symbolsCount: symbols.size,
169
+ nodesCount: graph.nodes.size,
170
+ edgesCount: Array.from(graph.edges.values()).reduce((sum, edges) => sum + edges.length, 0)
171
+ };
172
+ project.status = 'ready';
173
+ project.lastIndexedAt = new Date().toISOString();
174
+ project.stats = stats;
175
+ await this.saveProjects();
176
+ Logger.success(`Project indexed: ${stats.filesCount} files, ${stats.symbolsCount} symbols`);
177
+ return { success: true, stats };
178
+ }
179
+ catch (error) {
180
+ project.status = 'error';
181
+ await this.saveProjects();
182
+ Logger.error('Indexing failed:', error);
183
+ throw error;
184
+ }
185
+ }
186
+ async getArchitecture(projectId) {
187
+ const data = await this.getProjectData(projectId);
188
+ const model = data.architecture.getModel();
189
+ // Преобразуем граф в формат для визуализации
190
+ const graphData = this.convertGraphForVisualization(data.graph);
191
+ return {
192
+ boundedContexts: model.boundedContexts,
193
+ entities: model.entities,
194
+ rules: model.rules.map(r => ({
195
+ id: r.id,
196
+ description: r.description,
197
+ type: r.type,
198
+ severity: r.severity
199
+ })),
200
+ graph: graphData
201
+ };
202
+ }
203
+ async getDependencyGraph(projectId) {
204
+ const data = await this.getProjectData(projectId);
205
+ return this.convertGraphForVisualization(data.graph);
206
+ }
207
+ convertGraphForVisualization(graph) {
208
+ if (!graph) {
209
+ return { nodes: [], edges: [] };
210
+ }
211
+ const nodes = Array.from(graph.nodes.entries()).map(([id, node]) => ({
212
+ id,
213
+ label: node.name,
214
+ type: node.type,
215
+ filePath: node.filePath,
216
+ // Для визуализации - группировка по папкам
217
+ group: node.filePath.split('/').slice(-2, -1)[0] || 'root'
218
+ }));
219
+ const edges = [];
220
+ for (const [from, edgeList] of graph.edges) {
221
+ for (const edge of edgeList) {
222
+ edges.push({
223
+ from,
224
+ to: edge.to,
225
+ type: edge.type,
226
+ weight: edge.weight || 1
227
+ });
228
+ }
229
+ }
230
+ return { nodes, edges };
231
+ }
232
+ async analyzeImpact(projectId, change) {
233
+ const data = await this.getProjectData(projectId);
234
+ if (!data.graph || !data.symbols) {
235
+ throw new Error('Project not indexed. Please index first.');
236
+ }
237
+ const changeObj = {
238
+ type: change.type,
239
+ description: change.description,
240
+ files: change.files,
241
+ symbols: change.symbols
242
+ };
243
+ const impact = data.impactEngine.analyzeChange(changeObj, data.graph, data.symbols, data.architecture.getModel());
244
+ return impact;
245
+ }
246
+ async simulateChange(projectId, changeDescription) {
247
+ const data = await this.getProjectData(projectId);
248
+ let affectedFiles = [];
249
+ let affectedSymbols = [];
250
+ let searchResults = [];
251
+ // Если есть семантическая память - ищем релевантные файлы
252
+ if (data.semanticMemory) {
253
+ const results = await data.semanticMemory.searchByQuery(changeDescription, 5);
254
+ searchResults = results;
255
+ affectedFiles = results.map(r => r.chunk.metadata.filePath);
256
+ affectedSymbols = results.flatMap(r => r.chunk.metadata.symbols);
257
+ }
258
+ // Анализируем impact
259
+ const impact = await this.analyzeImpact(projectId, {
260
+ description: changeDescription,
261
+ files: affectedFiles,
262
+ symbols: affectedSymbols,
263
+ type: 'modify'
264
+ });
265
+ // Получаем AI анализ
266
+ const aiAnalysis = await data.orchestrator.analyzeImpact(impact, {
267
+ architecture: data.architecture.getModel(),
268
+ semanticMemory: searchResults
269
+ });
270
+ return {
271
+ impact,
272
+ aiAnalysis,
273
+ recommendations: impact.recommendations.map(r => r.description)
274
+ };
275
+ }
276
+ async semanticSearch(projectId, query, limit = 10) {
277
+ const data = await this.getProjectData(projectId);
278
+ if (!data.semanticMemory) {
279
+ throw new Error('Semantic search unavailable - no embedding API configured');
280
+ }
281
+ const results = await data.semanticMemory.searchByQuery(query, limit);
282
+ return results.map(r => ({
283
+ filePath: r.chunk.metadata.filePath,
284
+ startLine: r.chunk.metadata.startLine,
285
+ endLine: r.chunk.metadata.endLine,
286
+ type: r.chunk.metadata.type,
287
+ symbols: r.chunk.metadata.symbols,
288
+ score: r.score,
289
+ context: r.context
290
+ }));
291
+ }
292
+ async askArchitect(projectId, question, language = 'en') {
293
+ const data = await this.getProjectData(projectId);
294
+ // Ищем релевантный контекст (если есть семантическая память)
295
+ let searchResults = [];
296
+ if (data.semanticMemory) {
297
+ try {
298
+ searchResults = await data.semanticMemory.searchByQuery(question, 5);
299
+ Logger.debug(`Semantic search returned ${searchResults.length} results`);
300
+ }
301
+ catch (err) {
302
+ Logger.warn('Semantic search failed, using fallback');
303
+ }
304
+ }
305
+ // Fallback: если семантический поиск не работает, даём контекст из графа
306
+ if (searchResults.length === 0 && data.graph) {
307
+ Logger.info('Using graph fallback for context');
308
+ const files = Array.from(data.graph.nodes.values()).slice(0, 10);
309
+ for (const file of files) {
310
+ searchResults.push({
311
+ chunk: {
312
+ id: file.id,
313
+ content: `File: ${file.filePath}\nType: ${file.type}`,
314
+ embedding: [],
315
+ metadata: {
316
+ filePath: file.filePath,
317
+ startLine: 0,
318
+ endLine: 0,
319
+ type: 'module',
320
+ symbols: [],
321
+ tags: []
322
+ }
323
+ },
324
+ score: 1,
325
+ context: `File: ${file.filePath}`
326
+ });
327
+ }
328
+ }
329
+ // Добавляем информацию о символах если есть
330
+ if (data.symbols && data.symbols.size > 0) {
331
+ const symbolsList = Array.from(data.symbols.values()).slice(0, 20);
332
+ for (const sym of symbolsList) {
333
+ const existing = searchResults.find(r => r.chunk.metadata.filePath === sym.filePath);
334
+ if (existing) {
335
+ existing.context += `\n- ${sym.kind}: ${sym.name}`;
336
+ }
337
+ }
338
+ }
339
+ Logger.debug(`Passing ${searchResults.length} context items to AI`);
340
+ const answer = await data.orchestrator.answerArchitecturalQuestion(question, {
341
+ architecture: data.architecture.getModel(),
342
+ semanticMemory: searchResults,
343
+ language
344
+ });
345
+ return answer;
346
+ }
347
+ async getStatistics(projectId) {
348
+ const project = this.projects.get(projectId);
349
+ if (!project) {
350
+ throw new Error(`Project not found: ${projectId}`);
351
+ }
352
+ let memoryStats = null;
353
+ try {
354
+ const data = await this.getProjectData(projectId);
355
+ if (data.semanticMemory) {
356
+ memoryStats = await data.semanticMemory.getStatistics();
357
+ }
358
+ }
359
+ catch {
360
+ // Игнорируем если семантическая память не инициализирована
361
+ }
362
+ return {
363
+ project,
364
+ indexStats: project.stats || null,
365
+ memoryStats
366
+ };
367
+ }
368
+ async deleteProject(projectId) {
369
+ this.projects.delete(projectId);
370
+ this.projectData.delete(projectId);
371
+ await this.saveProjects();
372
+ Logger.info(`Deleted project: ${projectId}`);
373
+ }
374
+ /**
375
+ * Получить метрики кода
376
+ */
377
+ async getMetrics(projectId) {
378
+ const data = await this.getProjectData(projectId);
379
+ if (!data.graph || !data.symbols || !data.asts) {
380
+ throw new Error('Project not indexed. Please index first.');
381
+ }
382
+ const fileContents = await this.getFileContents(projectId);
383
+ const calculator = new MetricsCalculator();
384
+ return calculator.calculateProjectMetrics(data.graph, data.symbols, fileContents, data.asts);
385
+ }
386
+ /**
387
+ * Проверить правила архитектуры
388
+ */
389
+ async checkRules(projectId) {
390
+ const data = await this.getProjectData(projectId);
391
+ if (!data.graph || !data.symbols) {
392
+ throw new Error('Project not indexed. Please index first.');
393
+ }
394
+ const fileContents = await this.getFileContents(projectId);
395
+ const rulesEngine = new RulesEngine();
396
+ return rulesEngine.check(data.graph, data.symbols, fileContents);
397
+ }
398
+ /**
399
+ * Найти мёртвый код
400
+ */
401
+ async findDeadCode(projectId) {
402
+ const data = await this.getProjectData(projectId);
403
+ if (!data.graph || !data.symbols) {
404
+ throw new Error('Project not indexed. Please index first.');
405
+ }
406
+ const fileContents = await this.getFileContents(projectId);
407
+ const detector = new DeadCodeDetector();
408
+ return detector.analyze(data.graph, data.symbols, fileContents);
409
+ }
410
+ /**
411
+ * Найти дублирование кода
412
+ */
413
+ async findDuplication(projectId) {
414
+ const fileContents = await this.getFileContents(projectId);
415
+ const detector = new DuplicationDetector();
416
+ return detector.analyze(fileContents);
417
+ }
418
+ /**
419
+ * Анализ безопасности
420
+ */
421
+ async analyzeSecurity(projectId) {
422
+ const fileContents = await this.getFileContents(projectId);
423
+ const analyzer = new SecurityAnalyzer();
424
+ return analyzer.analyze(fileContents);
425
+ }
426
+ /**
427
+ * Получить предложения по рефакторингу
428
+ */
429
+ async getRefactoringSuggestions(projectId) {
430
+ const data = await this.getProjectData(projectId);
431
+ if (!data.graph || !data.symbols || !data.asts) {
432
+ throw new Error('Project not indexed. Please index first.');
433
+ }
434
+ const fileContents = await this.getFileContents(projectId);
435
+ // Запускаем необходимые анализаторы
436
+ const metrics = await this.getMetrics(projectId);
437
+ const duplication = await this.findDuplication(projectId);
438
+ const deadCode = await this.findDeadCode(projectId);
439
+ const rulesResult = await this.checkRules(projectId);
440
+ const engine = new RefactoringEngine();
441
+ return engine.analyze(data.graph, data.symbols, fileContents, metrics, duplication, deadCode, rulesResult);
442
+ }
443
+ /**
444
+ * Получить данные для экспорта
445
+ */
446
+ async getExportData(projectId) {
447
+ const project = this.projects.get(projectId);
448
+ if (!project) {
449
+ throw new Error(`Project not found: ${projectId}`);
450
+ }
451
+ const data = await this.getProjectData(projectId);
452
+ // Собираем все данные
453
+ const metrics = data.graph && data.symbols && data.asts
454
+ ? await this.getMetrics(projectId)
455
+ : undefined;
456
+ const rules = data.graph && data.symbols
457
+ ? await this.checkRules(projectId)
458
+ : undefined;
459
+ const deadCode = data.graph && data.symbols
460
+ ? await this.findDeadCode(projectId)
461
+ : undefined;
462
+ const duplication = await this.findDuplication(projectId);
463
+ const security = await this.analyzeSecurity(projectId);
464
+ return {
465
+ projectName: project.name,
466
+ exportDate: new Date().toISOString(),
467
+ version: '1.0.0',
468
+ graph: data.graph,
469
+ symbols: data.symbols,
470
+ metrics,
471
+ rules,
472
+ deadCode,
473
+ duplication,
474
+ security
475
+ };
476
+ }
477
+ /**
478
+ * Полный анализ проекта
479
+ */
480
+ async runFullAnalysis(projectId) {
481
+ Logger.progress('Running full analysis...');
482
+ const [metrics, rules, deadCode, duplication, security] = await Promise.all([
483
+ this.getMetrics(projectId),
484
+ this.checkRules(projectId),
485
+ this.findDeadCode(projectId),
486
+ this.findDuplication(projectId),
487
+ this.analyzeSecurity(projectId)
488
+ ]);
489
+ // Refactoring зависит от предыдущих результатов
490
+ const data = await this.getProjectData(projectId);
491
+ const fileContents = await this.getFileContents(projectId);
492
+ const engine = new RefactoringEngine();
493
+ const refactoring = await engine.analyze(data.graph, data.symbols, fileContents, metrics, duplication, deadCode, rules);
494
+ Logger.success('Full analysis complete');
495
+ return {
496
+ metrics,
497
+ rules,
498
+ deadCode,
499
+ duplication,
500
+ security,
501
+ refactoring
502
+ };
503
+ }
504
+ /**
505
+ * Получить содержимое файлов проекта
506
+ */
507
+ async getFileContents(projectId) {
508
+ const project = this.projects.get(projectId);
509
+ if (!project) {
510
+ throw new Error(`Project not found: ${projectId}`);
511
+ }
512
+ const data = await this.getProjectData(projectId);
513
+ const fileContents = new Map();
514
+ if (data.asts) {
515
+ for (const [filePath] of data.asts) {
516
+ try {
517
+ const fullPath = path.isAbsolute(filePath)
518
+ ? filePath
519
+ : path.join(project.path, filePath);
520
+ if (existsSync(fullPath)) {
521
+ const content = await readFile(fullPath, 'utf-8');
522
+ fileContents.set(filePath, content);
523
+ }
524
+ }
525
+ catch {
526
+ // Игнорируем ошибки чтения
527
+ }
528
+ }
529
+ }
530
+ return fileContents;
531
+ }
532
+ }
533
+ //# sourceMappingURL=project-service.js.map
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Token Service for ArchiCore Developer API
3
+ *
4
+ * Подсчёт токенов, биллинг и rate limiting
5
+ */
6
+ import { TokenUsage, ApiOperation, BillingAccount, RateLimitState, RateLimitResult, UsageStats } from '../../types/api.js';
7
+ export declare class TokenService {
8
+ private dataDir;
9
+ private usageRecords;
10
+ private billingAccounts;
11
+ private rateLimitStates;
12
+ private initialized;
13
+ constructor(dataDir?: string);
14
+ private ensureInitialized;
15
+ private saveUsage;
16
+ private saveBilling;
17
+ private saveRateLimits;
18
+ /**
19
+ * Подсчёт токенов для операции
20
+ */
21
+ calculateTokens(operation: ApiOperation, params: {
22
+ fileCount?: number;
23
+ contentSizeKb?: number;
24
+ inputText?: string;
25
+ outputText?: string;
26
+ }): {
27
+ inputTokens: number;
28
+ outputTokens: number;
29
+ totalTokens: number;
30
+ };
31
+ /**
32
+ * Расчёт стоимости в центах
33
+ */
34
+ calculateCost(totalTokens: number, pricingTier: string): number;
35
+ /**
36
+ * Запись использования токенов
37
+ */
38
+ recordUsage(apiKeyId: string, userId: string, operation: ApiOperation, tokens: {
39
+ inputTokens: number;
40
+ outputTokens: number;
41
+ totalTokens: number;
42
+ }, projectId?: string, metadata?: Record<string, unknown>): Promise<TokenUsage>;
43
+ /**
44
+ * Получение истории использования
45
+ */
46
+ getUsageHistory(userId: string, options: {
47
+ limit?: number;
48
+ offset?: number;
49
+ apiKeyId?: string;
50
+ operation?: ApiOperation;
51
+ }): Promise<TokenUsage[]>;
52
+ /**
53
+ * Получение статистики использования
54
+ */
55
+ getUsageStats(userId: string, period: 'hour' | 'day' | 'week' | 'month'): Promise<UsageStats>;
56
+ /**
57
+ * Получение или создание billing account
58
+ */
59
+ getBillingAccount(userId: string): Promise<BillingAccount>;
60
+ /**
61
+ * Обновление использования в billing account
62
+ */
63
+ private updateBillingUsage;
64
+ /**
65
+ * Пополнение баланса
66
+ */
67
+ addCredits(userId: string, amount: number): Promise<BillingAccount>;
68
+ /**
69
+ * Изменение pricing tier
70
+ */
71
+ updatePricingTier(userId: string, tier: string): Promise<BillingAccount>;
72
+ /**
73
+ * Проверка достаточности баланса
74
+ */
75
+ checkBalance(userId: string, estimatedTokens: number): Promise<{
76
+ allowed: boolean;
77
+ balance: number;
78
+ estimatedCost: number;
79
+ tokensRemaining: number;
80
+ }>;
81
+ /**
82
+ * Проверка rate limit
83
+ */
84
+ checkRateLimit(apiKeyId: string, limits: {
85
+ requestsPerMinute: number;
86
+ requestsPerDay: number;
87
+ tokensPerMinute: number;
88
+ tokensPerDay: number;
89
+ }): Promise<RateLimitResult>;
90
+ /**
91
+ * Инкремент rate limit счётчиков
92
+ */
93
+ incrementRateLimit(apiKeyId: string, tokens: number): Promise<void>;
94
+ /**
95
+ * Получение текущего состояния rate limit
96
+ */
97
+ getRateLimitState(apiKeyId: string): Promise<RateLimitState | null>;
98
+ /**
99
+ * Очистка старых записей usage (старше 90 дней)
100
+ */
101
+ cleanupOldUsageRecords(): Promise<number>;
102
+ /**
103
+ * Сброс месячных лимитов (вызывается в начале месяца)
104
+ */
105
+ resetMonthlyLimits(): Promise<void>;
106
+ }
107
+ //# sourceMappingURL=token-service.d.ts.map