solver-sdk 6.0.0 → 6.0.2
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.
- package/README.md +35 -1034
- package/dist/cjs/api/indexing-api.js +38 -156
- package/dist/cjs/api/indexing-api.js.map +1 -1
- package/dist/cjs/api/projects-api.js +133 -779
- package/dist/cjs/api/projects-api.js.map +1 -1
- package/dist/cjs/code-solver-sdk.js +1 -9
- package/dist/cjs/code-solver-sdk.js.map +1 -1
- package/dist/esm/api/indexing-api.js +36 -154
- package/dist/esm/api/indexing-api.js.map +1 -1
- package/dist/esm/api/projects-api.js +132 -745
- package/dist/esm/api/projects-api.js.map +1 -1
- package/dist/esm/code-solver-sdk.js +1 -9
- package/dist/esm/code-solver-sdk.js.map +1 -1
- package/dist/types/api/indexing-api.d.ts +20 -129
- package/dist/types/api/indexing-api.d.ts.map +1 -1
- package/dist/types/api/projects-api.d.ts +36 -497
- package/dist/types/api/projects-api.d.ts.map +1 -1
- package/dist/types/code-solver-sdk.d.ts +0 -7
- package/dist/types/code-solver-sdk.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,614 +1,136 @@
|
|
|
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 =
|
|
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
|
-
*
|
|
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(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const existingProject = projects.find(p => p.name === projectName);
|
|
137
|
-
if (existingProject) {
|
|
138
|
-
return existingProject;
|
|
28
|
+
async findOrCreateProject(projectName) {
|
|
29
|
+
if (!projectName?.trim()) {
|
|
30
|
+
throw new Error('Project name is required');
|
|
139
31
|
}
|
|
140
|
-
|
|
141
|
-
|
|
32
|
+
const response = await this.httpClient.post('/api/v1/projects/find-or-create', {
|
|
33
|
+
name: projectName.trim()
|
|
34
|
+
});
|
|
35
|
+
return response;
|
|
142
36
|
}
|
|
143
37
|
/**
|
|
144
|
-
* Получает
|
|
145
|
-
* @returns {Promise<Project[]>} Список готовых проектов
|
|
38
|
+
* Получает проекты, готовые к использованию
|
|
146
39
|
*/
|
|
147
40
|
async getReadyProjects() {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return projects;
|
|
41
|
+
const allProjects = await this.getAllProjects();
|
|
42
|
+
return allProjects.filter(project => project.indexStatus === 'indexed');
|
|
151
43
|
}
|
|
152
44
|
/**
|
|
153
|
-
* Получает
|
|
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} Если проект не найден или произошла ошибка сервера
|
|
45
|
+
* Получает проект по ID
|
|
179
46
|
*/
|
|
180
47
|
async getProject(projectId) {
|
|
181
|
-
|
|
48
|
+
this.validateProjectId(projectId);
|
|
182
49
|
const project = await this.httpClient.get(`/api/v1/projects/${projectId}`);
|
|
183
50
|
return project;
|
|
184
51
|
}
|
|
185
52
|
/**
|
|
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} Если путь недоступен, проект не может быть создан, или ошибка сервера
|
|
53
|
+
* Создает новый проект
|
|
222
54
|
*/
|
|
223
|
-
async createProject(name,
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
...data
|
|
55
|
+
async createProject(name, data, options) {
|
|
56
|
+
if (!name?.trim()) {
|
|
57
|
+
throw new Error('Project name is required');
|
|
58
|
+
}
|
|
59
|
+
const requestData = {
|
|
60
|
+
name: name.trim(),
|
|
61
|
+
...data,
|
|
62
|
+
...options
|
|
230
63
|
};
|
|
231
|
-
|
|
232
|
-
const project = await this.httpClient.post('/api/v1/projects', projectData);
|
|
64
|
+
const project = await this.httpClient.post('/api/v1/projects', requestData);
|
|
233
65
|
return project;
|
|
234
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Удаляет проект
|
|
69
|
+
*/
|
|
70
|
+
async deleteProject(projectId) {
|
|
71
|
+
this.validateProjectId(projectId);
|
|
72
|
+
await this.httpClient.delete(`/api/v1/projects/${projectId}`);
|
|
73
|
+
}
|
|
74
|
+
// ===== ИНДЕКСАЦИЯ И СТАТУС =====
|
|
235
75
|
/**
|
|
236
76
|
* Получает статус проекта
|
|
237
|
-
* @param {string} projectId ID проекта
|
|
238
|
-
* @returns {Promise<any>} Статус проекта
|
|
239
77
|
*/
|
|
240
78
|
async getProjectStatus(projectId) {
|
|
241
|
-
|
|
79
|
+
this.validateProjectId(projectId);
|
|
80
|
+
return await this.httpClient.get(`/api/v1/projects/${projectId}/status`);
|
|
242
81
|
}
|
|
243
82
|
/**
|
|
244
83
|
* Запускает индексацию проекта
|
|
245
|
-
* @param {string} projectId ID проекта
|
|
246
|
-
* @param {any} options Опции индексации
|
|
247
|
-
* @returns {Promise<any>} Результат запуска индексации
|
|
248
84
|
*/
|
|
249
85
|
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
86
|
this.validateProjectId(projectId);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
}
|
|
87
|
+
const payload = options.force
|
|
88
|
+
? { action: 'force_restart' }
|
|
89
|
+
: {};
|
|
90
|
+
return await this.httpClient.post(`/api/v1/projects/${projectId}/index`, payload);
|
|
296
91
|
}
|
|
297
92
|
/**
|
|
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)
|
|
93
|
+
* Получает статус индексации проекта
|
|
314
94
|
*/
|
|
315
95
|
async getIndexingStatus(projectId) {
|
|
316
96
|
this.validateProjectId(projectId);
|
|
317
|
-
return this.httpClient.get(`/api/v1/projects/${projectId}/indexing-status`);
|
|
97
|
+
return await this.httpClient.get(`/api/v1/projects/${projectId}/indexing-status`);
|
|
318
98
|
}
|
|
319
99
|
/**
|
|
320
100
|
* Отменяет индексацию проекта
|
|
321
|
-
* @param {string} projectId Идентификатор проекта
|
|
322
|
-
* @returns {Promise<boolean>} Результат отмены индексации
|
|
323
101
|
*/
|
|
324
102
|
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
103
|
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
104
|
try {
|
|
499
|
-
|
|
500
|
-
|
|
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
|
-
}
|
|
105
|
+
await this.httpClient.post(`/api/v1/projects/${projectId}/cancel-indexing`);
|
|
106
|
+
return true;
|
|
556
107
|
}
|
|
557
108
|
catch (error) {
|
|
558
|
-
|
|
559
|
-
|
|
109
|
+
console.error('Failed to cancel indexing:', error);
|
|
110
|
+
return false;
|
|
560
111
|
}
|
|
561
112
|
}
|
|
562
113
|
/**
|
|
563
|
-
*
|
|
564
|
-
* @param path Путь к проекту
|
|
565
|
-
* @param options Дополнительные опции
|
|
566
|
-
* @returns Найденный или созданный проект
|
|
114
|
+
* Очищает ошибку индексации
|
|
567
115
|
*/
|
|
568
|
-
async
|
|
569
|
-
|
|
570
|
-
throw new Error('Путь к проекту не может быть пустым');
|
|
571
|
-
}
|
|
572
|
-
const normalizedPath = path.replace(/\\/g, '/').replace(/\/$/, '');
|
|
116
|
+
async clearIndexingError(projectId) {
|
|
117
|
+
this.validateProjectId(projectId);
|
|
573
118
|
try {
|
|
574
|
-
|
|
575
|
-
|
|
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;
|
|
119
|
+
await this.httpClient.post(`/api/v1/projects/${projectId}/clear-error`);
|
|
120
|
+
return true;
|
|
596
121
|
}
|
|
597
122
|
catch (error) {
|
|
598
|
-
|
|
123
|
+
console.error('Failed to clear indexing error:', error);
|
|
124
|
+
return false;
|
|
599
125
|
}
|
|
600
126
|
}
|
|
601
127
|
// ===== DELTA CHUNKING МЕТОДЫ =====
|
|
602
128
|
/**
|
|
603
129
|
* Инициализация синхронизации проекта для Delta Chunking
|
|
604
|
-
* @param projectId ID проекта
|
|
605
|
-
* @param request Данные для инициализации синхронизации
|
|
606
|
-
* @returns Результат инициализации с needsSync и опциональным sessionId
|
|
607
130
|
*/
|
|
608
131
|
async sendInitialSync(projectId, request) {
|
|
609
132
|
this.validateProjectId(projectId);
|
|
610
133
|
const response = await this.httpClient.post(`/api/v1/projects/${projectId}/sync-init`, request);
|
|
611
|
-
// ✅ ИСПРАВЛЕНО: Проверяем sessionId только если needsSync = true
|
|
612
134
|
if (response.needsSync && !response.sessionId) {
|
|
613
135
|
throw new Error('Server did not return sessionId when sync is needed');
|
|
614
136
|
}
|
|
@@ -616,9 +138,6 @@ class ProjectsApi {
|
|
|
616
138
|
}
|
|
617
139
|
/**
|
|
618
140
|
* Отправка батча зашифрованных чанков для Delta Chunking
|
|
619
|
-
* @param projectId ID проекта
|
|
620
|
-
* @param batchRequest Батч зашифрованных чанков
|
|
621
|
-
* @returns Результат обработки батча
|
|
622
141
|
*/
|
|
623
142
|
async sendDeltaSync(projectId, batchRequest) {
|
|
624
143
|
this.validateProjectId(projectId);
|
|
@@ -627,8 +146,6 @@ class ProjectsApi {
|
|
|
627
146
|
}
|
|
628
147
|
/**
|
|
629
148
|
* Получение статуса синхронизации Delta Chunking
|
|
630
|
-
* @param projectId ID проекта
|
|
631
|
-
* @returns Текущий статус синхронизации
|
|
632
149
|
*/
|
|
633
150
|
async getDeltaSyncStatus(projectId) {
|
|
634
151
|
this.validateProjectId(projectId);
|
|
@@ -649,13 +166,10 @@ class ProjectsApi {
|
|
|
649
166
|
}
|
|
650
167
|
/**
|
|
651
168
|
* Завершение синхронизации Delta Chunking
|
|
652
|
-
* @param projectId ID проекта
|
|
653
|
-
* @returns Результат завершения синхронизации
|
|
654
169
|
*/
|
|
655
170
|
async finalizeDeltaSync(projectId) {
|
|
656
171
|
this.validateProjectId(projectId);
|
|
657
172
|
const response = await this.httpClient.post(`/api/v1/projects/${projectId}/sync-finalize`, {});
|
|
658
|
-
// HttpClient уже извлекает data из axios response, response - это прямой объект от backend
|
|
659
173
|
return {
|
|
660
174
|
success: response.success || false,
|
|
661
175
|
projectId,
|
|
@@ -669,88 +183,39 @@ class ProjectsApi {
|
|
|
669
183
|
}
|
|
670
184
|
/**
|
|
671
185
|
* Отмена синхронизации Delta Chunking
|
|
672
|
-
* @param projectId ID проекта
|
|
673
|
-
* @returns Результат отмены
|
|
674
186
|
*/
|
|
675
187
|
async cancelDeltaSync(projectId) {
|
|
676
188
|
this.validateProjectId(projectId);
|
|
677
189
|
const response = await this.httpClient.delete(`/api/v1/projects/${projectId}/sync-cancel`);
|
|
678
|
-
// HttpClient уже извлекает data из axios response, response - это прямой объект от backend
|
|
679
190
|
return {
|
|
680
|
-
success: response.success
|
|
191
|
+
success: response.success !== false,
|
|
681
192
|
message: response.message
|
|
682
193
|
};
|
|
683
194
|
}
|
|
195
|
+
// ===== PROJECT STATE И SMART SYNC =====
|
|
684
196
|
/**
|
|
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
|
-
* ```
|
|
197
|
+
* Получение состояния проекта с проверкой синхронизации
|
|
711
198
|
*/
|
|
712
199
|
async getProjectState(projectId, clientRootHash) {
|
|
713
200
|
this.validateProjectId(projectId);
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
201
|
+
const url = clientRootHash
|
|
202
|
+
? `/api/v1/projects/${projectId}/state?clientRootHash=${encodeURIComponent(clientRootHash)}`
|
|
203
|
+
: `/api/v1/projects/${projectId}/state`;
|
|
204
|
+
const response = await this.httpClient.get(url);
|
|
717
205
|
return {
|
|
718
|
-
projectId: response.projectId,
|
|
719
|
-
projectName: response.projectName,
|
|
720
|
-
merkleRootHash: response.merkleRootHash,
|
|
206
|
+
projectId: response.projectId || projectId,
|
|
207
|
+
projectName: response.projectName || 'Unknown',
|
|
208
|
+
merkleRootHash: response.merkleRootHash || null,
|
|
721
209
|
lastIndexedAt: response.lastIndexedAt ? new Date(response.lastIndexedAt) : null,
|
|
722
210
|
totalChunks: response.totalChunks || 0,
|
|
723
|
-
indexingStatus: response.indexingStatus,
|
|
211
|
+
indexingStatus: response.indexingStatus || 'unknown',
|
|
724
212
|
syncRequired: response.syncRequired,
|
|
725
213
|
lastSyncSessionId: response.lastSyncSessionId
|
|
726
214
|
};
|
|
727
215
|
}
|
|
216
|
+
// ===== SESSION RECOVERY =====
|
|
728
217
|
/**
|
|
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
|
-
* ```
|
|
218
|
+
* Получение статуса восстановления сессии
|
|
754
219
|
*/
|
|
755
220
|
async getRecoveryStatus(projectId) {
|
|
756
221
|
this.validateProjectId(projectId);
|
|
@@ -759,229 +224,118 @@ class ProjectsApi {
|
|
|
759
224
|
hasInterruptedSession: response.hasInterruptedSession || false,
|
|
760
225
|
recoveryInfo: response.recoveryInfo ? {
|
|
761
226
|
sessionId: response.recoveryInfo.sessionId,
|
|
762
|
-
projectId: response.recoveryInfo.projectId,
|
|
763
|
-
projectName: response.recoveryInfo.projectName,
|
|
764
|
-
status: response.recoveryInfo.status,
|
|
765
|
-
progress:
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
227
|
+
projectId: response.recoveryInfo.projectId || projectId,
|
|
228
|
+
projectName: response.recoveryInfo.projectName || 'Unknown',
|
|
229
|
+
status: response.recoveryInfo.status || 'interrupted',
|
|
230
|
+
progress: {
|
|
231
|
+
received: response.recoveryInfo.progress?.totalChunks || 0,
|
|
232
|
+
processed: response.recoveryInfo.progress?.processedChunks || 0,
|
|
233
|
+
total: response.recoveryInfo.progress?.totalChunks || 0,
|
|
234
|
+
percentage: response.recoveryInfo.progress?.percentage || 0
|
|
235
|
+
},
|
|
236
|
+
interruptedAt: response.recoveryInfo.lastActivity ? new Date(response.recoveryInfo.lastActivity) : new Date(),
|
|
237
|
+
resumeReason: response.recoveryInfo.resumeReason || 'session_recovery',
|
|
238
|
+
canResume: response.recoveryInfo.canResume !== false
|
|
239
|
+
} : undefined
|
|
772
240
|
};
|
|
773
241
|
}
|
|
774
242
|
/**
|
|
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
|
-
* ```
|
|
243
|
+
* Восстановление прерванной синхронизации
|
|
808
244
|
*/
|
|
809
245
|
async resumeSync(projectId, options = {}) {
|
|
810
246
|
this.validateProjectId(projectId);
|
|
811
|
-
const response = await this.httpClient.post(`/api/v1/projects/${projectId}/resume-sync`,
|
|
247
|
+
const response = await this.httpClient.post(`/api/v1/projects/${projectId}/resume-sync`, {
|
|
248
|
+
skipDuplicates: options.skipDuplicates !== false,
|
|
249
|
+
continueFromLastBatch: options.continueFromLastBatch !== false
|
|
250
|
+
});
|
|
812
251
|
return {
|
|
813
|
-
success: response.success
|
|
814
|
-
message: response.message
|
|
815
|
-
sessionId: response.sessionId,
|
|
252
|
+
success: response.success !== false,
|
|
253
|
+
message: response.message,
|
|
816
254
|
continueFromBatch: response.continueFromBatch,
|
|
817
|
-
|
|
255
|
+
sessionId: response.sessionId
|
|
818
256
|
};
|
|
819
257
|
}
|
|
820
258
|
/**
|
|
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
|
-
* ```
|
|
259
|
+
* Отмена восстановления сессии
|
|
839
260
|
*/
|
|
840
261
|
async cancelRecovery(projectId) {
|
|
841
262
|
this.validateProjectId(projectId);
|
|
842
|
-
const response = await this.httpClient.delete(`/api/v1/projects/${projectId}/
|
|
263
|
+
const response = await this.httpClient.delete(`/api/v1/projects/${projectId}/recovery`);
|
|
843
264
|
return {
|
|
844
|
-
success: response.success
|
|
845
|
-
message: response.message
|
|
265
|
+
success: response.success !== false,
|
|
266
|
+
message: response.message
|
|
846
267
|
};
|
|
847
268
|
}
|
|
848
269
|
/**
|
|
849
|
-
*
|
|
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
|
-
* ```
|
|
270
|
+
* Инициализация Delta Sync
|
|
870
271
|
*/
|
|
871
272
|
async initializeDeltaSync(projectId, clientRootHash) {
|
|
872
273
|
this.validateProjectId(projectId);
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
274
|
+
if (!clientRootHash?.trim()) {
|
|
275
|
+
throw new Error('Client root hash is required');
|
|
276
|
+
}
|
|
277
|
+
const response = await this.httpClient.post(`/api/v1/projects/${projectId}/initialize-delta-sync`, {
|
|
278
|
+
clientRootHash: clientRootHash.trim()
|
|
279
|
+
});
|
|
280
|
+
return {
|
|
281
|
+
needsSync: response.needsSync !== false,
|
|
282
|
+
sessionId: response.sessionId,
|
|
283
|
+
lastSyncHash: response.lastSyncHash
|
|
284
|
+
};
|
|
876
285
|
}
|
|
877
286
|
/**
|
|
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
|
-
* ```
|
|
287
|
+
* Сброс индексации проекта
|
|
900
288
|
*/
|
|
901
289
|
async resetIndexing(projectId) {
|
|
902
290
|
this.validateProjectId(projectId);
|
|
903
291
|
const response = await this.httpClient.post(`/api/v1/projects/${projectId}/reset-indexing`);
|
|
904
292
|
return {
|
|
905
|
-
success: response.success
|
|
906
|
-
message: response.message || '',
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
chunksDeleted: 0,
|
|
910
|
-
sessionsDeleted: 0
|
|
911
|
-
}
|
|
293
|
+
success: response.success !== false,
|
|
294
|
+
message: response.message || 'Reset completed',
|
|
295
|
+
clearedChunks: response.clearedChunks,
|
|
296
|
+
clearedFiles: response.clearedFiles
|
|
912
297
|
};
|
|
913
298
|
}
|
|
914
299
|
/**
|
|
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
|
-
* ```
|
|
300
|
+
* Перезапуск индексации проекта
|
|
938
301
|
*/
|
|
939
302
|
async restartIndexing(projectId) {
|
|
940
303
|
this.validateProjectId(projectId);
|
|
941
304
|
const response = await this.httpClient.post(`/api/v1/projects/${projectId}/restart-indexing`);
|
|
942
305
|
return {
|
|
943
|
-
success: response.success
|
|
944
|
-
message: response.message || '',
|
|
945
|
-
|
|
946
|
-
vectorsDeleted: 0,
|
|
947
|
-
chunksDeleted: 0,
|
|
948
|
-
sessionsDeleted: 0
|
|
949
|
-
},
|
|
950
|
-
readyForNewSync: response.readyForNewSync || false
|
|
306
|
+
success: response.success !== false,
|
|
307
|
+
message: response.message || 'Restart initiated',
|
|
308
|
+
newSessionId: response.newSessionId
|
|
951
309
|
};
|
|
952
310
|
}
|
|
953
311
|
/**
|
|
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
|
-
* ```
|
|
312
|
+
* Получение маппинга файловых путей
|
|
971
313
|
*/
|
|
972
314
|
async getFilePathMapping(projectId) {
|
|
973
315
|
this.validateProjectId(projectId);
|
|
974
316
|
const response = await this.httpClient.get(`/api/v1/projects/${projectId}/file-path-mapping`);
|
|
975
|
-
// Конвертируем даты из строк в Date объекты
|
|
976
317
|
return {
|
|
977
|
-
success: response.success,
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
chunkCount: file.chunkCount,
|
|
981
|
-
lastUpdated: new Date(file.lastUpdated)
|
|
982
|
-
}))
|
|
318
|
+
success: response.success !== false,
|
|
319
|
+
mapping: response.mapping || [],
|
|
320
|
+
totalFiles: response.totalFiles || 0
|
|
983
321
|
};
|
|
984
322
|
}
|
|
323
|
+
// ===== УТИЛИТЫ =====
|
|
324
|
+
/**
|
|
325
|
+
* Валидация ID проекта
|
|
326
|
+
* @private
|
|
327
|
+
*/
|
|
328
|
+
validateProjectId(projectId) {
|
|
329
|
+
if (projectId === undefined || projectId === null) {
|
|
330
|
+
throw new Error('Project ID is required');
|
|
331
|
+
}
|
|
332
|
+
if (typeof projectId !== 'string') {
|
|
333
|
+
throw new Error('Project ID must be a string');
|
|
334
|
+
}
|
|
335
|
+
if (projectId.trim().length === 0) {
|
|
336
|
+
throw new Error('Project ID cannot be empty');
|
|
337
|
+
}
|
|
338
|
+
}
|
|
985
339
|
}
|
|
986
340
|
exports.ProjectsApi = ProjectsApi;
|
|
987
341
|
//# sourceMappingURL=projects-api.js.map
|