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,464 @@
1
+ /**
2
+ * Upload Service
3
+ *
4
+ * Безопасная загрузка и распаковка ZIP/RAR архивов.
5
+ *
6
+ * Защита от:
7
+ * - ZIP-бомб (проверка соотношения сжатия)
8
+ * - Path traversal атак (../../../etc/passwd)
9
+ * - Опасных файлов (исполняемые, скрипты и т.д.)
10
+ * - Слишком больших файлов
11
+ */
12
+ import { v4 as uuidv4 } from 'uuid';
13
+ import AdmZip from 'adm-zip';
14
+ import path from 'path';
15
+ import { mkdir, rm, stat, readdir, writeFile } from 'fs/promises';
16
+ import { existsSync } from 'fs';
17
+ import { Logger } from '../../utils/logger.js';
18
+ // Опасные расширения файлов
19
+ const DANGEROUS_EXTENSIONS = new Set([
20
+ // Исполняемые
21
+ '.exe', '.dll', '.so', '.dylib', '.bin', '.com', '.bat', '.cmd', '.ps1', '.vbs',
22
+ '.msi', '.msp', '.scr', '.pif', '.gadget', '.hta', '.cpl', '.msc', '.jar',
23
+ // Скрипты с shell
24
+ '.sh', '.bash', '.zsh', '.fish', '.ksh',
25
+ // Архивы-матрёшки (могут содержать вредоносный код)
26
+ '.zip', '.rar', '.7z', '.tar', '.gz', '.bz2', '.xz',
27
+ // Потенциально опасные
28
+ '.reg', '.inf', '.lnk', '.url', '.scf'
29
+ ]);
30
+ // Разрешённые расширения для кода
31
+ const ALLOWED_CODE_EXTENSIONS = new Set([
32
+ // JavaScript/TypeScript
33
+ '.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
34
+ // Vue/React/Angular
35
+ '.vue', '.svelte',
36
+ // Стили
37
+ '.css', '.scss', '.sass', '.less', '.styl',
38
+ // Разметка
39
+ '.html', '.htm', '.xml', '.svg',
40
+ // Python
41
+ '.py', '.pyw', '.pyx', '.pyd', '.pyi',
42
+ // Go
43
+ '.go',
44
+ // Rust
45
+ '.rs',
46
+ // Java/Kotlin/Scala
47
+ '.java', '.kt', '.kts', '.scala',
48
+ // C/C++
49
+ '.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', '.hh',
50
+ // C#
51
+ '.cs',
52
+ // PHP
53
+ '.php', '.phtml',
54
+ // Ruby
55
+ '.rb', '.erb', '.rake',
56
+ // Swift
57
+ '.swift',
58
+ // Конфиги и данные
59
+ '.json', '.yaml', '.yml', '.toml', '.ini', '.env.example', '.env.sample',
60
+ '.lock', '.config', '.conf',
61
+ // Документация
62
+ '.md', '.mdx', '.txt', '.rst',
63
+ // GraphQL
64
+ '.graphql', '.gql',
65
+ // Другое
66
+ '.sql', '.prisma', '.proto'
67
+ ]);
68
+ // Паттерны опасных путей
69
+ const DANGEROUS_PATH_PATTERNS = [
70
+ /\.\./, // Path traversal
71
+ /^\/etc\//, // System files
72
+ /^\/var\//,
73
+ /^\/usr\//,
74
+ /^\/bin\//,
75
+ /^\/sbin\//,
76
+ /^C:\\Windows/i, // Windows system
77
+ /^C:\\Program Files/i,
78
+ /^C:\\Users\\.*\\AppData/i,
79
+ /\0/, // Null byte injection
80
+ ];
81
+ export class UploadService {
82
+ config;
83
+ constructor(config) {
84
+ this.config = {
85
+ maxFileSize: config?.maxFileSize || 100 * 1024 * 1024, // 100MB default
86
+ maxExtractedSize: config?.maxExtractedSize || 500 * 1024 * 1024, // 500MB extracted
87
+ maxCompressionRatio: config?.maxCompressionRatio || 100, // Max 100x compression
88
+ uploadsDir: config?.uploadsDir || '.archicore/uploads',
89
+ extractDir: config?.extractDir || '.archicore/projects'
90
+ };
91
+ }
92
+ /**
93
+ * Проверка безопасности архива БЕЗ распаковки
94
+ */
95
+ async scanArchive(filePath) {
96
+ const threats = [];
97
+ const warnings = [];
98
+ try {
99
+ const fileStats = await stat(filePath);
100
+ // Проверка размера файла
101
+ if (fileStats.size > this.config.maxFileSize) {
102
+ threats.push({
103
+ type: 'oversized',
104
+ description: `File size ${this.formatBytes(fileStats.size)} exceeds limit ${this.formatBytes(this.config.maxFileSize)}`,
105
+ severity: 'high'
106
+ });
107
+ return { safe: false, threats, warnings };
108
+ }
109
+ const zip = new AdmZip(filePath);
110
+ const entries = zip.getEntries();
111
+ let totalUncompressedSize = 0;
112
+ for (const entry of entries) {
113
+ const entryName = entry.entryName;
114
+ const ext = path.extname(entryName).toLowerCase();
115
+ // 1. Проверка на path traversal
116
+ if (this.isPathTraversal(entryName)) {
117
+ threats.push({
118
+ type: 'path_traversal',
119
+ description: `Suspicious path detected: ${entryName}`,
120
+ file: entryName,
121
+ severity: 'critical'
122
+ });
123
+ }
124
+ // 2. Проверка на опасные файлы
125
+ if (DANGEROUS_EXTENSIONS.has(ext)) {
126
+ threats.push({
127
+ type: 'dangerous_file',
128
+ description: `Dangerous file type: ${ext}`,
129
+ file: entryName,
130
+ severity: 'high'
131
+ });
132
+ }
133
+ // 3. Проверка на symlinks
134
+ if (entry.header.attr === 0xA1FF0000 || entry.isDirectory && entryName.includes('->')) {
135
+ threats.push({
136
+ type: 'symlink',
137
+ description: 'Symbolic link detected',
138
+ file: entryName,
139
+ severity: 'medium'
140
+ });
141
+ }
142
+ // 4. Предупреждение о неизвестных типах файлов
143
+ if (!entry.isDirectory && !ALLOWED_CODE_EXTENSIONS.has(ext) && !DANGEROUS_EXTENSIONS.has(ext)) {
144
+ // Исключаем файлы без расширения (могут быть конфигами)
145
+ if (ext && ext !== '') {
146
+ warnings.push(`Unknown file type will be skipped: ${entryName}`);
147
+ }
148
+ }
149
+ // 5. Накапливаем размер для проверки zip-бомбы
150
+ if (!entry.isDirectory) {
151
+ totalUncompressedSize += entry.header.size;
152
+ }
153
+ }
154
+ // 6. Проверка на zip-бомбу (соотношение сжатия)
155
+ const compressionRatio = totalUncompressedSize / fileStats.size;
156
+ if (compressionRatio > this.config.maxCompressionRatio) {
157
+ threats.push({
158
+ type: 'zip_bomb',
159
+ description: `Suspicious compression ratio: ${compressionRatio.toFixed(1)}x (limit: ${this.config.maxCompressionRatio}x)`,
160
+ severity: 'critical'
161
+ });
162
+ }
163
+ // 7. Проверка общего размера после распаковки
164
+ if (totalUncompressedSize > this.config.maxExtractedSize) {
165
+ threats.push({
166
+ type: 'oversized',
167
+ description: `Extracted size ${this.formatBytes(totalUncompressedSize)} exceeds limit ${this.formatBytes(this.config.maxExtractedSize)}`,
168
+ severity: 'high'
169
+ });
170
+ }
171
+ const hasCriticalThreats = threats.some(t => t.severity === 'critical' || t.severity === 'high');
172
+ return {
173
+ safe: !hasCriticalThreats,
174
+ threats,
175
+ warnings
176
+ };
177
+ }
178
+ catch (error) {
179
+ Logger.error('Archive scan failed:', error);
180
+ threats.push({
181
+ type: 'dangerous_file',
182
+ description: `Failed to scan archive: ${error instanceof Error ? error.message : 'Unknown error'}`,
183
+ severity: 'critical'
184
+ });
185
+ return { safe: false, threats, warnings };
186
+ }
187
+ }
188
+ /**
189
+ * Безопасная распаковка архива
190
+ */
191
+ async extractArchive(filePath, uploadId) {
192
+ const warnings = [];
193
+ const skippedFiles = [];
194
+ let extractedSize = 0;
195
+ let fileCount = 0;
196
+ const projectPath = path.join(this.config.extractDir, uploadId);
197
+ try {
198
+ // Сначала сканируем
199
+ const scanResult = await this.scanArchive(filePath);
200
+ if (!scanResult.safe) {
201
+ return {
202
+ success: false,
203
+ uploadId,
204
+ projectPath: '',
205
+ error: `Security scan failed: ${scanResult.threats.map(t => t.description).join('; ')}`,
206
+ warnings: scanResult.warnings,
207
+ stats: {
208
+ originalSize: 0,
209
+ extractedSize: 0,
210
+ fileCount: 0,
211
+ skippedFiles: []
212
+ }
213
+ };
214
+ }
215
+ warnings.push(...scanResult.warnings);
216
+ // Создаём директорию проекта
217
+ await mkdir(projectPath, { recursive: true });
218
+ const fileStats = await stat(filePath);
219
+ const zip = new AdmZip(filePath);
220
+ const entries = zip.getEntries();
221
+ // Безопасная распаковка по одному файлу
222
+ for (const entry of entries) {
223
+ if (entry.isDirectory)
224
+ continue;
225
+ const entryName = entry.entryName;
226
+ const ext = path.extname(entryName).toLowerCase();
227
+ // Пропускаем опасные и неизвестные файлы
228
+ if (DANGEROUS_EXTENSIONS.has(ext)) {
229
+ skippedFiles.push(`${entryName} (dangerous extension)`);
230
+ continue;
231
+ }
232
+ // Пропускаем symlinks
233
+ if (entry.header.attr === 0xA1FF0000) {
234
+ skippedFiles.push(`${entryName} (symlink)`);
235
+ continue;
236
+ }
237
+ // Проверка path traversal ещё раз
238
+ if (this.isPathTraversal(entryName)) {
239
+ skippedFiles.push(`${entryName} (path traversal)`);
240
+ continue;
241
+ }
242
+ // Нормализуем путь
243
+ const safePath = this.sanitizePath(entryName);
244
+ const fullPath = path.join(projectPath, safePath);
245
+ // Проверяем что путь всё ещё внутри projectPath
246
+ const resolvedPath = path.resolve(fullPath);
247
+ const resolvedProjectPath = path.resolve(projectPath);
248
+ if (!resolvedPath.startsWith(resolvedProjectPath)) {
249
+ skippedFiles.push(`${entryName} (escaped project directory)`);
250
+ continue;
251
+ }
252
+ // Создаём директорию
253
+ const dir = path.dirname(fullPath);
254
+ await mkdir(dir, { recursive: true });
255
+ // Извлекаем файл
256
+ const content = entry.getData();
257
+ extractedSize += content.length;
258
+ // Проверяем лимит размера на ходу
259
+ if (extractedSize > this.config.maxExtractedSize) {
260
+ // Откатываем
261
+ await rm(projectPath, { recursive: true, force: true });
262
+ return {
263
+ success: false,
264
+ uploadId,
265
+ projectPath: '',
266
+ error: `Extracted size exceeds limit of ${this.formatBytes(this.config.maxExtractedSize)}`,
267
+ warnings,
268
+ stats: {
269
+ originalSize: fileStats.size,
270
+ extractedSize,
271
+ fileCount,
272
+ skippedFiles
273
+ }
274
+ };
275
+ }
276
+ await writeFile(fullPath, content);
277
+ fileCount++;
278
+ }
279
+ Logger.success(`Extracted ${fileCount} files to ${projectPath}`);
280
+ return {
281
+ success: true,
282
+ uploadId,
283
+ projectPath,
284
+ warnings,
285
+ stats: {
286
+ originalSize: fileStats.size,
287
+ extractedSize,
288
+ fileCount,
289
+ skippedFiles
290
+ }
291
+ };
292
+ }
293
+ catch (error) {
294
+ Logger.error('Extract failed:', error);
295
+ // Очищаем при ошибке
296
+ if (existsSync(projectPath)) {
297
+ await rm(projectPath, { recursive: true, force: true });
298
+ }
299
+ return {
300
+ success: false,
301
+ uploadId,
302
+ projectPath: '',
303
+ error: error instanceof Error ? error.message : 'Unknown error',
304
+ warnings,
305
+ stats: {
306
+ originalSize: 0,
307
+ extractedSize,
308
+ fileCount,
309
+ skippedFiles
310
+ }
311
+ };
312
+ }
313
+ }
314
+ /**
315
+ * Полный процесс загрузки: сохранение + сканирование + распаковка
316
+ */
317
+ async processUpload(buffer, originalName) {
318
+ const uploadId = uuidv4();
319
+ const ext = path.extname(originalName).toLowerCase();
320
+ // Проверяем формат
321
+ if (ext !== '.zip') {
322
+ return {
323
+ success: false,
324
+ uploadId,
325
+ projectPath: '',
326
+ error: 'Only ZIP files are supported',
327
+ warnings: [],
328
+ stats: {
329
+ originalSize: buffer.length,
330
+ extractedSize: 0,
331
+ fileCount: 0,
332
+ skippedFiles: []
333
+ }
334
+ };
335
+ }
336
+ // Создаём директорию для загрузок
337
+ await mkdir(this.config.uploadsDir, { recursive: true });
338
+ await mkdir(this.config.extractDir, { recursive: true });
339
+ // Сохраняем временный файл
340
+ const tempPath = path.join(this.config.uploadsDir, `${uploadId}${ext}`);
341
+ await writeFile(tempPath, buffer);
342
+ try {
343
+ // Распаковываем
344
+ const result = await this.extractArchive(tempPath, uploadId);
345
+ // Удаляем временный архив
346
+ await rm(tempPath, { force: true });
347
+ return result;
348
+ }
349
+ catch (error) {
350
+ // Очищаем при ошибке
351
+ await rm(tempPath, { force: true });
352
+ throw error;
353
+ }
354
+ }
355
+ /**
356
+ * Удаление проекта
357
+ */
358
+ async deleteProject(uploadId) {
359
+ const projectPath = path.join(this.config.extractDir, uploadId);
360
+ if (existsSync(projectPath)) {
361
+ await rm(projectPath, { recursive: true, force: true });
362
+ Logger.info(`Deleted project: ${uploadId}`);
363
+ }
364
+ }
365
+ /**
366
+ * Проверка на path traversal
367
+ */
368
+ isPathTraversal(filePath) {
369
+ // Нормализуем слеши
370
+ const normalized = filePath.replace(/\\/g, '/');
371
+ for (const pattern of DANGEROUS_PATH_PATTERNS) {
372
+ if (pattern.test(normalized)) {
373
+ return true;
374
+ }
375
+ }
376
+ return false;
377
+ }
378
+ /**
379
+ * Очистка пути от опасных элементов
380
+ */
381
+ sanitizePath(filePath) {
382
+ // Убираем ведущие слеши и path traversal
383
+ return filePath
384
+ .replace(/\\/g, '/')
385
+ .replace(/^\/+/, '')
386
+ .replace(/\.\.\//g, '')
387
+ .replace(/\/\.\.\//g, '/')
388
+ .split('/')
389
+ .filter(part => part !== '..' && part !== '')
390
+ .join('/');
391
+ }
392
+ /**
393
+ * Форматирование размера в читаемый вид
394
+ */
395
+ formatBytes(bytes) {
396
+ if (bytes === 0)
397
+ return '0 Bytes';
398
+ const k = 1024;
399
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
400
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
401
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
402
+ }
403
+ /**
404
+ * Получить информацию о загруженных проектах
405
+ */
406
+ async listUploadedProjects() {
407
+ const projects = [];
408
+ if (!existsSync(this.config.extractDir)) {
409
+ return projects;
410
+ }
411
+ const dirs = await readdir(this.config.extractDir, { withFileTypes: true });
412
+ for (const dir of dirs) {
413
+ if (dir.isDirectory()) {
414
+ const projectPath = path.join(this.config.extractDir, dir.name);
415
+ const stats = await stat(projectPath);
416
+ const files = await this.countFiles(projectPath);
417
+ projects.push({
418
+ id: dir.name,
419
+ path: projectPath,
420
+ size: await this.getDirSize(projectPath),
421
+ files,
422
+ createdAt: stats.birthtime
423
+ });
424
+ }
425
+ }
426
+ return projects;
427
+ }
428
+ /**
429
+ * Подсчёт файлов в директории рекурсивно
430
+ */
431
+ async countFiles(dir) {
432
+ let count = 0;
433
+ const entries = await readdir(dir, { withFileTypes: true });
434
+ for (const entry of entries) {
435
+ const fullPath = path.join(dir, entry.name);
436
+ if (entry.isDirectory()) {
437
+ count += await this.countFiles(fullPath);
438
+ }
439
+ else {
440
+ count++;
441
+ }
442
+ }
443
+ return count;
444
+ }
445
+ /**
446
+ * Размер директории рекурсивно
447
+ */
448
+ async getDirSize(dir) {
449
+ let size = 0;
450
+ const entries = await readdir(dir, { withFileTypes: true });
451
+ for (const entry of entries) {
452
+ const fullPath = path.join(dir, entry.name);
453
+ if (entry.isDirectory()) {
454
+ size += await this.getDirSize(fullPath);
455
+ }
456
+ else {
457
+ const stats = await stat(fullPath);
458
+ size += stats.size;
459
+ }
460
+ }
461
+ return size;
462
+ }
463
+ }
464
+ //# sourceMappingURL=upload-service.js.map
@@ -0,0 +1,188 @@
1
+ /**
2
+ * API Types for ArchiCore Developer API
3
+ *
4
+ * Система токенизированной тарификации как у OpenAI/Anthropic
5
+ */
6
+ export interface ApiKey {
7
+ id: string;
8
+ userId: string;
9
+ name: string;
10
+ keyHash: string;
11
+ keyPrefix: string;
12
+ permissions: ApiPermission[];
13
+ rateLimit: RateLimitConfig;
14
+ createdAt: Date;
15
+ lastUsedAt: Date | null;
16
+ expiresAt: Date | null;
17
+ isActive: boolean;
18
+ }
19
+ export type ApiPermission = 'read:projects' | 'write:projects' | 'analyze:impact' | 'analyze:security' | 'analyze:full' | 'search:semantic' | 'ask:architect' | 'export:data' | 'admin:all';
20
+ export interface RateLimitConfig {
21
+ requestsPerMinute: number;
22
+ requestsPerDay: number;
23
+ tokensPerMinute: number;
24
+ tokensPerDay: number;
25
+ }
26
+ export interface TokenUsage {
27
+ id: string;
28
+ apiKeyId: string;
29
+ userId: string;
30
+ operation: ApiOperation;
31
+ inputTokens: number;
32
+ outputTokens: number;
33
+ totalTokens: number;
34
+ cost: number;
35
+ timestamp: Date;
36
+ projectId?: string;
37
+ metadata?: Record<string, unknown>;
38
+ }
39
+ export type ApiOperation = 'index' | 'analyze_impact' | 'analyze_security' | 'analyze_full' | 'search_semantic' | 'ask_architect' | 'export' | 'refactoring' | 'metrics' | 'rules_check' | 'dead_code' | 'duplication';
40
+ export interface PricingTier {
41
+ name: string;
42
+ pricePerMillionTokens: number;
43
+ includedTokens: number;
44
+ monthlyPrice: number;
45
+ }
46
+ export declare const PRICING_TIERS: Record<string, PricingTier>;
47
+ export declare const OPERATION_TOKEN_COSTS: Record<ApiOperation, {
48
+ base: number;
49
+ perFile: number;
50
+ perKb: number;
51
+ }>;
52
+ export interface BillingAccount {
53
+ id: string;
54
+ userId: string;
55
+ pricingTier: string;
56
+ balance: number;
57
+ tokensUsedThisMonth: number;
58
+ tokensIncludedThisMonth: number;
59
+ billingCycleStart: Date;
60
+ billingCycleEnd: Date;
61
+ autoRecharge: boolean;
62
+ autoRechargeThreshold: number;
63
+ autoRechargeAmount: number;
64
+ paymentMethods: PaymentMethod[];
65
+ invoices: Invoice[];
66
+ }
67
+ export interface PaymentMethod {
68
+ id: string;
69
+ type: 'card' | 'paypal' | 'bank';
70
+ last4?: string;
71
+ brand?: string;
72
+ isDefault: boolean;
73
+ expiresAt?: Date;
74
+ }
75
+ export interface Invoice {
76
+ id: string;
77
+ userId: string;
78
+ amount: number;
79
+ currency: string;
80
+ status: 'pending' | 'paid' | 'failed' | 'refunded';
81
+ createdAt: Date;
82
+ paidAt?: Date;
83
+ items: InvoiceItem[];
84
+ pdfUrl?: string;
85
+ }
86
+ export interface InvoiceItem {
87
+ description: string;
88
+ quantity: number;
89
+ unitPrice: number;
90
+ total: number;
91
+ }
92
+ export interface RateLimitState {
93
+ apiKeyId: string;
94
+ requestsThisMinute: number;
95
+ requestsToday: number;
96
+ tokensThisMinute: number;
97
+ tokensToday: number;
98
+ minuteResetAt: Date;
99
+ dayResetAt: Date;
100
+ }
101
+ export interface RateLimitResult {
102
+ allowed: boolean;
103
+ remaining: {
104
+ requestsPerMinute: number;
105
+ requestsPerDay: number;
106
+ tokensPerMinute: number;
107
+ tokensPerDay: number;
108
+ };
109
+ resetAt: {
110
+ minute: Date;
111
+ day: Date;
112
+ };
113
+ retryAfter?: number;
114
+ }
115
+ export interface UsageStats {
116
+ period: 'hour' | 'day' | 'week' | 'month';
117
+ startDate: Date;
118
+ endDate: Date;
119
+ totalRequests: number;
120
+ totalTokens: number;
121
+ totalCost: number;
122
+ byOperation: Record<ApiOperation, {
123
+ requests: number;
124
+ tokens: number;
125
+ cost: number;
126
+ }>;
127
+ byDay?: Array<{
128
+ date: string;
129
+ requests: number;
130
+ tokens: number;
131
+ cost: number;
132
+ }>;
133
+ }
134
+ export interface ApiKeyCreateRequest {
135
+ name: string;
136
+ permissions?: ApiPermission[];
137
+ expiresIn?: number;
138
+ rateLimit?: Partial<RateLimitConfig>;
139
+ }
140
+ export interface ApiKeyCreateResponse {
141
+ success: boolean;
142
+ apiKey?: {
143
+ id: string;
144
+ name: string;
145
+ key: string;
146
+ keyPrefix: string;
147
+ permissions: ApiPermission[];
148
+ expiresAt: Date | null;
149
+ };
150
+ error?: string;
151
+ }
152
+ export interface ApiKeyListResponse {
153
+ success: boolean;
154
+ keys?: Array<{
155
+ id: string;
156
+ name: string;
157
+ keyPrefix: string;
158
+ permissions: ApiPermission[];
159
+ createdAt: Date;
160
+ lastUsedAt: Date | null;
161
+ expiresAt: Date | null;
162
+ isActive: boolean;
163
+ }>;
164
+ error?: string;
165
+ }
166
+ export interface UsageResponse {
167
+ success: boolean;
168
+ usage?: {
169
+ currentPeriod: UsageStats;
170
+ billing: {
171
+ tier: string;
172
+ balance: number;
173
+ tokensUsed: number;
174
+ tokensIncluded: number;
175
+ tokensRemaining: number;
176
+ estimatedCost: number;
177
+ };
178
+ };
179
+ error?: string;
180
+ }
181
+ export interface ApiRequestContext {
182
+ apiKey: ApiKey;
183
+ userId: string;
184
+ rateLimitState: RateLimitState;
185
+ billingAccount: BillingAccount;
186
+ }
187
+ export declare const DEFAULT_RATE_LIMITS: Record<string, RateLimitConfig>;
188
+ //# sourceMappingURL=api.d.ts.map