solver-sdk 6.0.0 → 6.0.1

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.
@@ -1,614 +1,137 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.ProjectsApi = exports.ApiEndpoints = void 0;
37
- /**
38
- * Константы для API путей
39
- */
40
- var ApiEndpoints;
41
- (function (ApiEndpoints) {
42
- ApiEndpoints["PROJECTS"] = "/api/v1/projects";
43
- ApiEndpoints["PROJECT"] = "/api/v1/projects/:id";
44
- ApiEndpoints["PROJECT_INDEXING_STATUS"] = "/api/v1/projects/:id/indexing_status";
45
- ApiEndpoints["PROJECT_CANCEL_INDEXING"] = "/api/v1/projects/:id/cancel_indexing";
46
- ApiEndpoints["PROJECT_CLEAR_ERROR"] = "/api/v1/projects/:id/clear_error";
47
- })(ApiEndpoints || (exports.ApiEndpoints = ApiEndpoints = {}));
48
- /**
49
- * Замещает параметры в пути API
50
- * @param endpoint Шаблон пути API
51
- * @param params Параметры для замещения
52
- * @returns Путь API с замещенными параметрами
53
- */
54
- function formatEndpoint(endpoint, params) {
55
- let result = endpoint;
56
- for (const [key, value] of Object.entries(params)) {
57
- result = result.replace(`:${key}`, value);
58
- }
59
- return result;
60
- }
3
+ exports.ProjectsApi = void 0;
61
4
  /**
62
5
  * API для работы с проектами
63
6
  */
64
7
  class ProjectsApi {
65
- /**
66
- * Создает новый экземпляр API для работы с проектами
67
- * @param {HttpClient} httpClient HTTP клиент
68
- */
69
8
  constructor(httpClient) {
70
9
  this.httpClient = httpClient;
71
10
  }
11
+ // ===== ОСНОВНЫЕ МЕТОДЫ УПРАВЛЕНИЯ ПРОЕКТАМИ =====
72
12
  /**
73
- * Проверяет валидность идентификатора проекта
74
- * @param {string} projectId ID проекта
75
- * @throws {Error} Если ID проекта не валиден
76
- */
77
- validateProjectId(projectId) {
78
- if (projectId === undefined || projectId === null) {
79
- throw new Error('Project ID is required');
80
- }
81
- if (typeof projectId !== 'string') {
82
- throw new Error('Project ID must be a string');
83
- }
84
- if (projectId.trim() === '') {
85
- throw new Error('Project ID cannot be empty');
86
- }
87
- }
88
- /**
89
- * Получает список всех проектов с полной информацией
90
- *
91
- * @returns {Promise<Project[]>} Массив проектов с богатой информацией:
92
- * - Базовые поля: id, name, path, description
93
- * - Статус индексации: indexStatus, indexingProgress, ready
94
- * - Статистика: fileCount, languageStats
95
- * - Ошибки: indexingErrors, indexingWarnings, lastError
96
- * - Автореиндексация: needsReindexing, changeInfo, autoReindexing
97
- *
98
- * @example
99
- * ```typescript
100
- * const projects = await sdk.getAllProjects();
101
- * projects.forEach(project => {
102
- * console.log(`${project.name}: ${project.ready ? 'Ready' : 'Indexing...'}`);
103
- * if (project.ready) {
104
- * console.log(`Files: ${project.fileCount}, Languages:`, project.languageStats);
105
- * }
106
- * });
107
- * ```
108
- *
109
- * @throws {Error} Если не удалось получить проекты или сервер вернул ошибку
13
+ * Получает все проекты пользователя
110
14
  */
111
15
  async getAllProjects() {
112
- // HttpClient уже извлекает data из axios response, возвращая прямой массив Project[]
113
16
  const projects = await this.httpClient.get('/api/v1/projects');
114
17
  return projects;
115
18
  }
116
19
  /**
117
- * Псевдоним для getAllProjects() для совместимости с тестами
118
- *
119
- * @returns {Promise<Project[]>} Массив проектов пользователя
20
+ * Альтернативное имя для getAllProjects() для совместимости
120
21
  */
121
22
  async getProjects() {
122
23
  return this.getAllProjects();
123
24
  }
124
25
  /**
125
- * Находит существующий проект или создает новый
126
- * Для совместимости с интеграционными тестами
127
- *
128
- * @param {string} projectPath Путь к проекту (для совместимости)
129
- * @param {string} projectName Имя проекта
130
- * @param {string} [description] Описание проекта
131
- * @returns {Promise<Project>} Найденный или созданный проект
26
+ * Находит или создает проект с указанным путем и именем
132
27
  */
133
- async findOrCreateProject(projectPath, projectName, description) {
134
- // Сначала пытаемся найти существующий проект по имени
135
- const projects = await this.getAllProjects();
136
- const existingProject = projects.find(p => p.name === projectName);
137
- if (existingProject) {
138
- return existingProject;
28
+ async findOrCreateProject(projectPath, projectName) {
29
+ if (!projectPath?.trim() || !projectName?.trim()) {
30
+ throw new Error('Project path and name are required');
139
31
  }
140
- // Если не найден, создаем новый
141
- return this.createProject(projectName, projectPath, { description: description || `Проект из ${projectPath}` });
32
+ const response = await this.httpClient.post('/api/v1/projects/find-or-create', {
33
+ path: projectPath.replace(/\\/g, '/').replace(/\/$/, ''),
34
+ name: projectName
35
+ });
36
+ return response;
142
37
  }
143
38
  /**
144
- * Получает список готовых к работе проектов
145
- * @returns {Promise<Project[]>} Список готовых проектов
39
+ * Получает проекты, готовые к использованию
146
40
  */
147
41
  async getReadyProjects() {
148
- // HttpClient уже извлекает data из axios response, возвращая прямой массив Project[]
149
- const projects = await this.httpClient.get('/api/v1/projects/ready');
150
- return projects;
42
+ const allProjects = await this.getAllProjects();
43
+ return allProjects.filter(project => project.indexStatus === 'indexed');
151
44
  }
152
45
  /**
153
- * Получает детальную информацию о проекте по его ID
154
- *
155
- * @param {string} projectId Уникальный идентификатор проекта
156
- * @returns {Promise<Project>} Полная информация о проекте включая:
157
- * - Статус готовности (ready) - можно ли работать с проектом
158
- * - Прогресс индексации (indexingProgress) - процент завершения
159
- * - Информацию об ошибках (indexingErrors, lastError)
160
- * - Статистику файлов (fileCount, languageStats)
161
- *
162
- * @example
163
- * ```typescript
164
- * const project = await sdk.getProject("proj_123");
165
- *
166
- * if (project.ready) {
167
- * console.log(`Проект ${project.name} готов к работе`);
168
- * console.log(`Файлов: ${project.fileCount}`);
169
- * } else if (project.indexingProgress) {
170
- * console.log(`Индексация: ${project.indexingProgress}%`);
171
- * }
172
- *
173
- * if (project.indexingErrors?.length > 0) {
174
- * console.warn('Есть ошибки индексации:', project.indexingErrors);
175
- * }
176
- * ```
177
- *
178
- * @throws {Error} Если проект не найден или произошла ошибка сервера
46
+ * Получает проект по ID
179
47
  */
180
48
  async getProject(projectId) {
181
- // HttpClient уже извлекает data из axios response, возвращая прямой объект Project
49
+ this.validateProjectId(projectId);
182
50
  const project = await this.httpClient.get(`/api/v1/projects/${projectId}`);
183
51
  return project;
184
52
  }
185
53
  /**
186
- * Создает новый проект или возвращает существующий (автоматическая дедупликация)
187
- *
188
- * @param {string} name Название проекта (отображаемое имя)
189
- * @param {string} path Абсолютный путь к проекту в файловой системе
190
- * @param {any} data Дополнительные параметры:
191
- * - description?: string - Описание проекта
192
- * - любые другие поля, которые будут переданы в backend
193
- *
194
- * @returns {Promise<Project>} Созданный или найденный проект с полной информацией.
195
- * После создания проект будет в статусе "pending" и начнет индексироваться автоматически.
196
- *
197
- * @example
198
- * ```typescript
199
- * // Простое создание
200
- * const project = await sdk.createProject(
201
- * "My React App",
202
- * "/Users/developer/my-react-app"
203
- * );
204
- *
205
- * // С описанием
206
- * const project = await sdk.createProject(
207
- * "Backend API",
208
- * "/path/to/backend",
209
- * { description: "Node.js REST API with TypeScript" }
210
- * );
211
- *
212
- * console.log(`Проект создан: ${project.name}`);
213
- * console.log(`Статус: ${project.indexStatus}`);
214
- *
215
- * // Мониторинг индексации
216
- * if (project.indexStatus === 'pending') {
217
- * console.log('Индексация начнется автоматически...');
218
- * }
219
- * ```
220
- *
221
- * @throws {Error} Если путь недоступен, проект не может быть создан, или ошибка сервера
54
+ * Создает новый проект
222
55
  */
223
- async createProject(name, path, data = {}) {
224
- // 🚀 УЛУЧШЕНО: Используем новые типы для создания проекта
225
- const projectData = {
226
- name,
227
- path,
228
- description: data.description,
229
- ...data
56
+ async createProject(name, data, options) {
57
+ if (!name?.trim()) {
58
+ throw new Error('Project name is required');
59
+ }
60
+ const requestData = {
61
+ name: name.trim(),
62
+ ...data,
63
+ ...options
230
64
  };
231
- // HttpClient уже извлекает data из axios response, возвращая прямой объект Project
232
- const project = await this.httpClient.post('/api/v1/projects', projectData);
65
+ const project = await this.httpClient.post('/api/v1/projects', requestData);
233
66
  return project;
234
67
  }
68
+ /**
69
+ * Удаляет проект
70
+ */
71
+ async deleteProject(projectId) {
72
+ this.validateProjectId(projectId);
73
+ await this.httpClient.delete(`/api/v1/projects/${projectId}`);
74
+ }
75
+ // ===== ИНДЕКСАЦИЯ И СТАТУС =====
235
76
  /**
236
77
  * Получает статус проекта
237
- * @param {string} projectId ID проекта
238
- * @returns {Promise<any>} Статус проекта
239
78
  */
240
79
  async getProjectStatus(projectId) {
241
- return this.httpClient.get(`/api/v1/projects/${projectId}/status`);
80
+ this.validateProjectId(projectId);
81
+ return await this.httpClient.get(`/api/v1/projects/${projectId}/status`);
242
82
  }
243
83
  /**
244
84
  * Запускает индексацию проекта
245
- * @param {string} projectId ID проекта
246
- * @param {any} options Опции индексации
247
- * @returns {Promise<any>} Результат запуска индексации
248
85
  */
249
86
  async startIndexing(projectId, options = {}) {
250
- return this.httpClient.post(`/api/v1/projects/${projectId}/index`, options);
251
- }
252
- /**
253
- * Удаляет проект
254
- * @param projectId - ID проекта
255
- * @returns Promise<{ success: boolean }>
256
- * @throws Error если метод не поддерживается
257
- */
258
- /**
259
- * 🗑️ Удалить проект полностью (ВНИМАНИЕ: Удаляет ВСЕ данные!)
260
- *
261
- * Удаляет проект и все связанные данные из системы:
262
- * - Все чанки и векторы из Qdrant
263
- * - Все записи из PostgreSQL
264
- * - Метаданные проекта
265
- * - Сессии синхронизации
266
- *
267
- * @param projectId ID проекта для удаления
268
- * @returns Результат удаления
269
- *
270
- * @example
271
- * ```typescript
272
- * // ВНИМАНИЕ: Это действие необратимо!
273
- * const result = await sdk.projects.deleteProject(projectId);
274
- *
275
- * if (result.success) {
276
- * console.log('✅ Проект полностью удален');
277
- * }
278
- * ```
279
- */
280
- async deleteProject(projectId) {
281
87
  this.validateProjectId(projectId);
282
- try {
283
- const response = await this.httpClient.delete(`/api/v1/projects/${projectId}`);
284
- return {
285
- success: response.success || false,
286
- message: response.message || 'Project deleted successfully'
287
- };
288
- }
289
- catch (error) {
290
- console.error('Failed to delete project:', error);
291
- return {
292
- success: false,
293
- message: `Failed to delete project: ${error instanceof Error ? error.message : String(error)}`
294
- };
295
- }
88
+ const payload = options.force
89
+ ? { action: 'force_restart' }
90
+ : {};
91
+ return await this.httpClient.post(`/api/v1/projects/${projectId}/index`, payload);
296
92
  }
297
93
  /**
298
- * Получает текущий статус индексации проекта (единичный запрос)
299
- *
300
- * ⚠️ ВАЖНО: Для real-time уведомлений используйте WebSocket:
301
- * ```typescript
302
- * // Правильно (real-time):
303
- * await sdk.connectWebSocket();
304
- * sdk.projectSync.on('sync-status-update', (status) => {
305
- * console.log('Статус обновлен:', status);
306
- * });
307
- *
308
- * // Неправильно (не делайте polling):
309
- * // setInterval(() => sdk.projects.getIndexingStatus(id), 2000); // ❌
310
- * ```
311
- *
312
- * @param {string} projectId Идентификатор проекта
313
- * @returns {Promise<any>} Текущий статус индексации (snapshot)
94
+ * Получает статус индексации проекта
314
95
  */
315
96
  async getIndexingStatus(projectId) {
316
97
  this.validateProjectId(projectId);
317
- return this.httpClient.get(`/api/v1/projects/${projectId}/indexing-status`);
98
+ return await this.httpClient.get(`/api/v1/projects/${projectId}/indexing-status`);
318
99
  }
319
100
  /**
320
101
  * Отменяет индексацию проекта
321
- * @param {string} projectId Идентификатор проекта
322
- * @returns {Promise<boolean>} Результат отмены индексации
323
102
  */
324
103
  async cancelIndexing(projectId) {
325
- if (!projectId) {
326
- throw new Error('Project ID is required');
327
- }
328
- return this.httpClient.post(formatEndpoint(ApiEndpoints.PROJECT_CANCEL_INDEXING, { id: projectId }))
329
- .then(response => {
330
- return true;
331
- })
332
- .catch(error => {
333
- throw new Error(`Failed to cancel indexing: ${error.message}`);
334
- });
335
- }
336
- /**
337
- * Останавливает индексацию проекта (алиас для cancelIndexing)
338
- * @param {string} projectId Идентификатор проекта
339
- * @returns {Promise<boolean>} Результат остановки индексации
340
- */
341
- async stopIndexing(projectId) {
342
- return this.cancelIndexing(projectId);
343
- }
344
- /**
345
- * Очищает ошибку индексации проекта
346
- * @param {string} projectId Идентификатор проекта
347
- * @returns {Promise<boolean>} Результат очистки ошибки
348
- */
349
- async clearIndexingError(projectId) {
350
- this.validateProjectId(projectId);
351
- return this.httpClient.post(formatEndpoint(ApiEndpoints.PROJECT_CLEAR_ERROR, { id: projectId }))
352
- .then(() => true)
353
- .catch(error => {
354
- throw new Error(`Failed to clear indexing error: ${error.message}`);
355
- });
356
- }
357
- /**
358
- * Обновляет индекс конкретного файла в проекте
359
- * @param {string} projectId ID проекта
360
- * @param {string} filePath Путь к файлу (относительно корня проекта)
361
- * @param {Object} options Опции обновления индекса
362
- * @param {string} [options.content] Содержимое файла (если не указано, будет прочитано с диска)
363
- * @param {boolean} [options.force=false] Принудительная переиндексация, даже если файл не изменился
364
- * @param {string} [options.language] Язык файла (если не указан, будет определен автоматически)
365
- * @param {boolean} [options.updateDependencies=false] Обновлять зависимости после индексации файла
366
- * @returns {Promise<FileIndexResponse>} Информация об обновленном индексе файла
367
- */
368
- async updateFileIndex(projectId, filePath, options = {}) {
369
104
  this.validateProjectId(projectId);
370
- if (!filePath) {
371
- throw new Error('Путь к файлу не может быть пустым');
372
- }
373
- // Кодируем путь для URL, чтобы избежать проблем со специальными символами
374
- const encodedFilePath = encodeURIComponent(filePath);
375
- return this.httpClient.post('/api/v1/projects/' + projectId + '/files/' + encodedFilePath + '/index', options);
376
- }
377
- /**
378
- * Кэширует соответствие пути и ID проекта
379
- * @param path Путь к проекту
380
- * @param projectId ID проекта
381
- * @private
382
- */
383
- cacheProjectId(path, projectId) {
384
- const normalizedPath = path.replace(/\\/g, '/').replace(/\/$/, '');
385
- if (typeof localStorage !== 'undefined') {
386
- // Браузерное окружение
387
- const cachedProjects = JSON.parse(localStorage.getItem('solverSdkProjectCache') || '{}');
388
- cachedProjects[normalizedPath] = projectId;
389
- localStorage.setItem('solverSdkProjectCache', JSON.stringify(cachedProjects));
390
- }
391
- else if (typeof process !== 'undefined') {
392
- // Node.js окружение (для VS Code/Cursor)
393
- if (!global.solverSdkProjectCache) {
394
- global.solverSdkProjectCache = {};
395
- }
396
- global.solverSdkProjectCache[normalizedPath] = projectId;
397
- }
398
- else {
399
- throw new Error('Неподдерживаемая среда выполнения - нет localStorage и process');
400
- }
401
- }
402
- /**
403
- * Получает ID проекта из кэша
404
- * @param path Путь к проекту
405
- * @returns ID проекта или null, если не найдено
406
- * @private
407
- */
408
- getCachedProjectId(path) {
409
- const normalizedPath = path.replace(/\\/g, '/').replace(/\/$/, '');
410
- if (typeof localStorage !== 'undefined') {
411
- // Браузерное окружение
412
- const cachedProjects = JSON.parse(localStorage.getItem('solverSdkProjectCache') || '{}');
413
- return cachedProjects[normalizedPath] || null;
414
- }
415
- else if (typeof process !== 'undefined' && global.solverSdkProjectCache) {
416
- // Node.js окружение
417
- return global.solverSdkProjectCache[normalizedPath] || null;
418
- }
419
- return null;
420
- }
421
- /**
422
- * Получает или создает проект по пути
423
- * @param path Путь к проекту
424
- * @param name Имя проекта (опционально, если не указано - будет сгенерировано из пути)
425
- * @returns Данные проекта
426
- */
427
- async getOrCreateProject(path, name) {
428
- if (!path) {
429
- throw new Error('Путь к проекту не может быть пустым');
430
- }
431
- const normalizedPath = path.replace(/\\/g, '/').replace(/\/$/, '');
432
- // Сначала проверяем кэш
433
- const cachedProjectId = this.getCachedProjectId(normalizedPath);
434
- if (cachedProjectId) {
435
- // Если в кэше есть ID - пытаемся получить проект, при ошибке бросаем исключение
436
- const project = await this.getProject(cachedProjectId);
437
- return project;
438
- }
439
- // Вызываем API для получения/создания проекта
440
- const project = await this.httpClient.post('/api/v1/projects', {
441
- path: normalizedPath,
442
- name: name || normalizedPath.split('/').pop() || 'Unnamed Project'
443
- });
444
- // Кэшируем результат
445
- this.cacheProjectId(normalizedPath, project.id);
446
- return project;
447
- }
448
- /**
449
- * Индексирует проект по указанному пути
450
- * @param path Путь к проекту
451
- * @param options Опции индексации
452
- * @returns Результат операции индексации
453
- */
454
- async indexProjectByPath(path, options = {}) {
455
- if (!path) {
456
- throw new Error('Путь к проекту не может быть пустым');
457
- }
458
- const normalizedPath = path.replace(/\\/g, '/').replace(/\/$/, '');
459
- const projectName = options.name || normalizedPath.split('/').pop() || 'Unnamed Project';
460
- try {
461
- // ✅ ЧИСТЫЙ ПОДХОД: Используем стандартный endpoint с автоматической дедупликацией
462
- const project = await this.httpClient.post('/api/v1/projects', {
463
- path: normalizedPath,
464
- name: projectName
465
- });
466
- if (!project || !project.id) {
467
- throw new Error('Не удалось создать проект - отсутствует ID');
468
- }
469
- // ✅ СОВРЕМЕННЫЙ API: 2. Запускаем индексацию
470
- const indexingResult = await this.httpClient.post(`/api/v1/projects/${project.id}/index`, {
471
- forceFull: options.forceFull,
472
- excludePatterns: options.excludePatterns
473
- });
474
- return {
475
- success: true,
476
- status: 'indexing',
477
- projectId: project.id
478
- };
479
- }
480
- catch (error) {
481
- // Улучшаем сообщение об ошибке
482
- const errorMessage = error instanceof Error ? error.message : String(error);
483
- throw new Error(`Не удалось выполнить индексацию проекта по пути: ${errorMessage}`);
484
- }
485
- }
486
- /**
487
- * Создание и индексация проекта в одной операции
488
- * @param path Путь к проекту
489
- * @param options Опции создания и индексации
490
- * @param {string} [options.name] Имя проекта (если не указано, будет получено из пути)
491
- * @param {boolean} [options.forceFull=false] Принудительная полная индексация
492
- * @param {string} [options.indexingMode] Режим индексации: 'full', 'incremental', 'auto'
493
- * @param {string[]} [options.includePatterns] Паттерны для включения файлов
494
- * @param {string[]} [options.excludePatterns] Паттерны для исключения файлов
495
- * @returns Информация о созданном проекте и начатой индексации
496
- */
497
- async createAndIndexProject(path, options = {}) {
498
105
  try {
499
- // Преобразуем indexingMode в forceFull для совместимости с бэкендом
500
- let requestOptions = {
501
- name: options.name,
502
- excludePatterns: options.excludePatterns
503
- };
504
- if (options.indexingMode === 'full' || options.forceFull) {
505
- requestOptions.forceFull = true;
506
- }
507
- if (options.includePatterns) {
508
- requestOptions.includePatterns = options.includePatterns;
509
- }
510
- // Используем API для индексации по пути, который также создает проект если его нет
511
- const result = await this.indexProjectByPath(path, requestOptions);
512
- // Проверяем, что результат содержит нужные поля
513
- if (!result || !result.projectId) {
514
- throw new Error('Сервер вернул некорректный ответ без идентификатора проекта');
515
- }
516
- return {
517
- projectId: result.projectId,
518
- indexingStatus: result.status || 'indexing'
519
- };
520
- }
521
- catch (error) {
522
- const errorMessage = error instanceof Error ? error.message : String(error);
523
- throw new Error(`Не удалось создать и проиндексировать проект: ${errorMessage}`);
524
- }
525
- }
526
- /**
527
- * Проверяет существование проекта и создает резервную копию при необходимости
528
- * @param projectId ID проекта для проверки
529
- * @param options Опции для создания резервной копии
530
- * @returns Существующий проект или созданную резервную копию
531
- */
532
- async ensureProjectExists(projectId, options = {}) {
533
- try {
534
- this.validateProjectId(projectId);
535
- // Пытаемся получить проект по ID
536
- try {
537
- const project = await this.getProject(projectId);
538
- return project;
539
- }
540
- catch (error) {
541
- // Проверяем, нужно ли создавать резервную копию
542
- if (!options.createBackup) {
543
- // Если резервная копия не требуется, пробрасываем ошибку
544
- const errorMessage = error instanceof Error ? error.message : String(error);
545
- throw new Error(`Проект с ID ${projectId} не найден: ${errorMessage}`);
546
- }
547
- // Проверяем наличие пути для резервной копии
548
- if (!options.backupPath) {
549
- throw new Error('Для создания резервной копии необходимо указать путь (backupPath)');
550
- }
551
- const backupName = options.backupName || `Резервная копия для ${projectId}`;
552
- // Создаем новый проект как резервную копию
553
- console.log(`[ProjectsApi] Создание резервной копии проекта ${projectId} с именем "${backupName}" по пути ${options.backupPath}`);
554
- return await this.createProject(backupName, options.backupPath);
555
- }
106
+ await this.httpClient.post(`/api/v1/projects/${projectId}/cancel-indexing`);
107
+ return true;
556
108
  }
557
109
  catch (error) {
558
- const errorMessage = error instanceof Error ? error.message : String(error);
559
- throw new Error(`Ошибка при проверке/создании проекта ${projectId}: ${errorMessage}`);
110
+ console.error('Failed to cancel indexing:', error);
111
+ return false;
560
112
  }
561
113
  }
562
114
  /**
563
- * Получает или создает проект по пути с расширенными опциями
564
- * @param path Путь к проекту
565
- * @param options Дополнительные опции
566
- * @returns Найденный или созданный проект
115
+ * Очищает ошибку индексации
567
116
  */
568
- async getOrCreateProjectByPath(path, options = {}) {
569
- if (!path) {
570
- throw new Error('Путь к проекту не может быть пустым');
571
- }
572
- const normalizedPath = path.replace(/\\/g, '/').replace(/\/$/, '');
117
+ async clearIndexingError(projectId) {
118
+ this.validateProjectId(projectId);
573
119
  try {
574
- // Сначала проверяем кэш
575
- const cachedProjectId = this.getCachedProjectId(normalizedPath);
576
- if (cachedProjectId && options.preferExisting !== false) {
577
- try {
578
- const project = await this.getProject(cachedProjectId);
579
- return project;
580
- }
581
- catch (error) {
582
- // Если ошибка - продолжаем с API запросом
583
- console.warn(`[ProjectsApi] Не удалось получить проект из кэша: ${error instanceof Error ? error.message : String(error)}`);
584
- }
585
- }
586
- // Если проект не найден в кэше или не смогли его получить, создаем новый через стандартный API
587
- const project = await this.httpClient.post('/api/v1/projects', {
588
- path: normalizedPath,
589
- name: options.name || normalizedPath.split('/').pop() || 'Unnamed Project'
590
- });
591
- // Кэшируем полученный проект
592
- if (project && project.id) {
593
- this.cacheProjectId(normalizedPath, project.id);
594
- }
595
- return project;
120
+ await this.httpClient.post(`/api/v1/projects/${projectId}/clear-error`);
121
+ return true;
596
122
  }
597
123
  catch (error) {
598
- throw new Error(`Не удалось получить или создать проект: ${error.message}`);
124
+ console.error('Failed to clear indexing error:', error);
125
+ return false;
599
126
  }
600
127
  }
601
128
  // ===== DELTA CHUNKING МЕТОДЫ =====
602
129
  /**
603
130
  * Инициализация синхронизации проекта для Delta Chunking
604
- * @param projectId ID проекта
605
- * @param request Данные для инициализации синхронизации
606
- * @returns Результат инициализации с needsSync и опциональным sessionId
607
131
  */
608
132
  async sendInitialSync(projectId, request) {
609
133
  this.validateProjectId(projectId);
610
134
  const response = await this.httpClient.post(`/api/v1/projects/${projectId}/sync-init`, request);
611
- // ✅ ИСПРАВЛЕНО: Проверяем sessionId только если needsSync = true
612
135
  if (response.needsSync && !response.sessionId) {
613
136
  throw new Error('Server did not return sessionId when sync is needed');
614
137
  }
@@ -616,9 +139,6 @@ class ProjectsApi {
616
139
  }
617
140
  /**
618
141
  * Отправка батча зашифрованных чанков для Delta Chunking
619
- * @param projectId ID проекта
620
- * @param batchRequest Батч зашифрованных чанков
621
- * @returns Результат обработки батча
622
142
  */
623
143
  async sendDeltaSync(projectId, batchRequest) {
624
144
  this.validateProjectId(projectId);
@@ -627,8 +147,6 @@ class ProjectsApi {
627
147
  }
628
148
  /**
629
149
  * Получение статуса синхронизации Delta Chunking
630
- * @param projectId ID проекта
631
- * @returns Текущий статус синхронизации
632
150
  */
633
151
  async getDeltaSyncStatus(projectId) {
634
152
  this.validateProjectId(projectId);
@@ -649,13 +167,10 @@ class ProjectsApi {
649
167
  }
650
168
  /**
651
169
  * Завершение синхронизации Delta Chunking
652
- * @param projectId ID проекта
653
- * @returns Результат завершения синхронизации
654
170
  */
655
171
  async finalizeDeltaSync(projectId) {
656
172
  this.validateProjectId(projectId);
657
173
  const response = await this.httpClient.post(`/api/v1/projects/${projectId}/sync-finalize`, {});
658
- // HttpClient уже извлекает data из axios response, response - это прямой объект от backend
659
174
  return {
660
175
  success: response.success || false,
661
176
  projectId,
@@ -669,88 +184,39 @@ class ProjectsApi {
669
184
  }
670
185
  /**
671
186
  * Отмена синхронизации Delta Chunking
672
- * @param projectId ID проекта
673
- * @returns Результат отмены
674
187
  */
675
188
  async cancelDeltaSync(projectId) {
676
189
  this.validateProjectId(projectId);
677
190
  const response = await this.httpClient.delete(`/api/v1/projects/${projectId}/sync-cancel`);
678
- // HttpClient уже извлекает data из axios response, response - это прямой объект от backend
679
191
  return {
680
- success: response.success || false,
192
+ success: response.success !== false,
681
193
  message: response.message
682
194
  };
683
195
  }
196
+ // ===== PROJECT STATE И SMART SYNC =====
684
197
  /**
685
- * 🔍 Получить состояние проекта без инициализации синхронизации
686
- *
687
- * Проверяет текущее состояние проекта на сервере. Если передан clientRootHash,
688
- * дополнительно проверяет нужна ли синхронизация.
689
- *
690
- * @param projectId ID проекта
691
- * @param clientRootHash (опционально) Merkle root hash клиента для проверки sync
692
- * @returns Состояние проекта
693
- *
694
- * @example
695
- * ```typescript
696
- * // Проверка только состояния проекта
697
- * const state = await sdk.projects.getProjectState(projectId);
698
- * console.log(`Проект "${state.projectName}" содержит ${state.totalChunks} чанков`);
699
- *
700
- * // Проверка нужна ли синхронизация
701
- * const clientHash = await computeMerkleRootHash(workspacePath);
702
- * const stateWithSync = await sdk.projects.getProjectState(projectId, clientHash);
703
- *
704
- * if (stateWithSync.syncRequired) {
705
- * console.log("Требуется синхронизация");
706
- * await sdk.projects.initializeDeltaSync(projectId, clientHash);
707
- * } else {
708
- * console.log("Проект актуален, синхронизация не нужна");
709
- * }
710
- * ```
198
+ * Получение состояния проекта с проверкой синхронизации
711
199
  */
712
200
  async getProjectState(projectId, clientRootHash) {
713
201
  this.validateProjectId(projectId);
714
- const params = clientRootHash ? { clientRootHash } : {};
715
- const response = await this.httpClient.get(`/api/v1/projects/${projectId}/state`, params);
716
- // Конвертируем даты из строк в Date объекты
202
+ const url = clientRootHash
203
+ ? `/api/v1/projects/${projectId}/state?clientRootHash=${encodeURIComponent(clientRootHash)}`
204
+ : `/api/v1/projects/${projectId}/state`;
205
+ const response = await this.httpClient.get(url);
717
206
  return {
718
- projectId: response.projectId,
719
- projectName: response.projectName,
720
- merkleRootHash: response.merkleRootHash,
207
+ projectId: response.projectId || projectId,
208
+ projectName: response.projectName || 'Unknown',
209
+ merkleRootHash: response.merkleRootHash || null,
721
210
  lastIndexedAt: response.lastIndexedAt ? new Date(response.lastIndexedAt) : null,
722
211
  totalChunks: response.totalChunks || 0,
723
- indexingStatus: response.indexingStatus,
212
+ indexingStatus: response.indexingStatus || 'unknown',
724
213
  syncRequired: response.syncRequired,
725
214
  lastSyncSessionId: response.lastSyncSessionId
726
215
  };
727
216
  }
217
+ // ===== SESSION RECOVERY =====
728
218
  /**
729
- * 🔄 Проверить есть ли прерванные сессии синхронизации для проекта
730
- *
731
- * Используется при открытии проекта для показа пользователю опции
732
- * "Continue indexing" если есть незавершенная синхронизация.
733
- *
734
- * @param projectId ID проекта
735
- * @returns Статус восстановления с информацией о прерванной сессии
736
- *
737
- * @example
738
- * ```typescript
739
- * // Проверка при открытии проекта
740
- * const recoveryStatus = await sdk.projects.getRecoveryStatus(projectId);
741
- *
742
- * if (recoveryStatus.hasInterruptedSession) {
743
- * // Показать UI: "Continue indexing" button
744
- * const info = recoveryStatus.recoveryInfo!;
745
- * console.log(`Прерванная синхронизация: ${info.progress.percentage}% завершено`);
746
- *
747
- * // Пользователь нажал "Continue"
748
- * const resumed = await sdk.projects.resumeSync(projectId);
749
- * } else {
750
- * // Обычная синхронизация
751
- * await sdk.projects.initializeDeltaSync(projectId, merkleHash);
752
- * }
753
- * ```
219
+ * Получение статуса восстановления сессии
754
220
  */
755
221
  async getRecoveryStatus(projectId) {
756
222
  this.validateProjectId(projectId);
@@ -759,229 +225,118 @@ class ProjectsApi {
759
225
  hasInterruptedSession: response.hasInterruptedSession || false,
760
226
  recoveryInfo: response.recoveryInfo ? {
761
227
  sessionId: response.recoveryInfo.sessionId,
762
- projectId: response.recoveryInfo.projectId,
763
- projectName: response.recoveryInfo.projectName,
764
- status: response.recoveryInfo.status,
765
- progress: response.recoveryInfo.progress,
766
- interruptedAt: response.recoveryInfo.interruptedAt ?
767
- new Date(response.recoveryInfo.interruptedAt) : null,
768
- canResume: response.recoveryInfo.canResume,
769
- resumeReason: response.recoveryInfo.resumeReason,
770
- estimatedTimeRemaining: response.recoveryInfo.estimatedTimeRemaining,
771
- } : undefined,
228
+ projectId: response.recoveryInfo.projectId || projectId,
229
+ projectName: response.recoveryInfo.projectName || 'Unknown',
230
+ status: response.recoveryInfo.status || 'interrupted',
231
+ progress: {
232
+ received: response.recoveryInfo.progress?.totalChunks || 0,
233
+ processed: response.recoveryInfo.progress?.processedChunks || 0,
234
+ total: response.recoveryInfo.progress?.totalChunks || 0,
235
+ percentage: response.recoveryInfo.progress?.percentage || 0
236
+ },
237
+ interruptedAt: response.recoveryInfo.lastActivity ? new Date(response.recoveryInfo.lastActivity) : new Date(),
238
+ resumeReason: response.recoveryInfo.resumeReason || 'session_recovery',
239
+ canResume: response.recoveryInfo.canResume !== false
240
+ } : undefined
772
241
  };
773
242
  }
774
243
  /**
775
- * 🔄 Возобновить прерванную сессию синхронизации
776
- *
777
- * Подготавливает прерванную сессию к возобновлению. После успешного
778
- * вызова клиент может продолжить отправку чанков через обычный endpoint.
779
- *
780
- * @param projectId ID проекта
781
- * @param options Опции возобновления (форсировать, пропустить дубликаты, etc.)
782
- * @returns Результат возобновления с информацией для продолжения
783
- *
784
- * @example
785
- * ```typescript
786
- * // Возобновить с настройками по умолчанию
787
- * const resumed = await sdk.projects.resumeSync(projectId);
788
- *
789
- * if (resumed.success) {
790
- * console.log(`Сессия ${resumed.sessionId} готова к возобновлению`);
791
- *
792
- * // Продолжить отправку чанков с определенного батча
793
- * if (resumed.continueFromBatch) {
794
- * console.log(`Начать с батча ${resumed.continueFromBatch}`);
795
- * }
796
- *
797
- * // Использовать обычный endpoint для отправки
798
- * await sdk.deltaChunking.sendBatch(projectId, chunks);
799
- * }
800
- *
801
- * // Возобновить с принудительными настройками
802
- * const forced = await sdk.projects.resumeSync(projectId, {
803
- * forceResume: true,
804
- * skipDuplicates: true,
805
- * continueFromLastBatch: true
806
- * });
807
- * ```
244
+ * Восстановление прерванной синхронизации
808
245
  */
809
246
  async resumeSync(projectId, options = {}) {
810
247
  this.validateProjectId(projectId);
811
- const response = await this.httpClient.post(`/api/v1/projects/${projectId}/resume-sync`, options);
248
+ const response = await this.httpClient.post(`/api/v1/projects/${projectId}/resume-sync`, {
249
+ skipDuplicates: options.skipDuplicates !== false,
250
+ continueFromLastBatch: options.continueFromLastBatch !== false
251
+ });
812
252
  return {
813
- success: response.success || false,
814
- message: response.message || '',
815
- sessionId: response.sessionId,
253
+ success: response.success !== false,
254
+ message: response.message,
816
255
  continueFromBatch: response.continueFromBatch,
817
- resumeEndpoint: response.resumeEndpoint,
256
+ sessionId: response.sessionId
818
257
  };
819
258
  }
820
259
  /**
821
- * 🗑️ Отменить прерванную сессию восстановления
822
- *
823
- * Отменяет прерванную сессию если пользователь решил не восстанавливать
824
- * синхронизацию. После этого будет выполнена обычная полная синхронизация.
825
- *
826
- * @param projectId ID проекта
827
- * @returns Результат отмены
828
- *
829
- * @example
830
- * ```typescript
831
- * // Пользователь нажал "Start fresh" вместо "Continue"
832
- * const cancelled = await sdk.projects.cancelRecovery(projectId);
833
- *
834
- * if (cancelled.success) {
835
- * console.log('Прерванная сессия отменена, начинаем полную синхронизацию');
836
- * await sdk.projects.initializeDeltaSync(projectId, newMerkleHash);
837
- * }
838
- * ```
260
+ * Отмена восстановления сессии
839
261
  */
840
262
  async cancelRecovery(projectId) {
841
263
  this.validateProjectId(projectId);
842
- const response = await this.httpClient.delete(`/api/v1/projects/${projectId}/cancel-recovery`);
264
+ const response = await this.httpClient.delete(`/api/v1/projects/${projectId}/recovery`);
843
265
  return {
844
- success: response.success || false,
845
- message: response.message || '',
266
+ success: response.success !== false,
267
+ message: response.message
846
268
  };
847
269
  }
848
270
  /**
849
- * 🚀 Инициализировать Delta-Chunking синхронизацию
850
- *
851
- * Начинает новую сессию синхронизации с использованием delta-chunking.
852
- * Алиас для удобства - делегирует к DeltaChunkingManager.
853
- *
854
- * @param projectId ID проекта
855
- * @param clientRootHash Merkle root hash клиента
856
- * @returns Результат инициализации
857
- *
858
- * @example
859
- * ```typescript
860
- * const merkleHash = await computeMerkleRootHash(workspacePath);
861
- * const result = await sdk.projects.initializeDeltaSync(projectId, merkleHash);
862
- *
863
- * if (result.needsSync) {
864
- * console.log(`Started sync session: ${result.sessionId}`);
865
- * // Продолжить отправку чанков...
866
- * } else {
867
- * console.log('Project is up to date');
868
- * }
869
- * ```
271
+ * Инициализация Delta Sync
870
272
  */
871
273
  async initializeDeltaSync(projectId, clientRootHash) {
872
274
  this.validateProjectId(projectId);
873
- // Делегируем к DeltaChunkingApi
874
- const deltaApi = new (await Promise.resolve().then(() => __importStar(require('./delta-chunking-api.js')))).DeltaChunkingApi(this.httpClient);
875
- return await deltaApi.initSync(projectId, { clientRootHash });
275
+ if (!clientRootHash?.trim()) {
276
+ throw new Error('Client root hash is required');
277
+ }
278
+ const response = await this.httpClient.post(`/api/v1/projects/${projectId}/initialize-delta-sync`, {
279
+ clientRootHash: clientRootHash.trim()
280
+ });
281
+ return {
282
+ needsSync: response.needsSync !== false,
283
+ sessionId: response.sessionId,
284
+ lastSyncHash: response.lastSyncHash
285
+ };
876
286
  }
877
287
  /**
878
- * 🔄 Сбросить индексацию проекта (очистить данные, сохранить проект)
879
- *
880
- * Очищает все проиндексированные данные, но сохраняет сам проект:
881
- * - Удаляет все векторы из Qdrant
882
- * - Удаляет все chunks из PostgreSQL
883
- * - Удаляет сессии синхронизации
884
- * - Сбрасывает статус индексации
885
- *
886
- * @param projectId ID проекта
887
- * @returns Результат сброса с статистикой
888
- *
889
- * @example
890
- * ```typescript
891
- * // Сброс индексации (проект остается)
892
- * const result = await sdk.projects.resetIndexing(projectId);
893
- *
894
- * console.log(`Cleared: ${result.clearedData.vectorsDeleted} vectors, ${result.clearedData.chunksDeleted} chunks`);
895
- *
896
- * // После сброса можно начать новую индексацию
897
- * const merkleHash = await computeMerkleRootHash(workspacePath);
898
- * await sdk.projects.initializeDeltaSync(projectId, merkleHash);
899
- * ```
288
+ * Сброс индексации проекта
900
289
  */
901
290
  async resetIndexing(projectId) {
902
291
  this.validateProjectId(projectId);
903
292
  const response = await this.httpClient.post(`/api/v1/projects/${projectId}/reset-indexing`);
904
293
  return {
905
- success: response.success || false,
906
- message: response.message || '',
907
- clearedData: response.clearedData || {
908
- vectorsDeleted: 0,
909
- chunksDeleted: 0,
910
- sessionsDeleted: 0
911
- }
294
+ success: response.success !== false,
295
+ message: response.message || 'Reset completed',
296
+ clearedChunks: response.clearedChunks,
297
+ clearedFiles: response.clearedFiles
912
298
  };
913
299
  }
914
300
  /**
915
- * 🔄 Перезапустить индексацию проекта (сброс + подготовка к новой)
916
- *
917
- * Выполняет полный перезапуск индексации:
918
- * 1. Очищает все существующие данные
919
- * 2. Сбрасывает статус проекта
920
- * 3. Подготавливает к новой синхронизации
921
- *
922
- * @param projectId ID проекта
923
- * @returns Результат перезапуска
924
- *
925
- * @example
926
- * ```typescript
927
- * // Перезапуск индексации
928
- * const result = await sdk.projects.restartIndexing(projectId);
929
- *
930
- * if (result.readyForNewSync) {
931
- * console.log('Project ready for new indexing');
932
- *
933
- * // Начать новую синхронизацию
934
- * const merkleHash = await computeMerkleRootHash(workspacePath);
935
- * await sdk.projects.initializeDeltaSync(projectId, merkleHash);
936
- * }
937
- * ```
301
+ * Перезапуск индексации проекта
938
302
  */
939
303
  async restartIndexing(projectId) {
940
304
  this.validateProjectId(projectId);
941
305
  const response = await this.httpClient.post(`/api/v1/projects/${projectId}/restart-indexing`);
942
306
  return {
943
- success: response.success || false,
944
- message: response.message || '',
945
- resetData: response.resetData || {
946
- vectorsDeleted: 0,
947
- chunksDeleted: 0,
948
- sessionsDeleted: 0
949
- },
950
- readyForNewSync: response.readyForNewSync || false
307
+ success: response.success !== false,
308
+ message: response.message || 'Restart initiated',
309
+ newSessionId: response.newSessionId
951
310
  };
952
311
  }
953
312
  /**
954
- * 🔍 Получить соответствие обфусцированных путей файлов
955
- *
956
- * Возвращает список obfuscatedPath для всех файлов проекта.
957
- * Используется для точной очистки удаленных файлов.
958
- *
959
- * @param projectId ID проекта
960
- * @returns Mapping обфусцированных путей
961
- *
962
- * @example
963
- * ```typescript
964
- * const mapping = await sdk.projects.getFilePathMapping(projectId);
965
- * console.log(`Найдено ${mapping.files.length} файлов в проекте`);
966
- *
967
- * mapping.files.forEach(file => {
968
- * console.log(`${file.obfuscatedPath}: ${file.chunkCount} чанков`);
969
- * });
970
- * ```
313
+ * Получение маппинга файловых путей
971
314
  */
972
315
  async getFilePathMapping(projectId) {
973
316
  this.validateProjectId(projectId);
974
317
  const response = await this.httpClient.get(`/api/v1/projects/${projectId}/file-path-mapping`);
975
- // Конвертируем даты из строк в Date объекты
976
318
  return {
977
- success: response.success,
978
- files: response.files.map((file) => ({
979
- obfuscatedPath: file.obfuscatedPath,
980
- chunkCount: file.chunkCount,
981
- lastUpdated: new Date(file.lastUpdated)
982
- }))
319
+ success: response.success !== false,
320
+ mapping: response.mapping || [],
321
+ totalFiles: response.totalFiles || 0
983
322
  };
984
323
  }
324
+ // ===== УТИЛИТЫ =====
325
+ /**
326
+ * Валидация ID проекта
327
+ * @private
328
+ */
329
+ validateProjectId(projectId) {
330
+ if (projectId === undefined || projectId === null) {
331
+ throw new Error('Project ID is required');
332
+ }
333
+ if (typeof projectId !== 'string') {
334
+ throw new Error('Project ID must be a string');
335
+ }
336
+ if (projectId.trim().length === 0) {
337
+ throw new Error('Project ID cannot be empty');
338
+ }
339
+ }
985
340
  }
986
341
  exports.ProjectsApi = ProjectsApi;
987
342
  //# sourceMappingURL=projects-api.js.map