solver-sdk 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +176 -468
  2. package/dist/cjs/api/{chat-api.js → chat-api/index.js} +77 -125
  3. package/dist/cjs/api/chat-api/index.js.map +1 -0
  4. package/dist/cjs/api/chat-api/interfaces.js +3 -0
  5. package/dist/cjs/api/chat-api/interfaces.js.map +1 -0
  6. package/dist/cjs/api/chat-api/models.js +6 -0
  7. package/dist/cjs/api/chat-api/models.js.map +1 -0
  8. package/dist/cjs/api/chat-api/stream-utils.js +192 -0
  9. package/dist/cjs/api/chat-api/stream-utils.js.map +1 -0
  10. package/dist/cjs/api/chat-api/websocket-helpers.js +211 -0
  11. package/dist/cjs/api/chat-api/websocket-helpers.js.map +1 -0
  12. package/dist/cjs/api/projects-api.js +275 -3
  13. package/dist/cjs/api/projects-api.js.map +1 -1
  14. package/dist/cjs/code-solver-sdk.js +21 -23
  15. package/dist/cjs/code-solver-sdk.js.map +1 -1
  16. package/dist/cjs/constants/websocket-events.constants.js +90 -52
  17. package/dist/cjs/constants/websocket-events.constants.js.map +1 -1
  18. package/dist/cjs/constants/websocket-namespaces.constants.js +18 -0
  19. package/dist/cjs/constants/websocket-namespaces.constants.js.map +1 -0
  20. package/dist/cjs/interfaces/websocket/callbacks.interfaces.js +3 -0
  21. package/dist/cjs/interfaces/websocket/callbacks.interfaces.js.map +1 -0
  22. package/dist/cjs/interfaces/websocket/client-options.interfaces.js +3 -0
  23. package/dist/cjs/interfaces/websocket/client-options.interfaces.js.map +1 -0
  24. package/dist/cjs/interfaces/websocket/events.interfaces.js +3 -0
  25. package/dist/cjs/interfaces/websocket/events.interfaces.js.map +1 -0
  26. package/dist/cjs/interfaces/websocket/stats.interfaces.js +3 -0
  27. package/dist/cjs/interfaces/websocket/stats.interfaces.js.map +1 -0
  28. package/dist/cjs/utils/code-solver-websocket-client.js +383 -1473
  29. package/dist/cjs/utils/code-solver-websocket-client.js.map +1 -1
  30. package/dist/cjs/utils/connection-state-manager.js +133 -0
  31. package/dist/cjs/utils/connection-state-manager.js.map +1 -0
  32. package/dist/cjs/utils/http-client.js +7 -0
  33. package/dist/cjs/utils/http-client.js.map +1 -1
  34. package/dist/cjs/utils/logger.js +106 -0
  35. package/dist/cjs/utils/logger.js.map +1 -0
  36. package/dist/cjs/utils/ping-pong-manager.js +306 -0
  37. package/dist/cjs/utils/ping-pong-manager.js.map +1 -0
  38. package/dist/cjs/utils/reasoning-auth-helper.js +58 -0
  39. package/dist/cjs/utils/reasoning-auth-helper.js.map +1 -0
  40. package/dist/cjs/utils/session-manager.js +114 -0
  41. package/dist/cjs/utils/session-manager.js.map +1 -0
  42. package/dist/cjs/utils/websocket-client.js +37 -10
  43. package/dist/cjs/utils/websocket-client.js.map +1 -1
  44. package/dist/cjs/ws/base-ws-client.js +286 -0
  45. package/dist/cjs/ws/base-ws-client.js.map +1 -0
  46. package/dist/cjs/ws/dependencies-ws-client.js +11 -0
  47. package/dist/cjs/ws/dependencies-ws-client.js.map +1 -0
  48. package/dist/cjs/ws/diagnostics-service.js +170 -0
  49. package/dist/cjs/ws/diagnostics-service.js.map +1 -0
  50. package/dist/cjs/ws/indexing-ws-client.js +223 -0
  51. package/dist/cjs/ws/indexing-ws-client.js.map +1 -0
  52. package/dist/cjs/ws/notifications-ws-client.js +12 -0
  53. package/dist/cjs/ws/notifications-ws-client.js.map +1 -0
  54. package/dist/cjs/ws/reasoning-ws-client.js +330 -0
  55. package/dist/cjs/ws/reasoning-ws-client.js.map +1 -0
  56. package/dist/esm/api/{chat-api.js → chat-api/index.js} +74 -125
  57. package/dist/esm/api/chat-api/index.js.map +1 -0
  58. package/dist/esm/api/chat-api/interfaces.js +2 -0
  59. package/dist/esm/api/chat-api/interfaces.js.map +1 -0
  60. package/dist/esm/api/chat-api/models.js +5 -0
  61. package/dist/esm/api/chat-api/models.js.map +1 -0
  62. package/dist/esm/api/chat-api/stream-utils.js +188 -0
  63. package/dist/esm/api/chat-api/stream-utils.js.map +1 -0
  64. package/dist/esm/api/chat-api/websocket-helpers.js +205 -0
  65. package/dist/esm/api/chat-api/websocket-helpers.js.map +1 -0
  66. package/dist/esm/api/projects-api.js +275 -3
  67. package/dist/esm/api/projects-api.js.map +1 -1
  68. package/dist/esm/code-solver-sdk.js +13 -15
  69. package/dist/esm/code-solver-sdk.js.map +1 -1
  70. package/dist/esm/constants/websocket-events.constants.js +89 -51
  71. package/dist/esm/constants/websocket-events.constants.js.map +1 -1
  72. package/dist/esm/constants/websocket-namespaces.constants.js +15 -0
  73. package/dist/esm/constants/websocket-namespaces.constants.js.map +1 -0
  74. package/dist/esm/interfaces/websocket/callbacks.interfaces.js +2 -0
  75. package/dist/esm/interfaces/websocket/callbacks.interfaces.js.map +1 -0
  76. package/dist/esm/interfaces/websocket/client-options.interfaces.js +2 -0
  77. package/dist/esm/interfaces/websocket/client-options.interfaces.js.map +1 -0
  78. package/dist/esm/interfaces/websocket/events.interfaces.js +2 -0
  79. package/dist/esm/interfaces/websocket/events.interfaces.js.map +1 -0
  80. package/dist/esm/interfaces/websocket/stats.interfaces.js +2 -0
  81. package/dist/esm/interfaces/websocket/stats.interfaces.js.map +1 -0
  82. package/dist/esm/utils/code-solver-websocket-client.js +382 -1473
  83. package/dist/esm/utils/code-solver-websocket-client.js.map +1 -1
  84. package/dist/esm/utils/connection-state-manager.js +129 -0
  85. package/dist/esm/utils/connection-state-manager.js.map +1 -0
  86. package/dist/esm/utils/http-client.js +7 -0
  87. package/dist/esm/utils/http-client.js.map +1 -1
  88. package/dist/esm/utils/logger.js +101 -0
  89. package/dist/esm/utils/logger.js.map +1 -0
  90. package/dist/esm/utils/ping-pong-manager.js +302 -0
  91. package/dist/esm/utils/ping-pong-manager.js.map +1 -0
  92. package/dist/esm/utils/reasoning-auth-helper.js +54 -0
  93. package/dist/esm/utils/reasoning-auth-helper.js.map +1 -0
  94. package/dist/esm/utils/session-manager.js +109 -0
  95. package/dist/esm/utils/session-manager.js.map +1 -0
  96. package/dist/esm/utils/websocket-client.js +37 -10
  97. package/dist/esm/utils/websocket-client.js.map +1 -1
  98. package/dist/esm/ws/base-ws-client.js +282 -0
  99. package/dist/esm/ws/base-ws-client.js.map +1 -0
  100. package/dist/esm/ws/dependencies-ws-client.js +7 -0
  101. package/dist/esm/ws/dependencies-ws-client.js.map +1 -0
  102. package/dist/esm/ws/diagnostics-service.js +166 -0
  103. package/dist/esm/ws/diagnostics-service.js.map +1 -0
  104. package/dist/esm/ws/indexing-ws-client.js +219 -0
  105. package/dist/esm/ws/indexing-ws-client.js.map +1 -0
  106. package/dist/esm/ws/notifications-ws-client.js +8 -0
  107. package/dist/esm/ws/notifications-ws-client.js.map +1 -0
  108. package/dist/esm/ws/reasoning-ws-client.js +326 -0
  109. package/dist/esm/ws/reasoning-ws-client.js.map +1 -0
  110. package/dist/types/api/chat-api/index.d.ts +81 -0
  111. package/dist/types/api/chat-api/index.d.ts.map +1 -0
  112. package/dist/types/api/chat-api/interfaces.d.ts +47 -0
  113. package/dist/types/api/chat-api/interfaces.d.ts.map +1 -0
  114. package/dist/types/api/{chat-api.d.ts → chat-api/models.d.ts} +10 -73
  115. package/dist/types/api/chat-api/models.d.ts.map +1 -0
  116. package/dist/types/api/chat-api/stream-utils.d.ts +31 -0
  117. package/dist/types/api/chat-api/stream-utils.d.ts.map +1 -0
  118. package/dist/types/api/chat-api/websocket-helpers.d.ts +40 -0
  119. package/dist/types/api/chat-api/websocket-helpers.d.ts.map +1 -0
  120. package/dist/types/api/projects-api.d.ts +114 -1
  121. package/dist/types/api/projects-api.d.ts.map +1 -1
  122. package/dist/types/code-solver-sdk.d.ts +3 -2
  123. package/dist/types/code-solver-sdk.d.ts.map +1 -1
  124. package/dist/types/constants/websocket-events.constants.d.ts +77 -36
  125. package/dist/types/constants/websocket-events.constants.d.ts.map +1 -1
  126. package/dist/types/constants/websocket-namespaces.constants.d.ts +14 -0
  127. package/dist/types/constants/websocket-namespaces.constants.d.ts.map +1 -0
  128. package/dist/types/interfaces/http-client.d.ts +1 -1
  129. package/dist/types/interfaces/http-client.d.ts.map +1 -1
  130. package/dist/types/interfaces/websocket/callbacks.interfaces.d.ts +30 -0
  131. package/dist/types/interfaces/websocket/callbacks.interfaces.d.ts.map +1 -0
  132. package/dist/types/interfaces/websocket/client-options.interfaces.d.ts +51 -0
  133. package/dist/types/interfaces/websocket/client-options.interfaces.d.ts.map +1 -0
  134. package/dist/types/interfaces/websocket/events.interfaces.d.ts +165 -0
  135. package/dist/types/interfaces/websocket/events.interfaces.d.ts.map +1 -0
  136. package/dist/types/interfaces/websocket/stats.interfaces.d.ts +72 -0
  137. package/dist/types/interfaces/websocket/stats.interfaces.d.ts.map +1 -0
  138. package/dist/types/types/index.d.ts +8 -0
  139. package/dist/types/types/index.d.ts.map +1 -1
  140. package/dist/types/utils/code-solver-websocket-client.d.ts +67 -612
  141. package/dist/types/utils/code-solver-websocket-client.d.ts.map +1 -1
  142. package/dist/types/utils/connection-state-manager.d.ts +94 -0
  143. package/dist/types/utils/connection-state-manager.d.ts.map +1 -0
  144. package/dist/types/utils/http-client.d.ts +5 -0
  145. package/dist/types/utils/http-client.d.ts.map +1 -1
  146. package/dist/types/utils/logger.d.ts +62 -0
  147. package/dist/types/utils/logger.d.ts.map +1 -0
  148. package/dist/types/utils/ping-pong-manager.d.ts +118 -0
  149. package/dist/types/utils/ping-pong-manager.d.ts.map +1 -0
  150. package/dist/types/utils/reasoning-auth-helper.d.ts +24 -0
  151. package/dist/types/utils/reasoning-auth-helper.d.ts.map +1 -0
  152. package/dist/types/utils/session-manager.d.ts +98 -0
  153. package/dist/types/utils/session-manager.d.ts.map +1 -0
  154. package/dist/types/utils/websocket-client.d.ts +6 -0
  155. package/dist/types/utils/websocket-client.d.ts.map +1 -1
  156. package/dist/types/ws/base-ws-client.d.ts +119 -0
  157. package/dist/types/ws/base-ws-client.d.ts.map +1 -0
  158. package/dist/types/ws/dependencies-ws-client.d.ts +6 -0
  159. package/dist/types/ws/dependencies-ws-client.d.ts.map +1 -0
  160. package/dist/types/ws/diagnostics-service.d.ts +93 -0
  161. package/dist/types/ws/diagnostics-service.d.ts.map +1 -0
  162. package/dist/types/ws/indexing-ws-client.d.ts +78 -0
  163. package/dist/types/ws/indexing-ws-client.d.ts.map +1 -0
  164. package/dist/types/ws/notifications-ws-client.d.ts +5 -0
  165. package/dist/types/ws/notifications-ws-client.d.ts.map +1 -0
  166. package/dist/types/ws/reasoning-ws-client.d.ts +87 -0
  167. package/dist/types/ws/reasoning-ws-client.d.ts.map +1 -0
  168. package/docs/API_REFERENCE.md +432 -0
  169. package/docs/AUTHENTICATION.md +83 -0
  170. package/docs/ERROR_HANDLING.md +240 -0
  171. package/docs/INTEGRATION_EXAMPLES.md +342 -0
  172. package/docs/PING_PONG.md +212 -0
  173. package/docs/README.md +102 -0
  174. package/docs/WEBSOCKET.md +139 -0
  175. package/docs/advanced/PING_PONG.md +212 -0
  176. package/docs/features/THINKING.md +158 -0
  177. package/docs/indexing/INDEXING.md +231 -0
  178. package/package.json +4 -3
  179. package/dist/cjs/api/chat-api.js.map +0 -1
  180. package/dist/esm/api/chat-api.js.map +0 -1
  181. package/dist/types/api/chat-api.d.ts.map +0 -1
@@ -1,1629 +1,538 @@
1
- import { WebSocketClient } from './websocket-client.js';
2
- import { WebSocketEvents as WsEvents } from '../constants/websocket-events.constants.js';
1
+ import { WebSocketEvents as WsEvents, ReasoningEventNames } from '../constants/websocket-events.constants';
2
+ import { WebSocketNamespace } from '../constants/websocket-namespaces.constants.js';
3
+ import { createWebSocketLogger } from './logger.js';
4
+ import { BaseWebSocketClient } from '../ws/base-ws-client.js';
5
+ // Реэкспортируем WebSocketNamespace для других модулей
6
+ export { WebSocketNamespace } from '../constants/websocket-namespaces.constants.js';
7
+ // Дополнительные события, которые могут отсутствовать в основном перечислении
8
+ const CUSTOM_EVENTS = {
9
+ GET_REASONING_STATUS: 'get_reasoning_status'
10
+ };
3
11
  /**
4
- * Пространства имен для WebSocket
12
+ * Специализированный WebSocket клиент для пространства имен reasoning
5
13
  */
6
- export var WebSocketNamespace;
7
- (function (WebSocketNamespace) {
8
- /** Основное пространство имен */
9
- WebSocketNamespace["DEFAULT"] = "/";
10
- /** Пространство для рассуждений */
11
- WebSocketNamespace["REASONING"] = "/reasoning";
12
- /** Пространство для индексации */
13
- WebSocketNamespace["INDEXING"] = "/indexing";
14
- /** Пространство для зависимостей */
15
- WebSocketNamespace["DEPENDENCIES"] = "/dependencies";
16
- })(WebSocketNamespace || (WebSocketNamespace = {}));
17
- /**
18
- * WebSocket клиент для работы с Code Solver API
19
- */
20
- export class CodeSolverWebSocketClient {
14
+ class ReasoningWsClient extends BaseWebSocketClient {
21
15
  /**
22
- * Создает новый WebSocket клиент для Code Solver API
16
+ * Создает новый WebSocket клиент для рассуждений
23
17
  * @param {string} baseURL Базовый URL API
24
- * @param {CodeSolverWebSocketOptions} [options] Опции клиента
18
+ * @param {BaseWebSocketClientOptions} options Опции клиента
25
19
  */
26
20
  constructor(baseURL, options = {}) {
27
- /** Пространство имен для Socket.IO */
28
- this.namespace = '';
29
- /** WebSocket клиенты для разных пространств имен */
30
- this.clients = new Map();
21
+ super(WebSocketNamespace.REASONING, baseURL, options);
31
22
  /** Активная сессия рассуждения */
32
23
  this.activeReasoningId = null;
33
- /** Активная сессия индексации */
34
- this.activeProjectId = null;
35
- /** Обработчики событий мышления */
36
- this.thinkingEventHandlers = new Map();
37
- /** Таймеры для ping/pong */
38
- this.pingIntervals = new Map();
39
- /** Статистика ping/pong */
40
- this.pingStats = new Map();
41
- /** Количество последовательных таймаутов */
42
- this.pingTimeouts = new Map();
43
- /** Задержка по умолчанию между ping-сообщениями (30 секунд) */
44
- this.defaultPingInterval = 30000;
45
- /** Порог таймаута (количество пропущенных pong) */
46
- this.timeoutThreshold = 3;
47
- /** Хранилище обработчиков ping/pong */
48
- this.pingPongEventHandlers = new Map();
49
- /** Токены сессий для разных пространств имен */
50
- this.sessionTokens = new Map();
51
- /** Состояние подключения для разных пространств имен */
52
- this.connectionState = new Map();
53
- /** Таймер для проверки здоровья соединений */
54
- this.healthCheckTimer = null;
55
- this.baseURL = baseURL.replace(/^http/, 'ws');
56
- this.options = {
57
- ...options,
58
- headers: {
59
- ...(options.headers || {}),
60
- ...(options.apiKey ? { 'Authorization': `Bearer ${options.apiKey}` } : {})
61
- }
62
- };
63
- // Инициализируем пространство имен
64
- this.namespace = String(WebSocketNamespace.REASONING);
65
24
  }
66
25
  /**
67
- * Логирование сообщений
68
- * @param {string} level Уровень логирования ('info', 'debug', 'error')
69
- * @param {string} message Сообщение для логирования
70
- * @param {any} [data] Дополнительные данные для логирования
26
+ * Подключается к серверу WebSocket с указанным ID рассуждения
27
+ * @param {string} reasoningId ID рассуждения
28
+ * @param {object} options Дополнительные опции
29
+ * @returns {Promise<boolean>} Успешность подключения
71
30
  */
72
- logger(level, message, data) {
73
- if (level === 'error') {
74
- console.error(`[CodeSolverWebSocket] ${message}`, data);
75
- }
76
- else if (level === 'info') {
77
- console.info(`[CodeSolverWebSocket] ${message}`, data);
78
- }
79
- else {
80
- console.debug(`[CodeSolverWebSocket] ${message}`, data);
81
- }
82
- }
83
- /**
84
- * Подключается к пространству имен WebSocket
85
- * @param {WebSocketNamespace} namespace Пространство имен
86
- * @param {Record<string, any>} [params] Параметры подключения
87
- * @returns {Promise<WebSocketClient>} WebSocket клиент
88
- */
89
- async connect(namespace, params = {}) {
90
- // Если клиент уже существует, возвращаем его
91
- if (this.clients.has(namespace)) {
92
- const client = this.clients.get(namespace);
93
- // Если клиент уже подключен, возвращаем его
94
- if (client.isConnected()) {
95
- console.debug(`[WS] Уже подключен к ${namespace}`);
96
- return client;
97
- }
98
- }
99
- // Формируем URL для подключения
100
- let baseUrl;
101
- let namespaceStr = '';
102
- // Добавляем namespace в URL путь (стандартный подход Socket.IO)
103
- if (namespace !== WebSocketNamespace.DEFAULT) {
104
- namespaceStr = String(namespace);
105
- if (!namespaceStr.startsWith('/')) {
106
- namespaceStr = '/' + namespaceStr;
107
- }
108
- }
109
- // Формируем правильный URL для Socket.IO
110
- if (this.baseURL.endsWith('/socket.io') || this.baseURL.endsWith('/socket.io/')) {
111
- // Для случая когда URL заканчивается на /socket.io, убираем слеш
112
- const cleanBaseUrl = this.baseURL.endsWith('/')
113
- ? this.baseURL.slice(0, -1)
114
- : this.baseURL;
115
- // Добавляем namespace к URL
116
- baseUrl = cleanBaseUrl + namespaceStr;
117
- }
118
- else {
119
- // Для случая когда URL не содержит /socket.io
120
- baseUrl = this.baseURL + namespaceStr;
31
+ async connectWithReasoning(reasoningId, options = {}) {
32
+ if (reasoningId) {
33
+ this.activeReasoningId = reasoningId;
121
34
  }
122
- // Создаем URL объект с параметрами
123
- const url = new URL(baseUrl);
124
- // Добавляем обязательные параметры для Socket.IO
125
- url.searchParams.append('EIO', '4');
126
- url.searchParams.append('transport', 'websocket');
127
- // Добавляем параметры к URL
128
- Object.entries(params).forEach(([key, value]) => {
129
- if (value !== undefined) {
130
- url.searchParams.append(key, String(value));
131
- }
132
- });
133
- const urlString = url.toString();
134
- console.debug(`[WS] Подключение к ${urlString}`, {
135
- namespace: String(namespace),
136
- hasApiKey: !!this.options.apiKey,
137
- apiKeyLength: this.options.apiKey ? this.options.apiKey.length : 0,
138
- params: Object.keys(params)
139
- });
140
- // Создаем новый WebSocket клиент
141
- const client = new WebSocketClient(urlString, {
142
- ...this.options,
143
- namespace: namespaceStr
144
- });
145
- // Добавляем обработчик для успешного подключения и отправки аутентификации
146
- client.on('open', () => {
147
- console.debug(`[WS] Подключение к ${String(namespace)} установлено`, {
148
- socketId: client.webSocket?.id,
149
- readyState: client.webSocket?.readyState
150
- });
151
- // Отправляем сообщение аутентификации, если задан API ключ
152
- if (this.options.apiKey && namespace !== WebSocketNamespace.DEFAULT) {
153
- try {
154
- const apiKeySafe = this.options.apiKey.length > 8
155
- ? `${this.options.apiKey.substring(0, 4)}...${this.options.apiKey.substring(this.options.apiKey.length - 4)}`
156
- : '[короткий ключ]';
157
- console.debug(`[WS] Отправка аутентификации для ${String(namespace)}`, {
158
- namespace: String(namespace),
159
- apiKey: apiKeySafe
160
- });
161
- // Отправляем сообщение аутентификации в формате Socket.IO
162
- const authMessage = {
163
- type: '2', // Socket.IO packet type: EVENT
164
- nsp: String(namespace),
165
- data: ['authenticate', { token: this.options.apiKey }]
166
- };
167
- client.send(authMessage);
168
- }
169
- catch (error) {
170
- console.error(`[WS] Ошибка при отправке аутентификации: ${error instanceof Error ? error.message : String(error)}`);
171
- }
172
- }
173
- });
174
- // Логирование ошибок
175
- client.on('error', (error) => {
176
- console.error(`[WS] Ошибка соединения с ${namespace}: ${error instanceof Error ? error.message : String(error)}`);
35
+ return super.connect({
36
+ reasoningId: this.activeReasoningId,
37
+ ...options
177
38
  });
178
- // Логирование разъединений
179
- client.on('close', (event) => {
180
- console.debug(`[WS] Соединение с ${namespace} закрыто: ${event.code || 'нет кода'}, ${event.reason || 'Причина не указана'}`);
181
- });
182
- // Логирование сообщений для отладки
183
- client.on('message', (data) => {
184
- try {
185
- console.debug(`[WS] Получено сообщение от ${namespace}: ${JSON.stringify(data).substring(0, 100)}...`);
186
- }
187
- catch (error) {
188
- console.debug(`[WS] Получено сообщение от ${namespace} (не может быть сериализовано в JSON)`);
189
- }
190
- });
191
- // Подключаемся к серверу
192
- await client.connect();
193
- // Сохраняем клиент
194
- this.clients.set(namespace, client);
195
- return client;
196
- }
197
- /**
198
- * Подключается к пространству имен индексации
199
- * @param projectId ID проекта (опционально)
200
- * @returns Promise с результатом подключения
201
- */
202
- async connectToIndexing(projectId) {
203
- try {
204
- this.logger('info', 'Подключение к пространству имен индексации', { projectId });
205
- // Если указан ID проекта, сохраняем его
206
- if (projectId) {
207
- this.activeProjectId = projectId;
208
- }
209
- // Подключаемся к пространству имен индексации
210
- const client = await this.connect(WebSocketNamespace.INDEXING);
211
- // Аутентифицируемся с увеличенным таймаутом
212
- try {
213
- const authResult = await client.emitWithAck(WsEvents.AUTHENTICATE, {
214
- token: this.options.apiKey,
215
- projectId: this.activeProjectId
216
- }, 10000);
217
- this.logger('debug', 'Результат аутентификации в namespace индексации', authResult);
218
- }
219
- catch (error) {
220
- this.logger('error', 'Ошибка аутентификации в namespace индексации', error);
221
- return false;
222
- }
223
- // Если у нас есть ID проекта, присоединяемся к нему
224
- if (this.activeProjectId) {
225
- try {
226
- const joinResult = await client.emitWithAck(WsEvents.JOIN_PROJECT, {
227
- projectId: this.activeProjectId,
228
- token: this.options.apiKey
229
- }, 10000);
230
- this.logger('debug', 'Результат присоединения к проекту', joinResult);
231
- return true;
232
- }
233
- catch (error) {
234
- this.logger('error', 'Ошибка присоединения к проекту', error);
235
- return false;
236
- }
237
- }
238
- return true;
239
- }
240
- catch (error) {
241
- this.logger('error', 'Ошибка подключения к пространству имен индексации', error);
242
- return false;
243
- }
244
39
  }
245
40
  /**
246
- * Подключается к уведомлениям
247
- * @returns {Promise<boolean>} Результат подключения
41
+ * Присоединяется к сессии рассуждения
42
+ * @param {string} reasoningId ID рассуждения
43
+ * @param {boolean} setActive Установить ли как активную сессию
44
+ * @returns {Promise<boolean>} Успешность операции
248
45
  */
249
- async connectToNotifications() {
250
- // Подключаемся к пространству имен
251
- try {
252
- await this.connect(WebSocketNamespace.DEFAULT);
253
- return true;
254
- }
255
- catch (error) {
46
+ async joinReasoning(reasoningId, setActive = true) {
47
+ if (!this.isConnected()) {
48
+ this.logger.warn(`Попытка присоединения к рассуждению ${reasoningId}, но клиент не подключен`);
256
49
  return false;
257
50
  }
258
- }
259
- /**
260
- * Подключается к пространству имен dependencies
261
- * @param projectId ID проекта (опционально)
262
- * @returns Promise с результатом подключения
263
- */
264
- async connectToDependencies(projectId) {
265
51
  try {
266
- this.logger('info', 'Подключение к пространству имен dependencies', { projectId });
267
- // Если указан ID проекта, сохраняем его
268
- if (projectId) {
269
- this.activeProjectId = projectId;
270
- }
271
- // Подключаемся к пространству имен dependencies
272
- const client = await this.connect(WebSocketNamespace.DEPENDENCIES);
273
- // Аутентифицируемся с увеличенным таймаутом
274
- try {
275
- const authResult = await client.emitWithAck(WsEvents.AUTHENTICATE, {
276
- token: this.options.apiKey,
277
- projectId: this.activeProjectId
278
- }, 10000);
279
- this.logger('debug', 'Результат аутентификации в namespace dependencies', authResult);
280
- }
281
- catch (error) {
282
- this.logger('error', 'Ошибка аутентификации в namespace dependencies', error);
283
- return false;
284
- }
285
- // Если у нас есть ID проекта, присоединяемся к нему
286
- if (this.activeProjectId) {
287
- try {
288
- const joinResult = await client.emitWithAck(WsEvents.JOIN_DEPENDENCIES, {
289
- projectId: this.activeProjectId,
290
- token: this.options.apiKey
291
- }, 10000);
292
- this.logger('debug', 'Результат присоединения к проекту', joinResult);
293
- return true;
294
- }
295
- catch (error) {
296
- this.logger('error', 'Ошибка присоединения к проекту', error);
297
- return false;
298
- }
52
+ // Отправляем запрос на присоединение
53
+ const result = await this.emitWithAck(WsEvents.JOIN_REASONING, { reasoningId });
54
+ if (result.success && setActive) {
55
+ this.activeReasoningId = reasoningId;
299
56
  }
300
- return true;
57
+ return result.success;
301
58
  }
302
59
  catch (error) {
303
- this.logger('error', 'Ошибка подключения к пространству имен dependencies', error);
60
+ this.logger.error(`Ошибка при присоединении к рассуждению ${reasoningId}`, error);
304
61
  return false;
305
62
  }
306
63
  }
307
64
  /**
308
- * Отключается от пространства имен
309
- * @param {WebSocketNamespace} namespace Пространство имен
65
+ * Подключает к сессии рассуждения и устанавливает обработчик для thinking
66
+ * @param {string} reasoningId ID рассуждения
67
+ * @param {function} thinkingHandler Обработчик событий thinking
68
+ * @returns {Promise<string>} ID рассуждения
310
69
  */
311
- disconnect(namespace) {
312
- const client = this.clients.get(namespace);
313
- if (client) {
314
- client.close();
315
- this.clients.delete(namespace);
316
- }
317
- // Сбрасываем активные сессии
318
- if (namespace === WebSocketNamespace.REASONING) {
319
- this.activeReasoningId = null;
70
+ async connectToThinkingSession(reasoningId, thinkingHandler) {
71
+ // Сначала подключаемся к сессии рассуждения
72
+ const connected = await this.connectWithReasoning(reasoningId, { autoJoin: true });
73
+ if (!connected) {
74
+ throw new Error(`Не удалось подключиться к рассуждению ${reasoningId}`);
320
75
  }
321
- else if (namespace === WebSocketNamespace.INDEXING) {
322
- this.activeProjectId = null;
76
+ // Затем подписываемся на мышление, если указан обработчик
77
+ if (thinkingHandler) {
78
+ this.subscribeToThinking(thinkingHandler);
323
79
  }
80
+ return reasoningId;
324
81
  }
325
82
  /**
326
- * Отключается от всех пространств имен
327
- * Отключает автоматический механизм ping/pong
83
+ * Подписаться на события мышления
84
+ * @param callback Функция обратного вызова для обработки событий мышления
328
85
  */
329
- disconnectAll() {
330
- // Отключаем ping/pong для всех соединений
331
- this.disablePingPong();
332
- // Отключаемся от всех namespace
333
- for (const [namespace, client] of this.clients.entries()) {
334
- if (client) {
335
- client.close();
336
- this.clients.delete(namespace);
337
- }
338
- }
339
- // Сбрасываем активные сессии
340
- this.activeReasoningId = null;
341
- this.activeProjectId = null;
86
+ subscribeToThinking(callback) {
87
+ this.on(ReasoningEventNames.THINKING, callback);
342
88
  }
343
89
  /**
344
- * Добавляет обработчик события для пространства имен
345
- * @param {string} eventType Тип события
346
- * @param {Function} handler Обработчик события
347
- * @param {WebSocketNamespace} [namespace] Пространство имен (если не указано, добавляется ко всем активным)
90
+ * Отписаться от событий мышления
91
+ * @param callback Функция обратного вызова для обработки событий мышления
348
92
  */
349
- on(eventType, handler, namespace) {
350
- if (namespace) {
351
- // Если указано пространство имен, добавляем обработчик только к нему
352
- const client = this.clients.get(namespace);
353
- if (!client) {
354
- throw new Error(`Не подключен к пространству имен ${namespace}`);
355
- }
356
- client.on(eventType, handler);
357
- }
358
- else {
359
- // Если пространство имен не указано, добавляем обработчик ко всем активным пространствам
360
- for (const client of this.clients.values()) {
361
- client.on(eventType, handler);
362
- }
363
- }
93
+ unsubscribeFromThinking(callback) {
94
+ this.off(ReasoningEventNames.THINKING, callback);
364
95
  }
365
96
  /**
366
- * Удаляет обработчик события для пространства имен
367
- * @param {string} eventType Тип события
368
- * @param {Function} [handler] Обработчик события (если не указан, удаляются все обработчики)
369
- * @param {WebSocketNamespace} [namespace] Пространство имен (если не указано, удаляется из всех активных)
97
+ * Проверяет статус рассуждения
98
+ * @param {string} reasoningId ID рассуждения
99
+ * @returns {Promise<object>} Объект с информацией о статусе
370
100
  */
371
- off(eventType, handler, namespace) {
372
- if (namespace) {
373
- // Если указано пространство имен, удаляем обработчик только из него
374
- const client = this.clients.get(namespace);
375
- if (!client) {
376
- return;
377
- }
378
- client.off(eventType, handler);
101
+ async getReasoningStatus(reasoningId) {
102
+ const id = reasoningId || this.activeReasoningId;
103
+ if (!id) {
104
+ return { exists: false, isActive: false };
379
105
  }
380
- else {
381
- // Если пространство имен не указано, удаляем обработчик из всех активных пространств
382
- for (const client of this.clients.values()) {
383
- client.off(eventType, handler);
106
+ try {
107
+ if (!this.isConnected()) {
108
+ await this.connect();
384
109
  }
110
+ const result = await this.emitWithAck(CUSTOM_EVENTS.GET_REASONING_STATUS, { reasoningId: id });
111
+ return result;
385
112
  }
386
- }
387
- /**
388
- * Отправляет сообщение в пространство имен
389
- * @param {WebSocketNamespace} namespace Пространство имен
390
- * @param {string} eventType Тип события
391
- * @param {any} [data] Данные сообщения
392
- * @returns {boolean} Успешно ли отправлено сообщение
393
- */
394
- send(namespace, eventType, data) {
395
- const client = this.clients.get(namespace);
396
- if (!client) {
397
- throw new Error(`Не подключен к пространству имен ${namespace}`);
398
- }
399
- return client.send({
400
- event: eventType,
401
- data
402
- });
403
- }
404
- /**
405
- * Отправляет сообщение в активную сессию рассуждения
406
- * @param {string} eventType Тип события
407
- * @param {any} [data] Данные сообщения
408
- * @returns {boolean} Успешно ли отправлено сообщение
409
- */
410
- sendToReasoning(eventType, data) {
411
- if (!this.activeReasoningId) {
412
- throw new Error('Не подключен к сессии рассуждения');
113
+ catch (error) {
114
+ this.logger.error(`Ошибка при получении статуса рассуждения ${id}`, error);
115
+ return { exists: false, isActive: false };
413
116
  }
414
- return this.send(WebSocketNamespace.REASONING, eventType, data);
415
117
  }
416
118
  /**
417
- * Отправляет сообщение в активную сессию индексации
418
- * @param {string} eventType Тип события
419
- * @param {any} [data] Данные сообщения
420
- * @returns {boolean} Успешно ли отправлено сообщение
119
+ * Проверяет существование рассуждения
120
+ * @param {string} reasoningId ID рассуждения
121
+ * @returns {Promise<boolean>} true, если рассуждение существует
421
122
  */
422
- sendToIndexing(eventType, data) {
423
- if (!this.activeProjectId) {
424
- throw new Error('Не подключен к сессии индексации');
123
+ async checkReasoningExists(reasoningId) {
124
+ try {
125
+ const { exists } = await this.getReasoningStatus(reasoningId);
126
+ return exists;
425
127
  }
426
- return this.send(WebSocketNamespace.INDEXING, eventType, data);
427
- }
428
- /**
429
- * Отправляет сообщение в уведомления
430
- * @param {string} eventType Тип события
431
- * @param {any} [data] Данные сообщения
432
- * @returns {boolean} Успешно ли отправлено сообщение
433
- */
434
- sendToNotifications(eventType, data) {
435
- return this.send(WebSocketNamespace.DEFAULT, eventType, data);
436
- }
437
- /**
438
- * Проверяет, подключен ли клиент к указанному пространству имен
439
- * @param {WebSocketNamespace} namespace Пространство имен
440
- * @returns {boolean} Статус подключения
441
- */
442
- isConnected(namespace) {
443
- const client = this.clients.get(namespace);
444
- return client ? client.isConnected() : false;
445
- }
446
- /**
447
- * Проверяет, подключен ли клиент к пространству имен рассуждений
448
- * @returns {boolean} Статус подключения
449
- */
450
- isConnectedToReasoning() {
451
- return this.isConnected(WebSocketNamespace.REASONING);
452
- }
453
- /**
454
- * Проверяет, подключен ли клиент к пространству имен индексации
455
- * @returns {boolean} Статус подключения
456
- */
457
- isConnectedToIndexing() {
458
- return this.isConnected(WebSocketNamespace.INDEXING);
459
- }
460
- /**
461
- * Проверяет, подключен ли клиент к пространству имен уведомлений
462
- * @returns {boolean} Статус подключения
463
- */
464
- isConnectedToNotifications() {
465
- return this.isConnected(WebSocketNamespace.DEFAULT);
466
- }
467
- /**
468
- * Получает ID сокета для указанного пространства имен
469
- * @param {WebSocketNamespace} [namespace=WebSocketNamespace.REASONING] Пространство имен
470
- * @returns {string|null} ID сокета или null, если соединение не установлено
471
- */
472
- getSocketId(namespace = WebSocketNamespace.REASONING) {
473
- const client = this.clients.get(namespace);
474
- if (!client || !client.isConnected()) {
475
- console.warn(`[WsClientWrapper] getSocketId: Нет активных соединений с Socket.IO сервером для ${namespace}`);
476
- return null;
128
+ catch (error) {
129
+ return false;
477
130
  }
478
- return client.getSocketId();
479
131
  }
480
132
  /**
481
- * Подписывается на события мышления
482
- * @param {string} reasoningId Идентификатор рассуждения
483
- * @param {Function} handler Обработчик событий мышления
484
- * @returns {void}
133
+ * Получает ID активной сессии рассуждения
134
+ * @returns {string | null} ID активной сессии или null
485
135
  */
486
- subscribeToThinking(reasoningId, handler) {
487
- // Сохраняем обработчик
488
- this.thinkingEventHandlers.set(reasoningId, handler);
489
- // Получаем клиент рассуждений
490
- const client = this.clients.get(WebSocketNamespace.REASONING);
491
- if (!client) {
492
- throw new Error('Не подключен к пространству имен рассуждения');
493
- }
494
- // Подписываемся на события мышления
495
- client.on(`thinking:${reasoningId}`, (data) => {
496
- handler(data);
497
- });
498
- // Дублируем подписку для полной совместимости
499
- client.on(`reasoning:thinking:${reasoningId}`, (data) => {
500
- handler(data);
501
- });
136
+ getActiveReasoningId() {
137
+ return this.activeReasoningId;
502
138
  }
139
+ }
140
+ /**
141
+ * Специализированный WebSocket клиент для пространства имен indexing
142
+ */
143
+ class IndexingWsClient extends BaseWebSocketClient {
503
144
  /**
504
- * Отписывается от событий мышления
505
- * @param {string} reasoningId Идентификатор рассуждения
506
- * @returns {void}
145
+ * Создает новый WebSocket клиент для индексации
146
+ * @param {string} baseURL Базовый URL API
147
+ * @param {BaseWebSocketClientOptions} options Опции клиента
507
148
  */
508
- unsubscribeFromThinking(reasoningId) {
509
- // Удаляем обработчик
510
- this.thinkingEventHandlers.delete(reasoningId);
511
- // Получаем клиент рассуждений
512
- const client = this.clients.get(WebSocketNamespace.REASONING);
513
- if (!client)
514
- return;
515
- // Отписываемся от событий
516
- client.off(`thinking:${reasoningId}`);
517
- client.off(`reasoning:thinking:${reasoningId}`);
149
+ constructor(baseURL, options = {}) {
150
+ super(WebSocketNamespace.INDEXING, baseURL, options);
151
+ /** Активный проект */
152
+ this.activeProjectId = null;
518
153
  }
519
154
  /**
520
- * Подключается к сессии рассуждения с thinking
521
- * @param {string} [reasoningId="system"] Идентификатор рассуждения
522
- * @param {Function} [thinkingHandler] Обработчик событий мышления
523
- * @returns {Promise<string>} Идентификатор сессии рассуждения
155
+ * Подключается к серверу WebSocket с указанным ID проекта
156
+ * @param {string} projectId ID проекта
157
+ * @returns {Promise<boolean>} Успешность подключения
524
158
  */
525
- async connectToThinkingSession(reasoningId = "system", thinkingHandler) {
526
- try {
527
- // Подключаемся к пространству имен
528
- await this.connect(WebSocketNamespace.REASONING);
529
- // Получаем клиент
530
- const client = this.clients.get(WebSocketNamespace.REASONING);
531
- if (!client) {
532
- throw new Error(`Не удалось получить WebSocket клиент для ${WebSocketNamespace.REASONING}`);
533
- }
534
- // Если reasoningId == "system", сервер заменит его на новый
535
- // с префиксом "system-". Для получения нового ID нужно подписаться
536
- // на событие создания рассуждения.
537
- if (reasoningId === "system") {
538
- // Будем ждать ответа о создании рассуждения
539
- return new Promise((resolve, reject) => {
540
- // Устанавливаем таймаут
541
- const timeout = setTimeout(() => {
542
- reject(new Error('Таймаут ожидания ответа о создании рассуждения'));
543
- }, 10000);
544
- // Подписываемся на событие создания рассуждения
545
- client.once(`${WsEvents.CREATE_REASONING}_response`, (data) => {
546
- clearTimeout(timeout);
547
- if (data.error) {
548
- reject(new Error(`Ошибка создания рассуждения: ${data.error}`));
549
- return;
550
- }
551
- const newReasoningId = data.reasoningId;
552
- this.activeReasoningId = newReasoningId;
553
- // Отправляем запрос на присоединение к сессии рассуждения
554
- client.sendSocketIOEvent(WsEvents.JOIN_REASONING, { reasoningId: newReasoningId }, (joinResponse) => {
555
- if (joinResponse.success === false) {
556
- reject(new Error(`Ошибка при присоединении к сессии рассуждения: ${joinResponse.error || 'Неизвестная ошибка'}`));
557
- return;
558
- }
559
- // Отправляем запрос на запуск рассуждения
560
- client.sendSocketIOEvent(WsEvents.START_REASONING, { reasoningId: newReasoningId }, (startResponse) => {
561
- if (startResponse.success === false) {
562
- reject(new Error(`Ошибка при запуске рассуждения: ${startResponse.error || 'Неизвестная ошибка'}`));
563
- return;
564
- }
565
- // Если передан обработчик событий мышления, подписываемся
566
- if (thinkingHandler) {
567
- this.subscribeToThinking(newReasoningId, thinkingHandler);
568
- }
569
- resolve(newReasoningId);
570
- }, this.namespace);
571
- }, this.namespace);
572
- });
573
- });
574
- }
575
- else {
576
- this.activeReasoningId = reasoningId;
577
- // Отправляем запрос на присоединение к сессии рассуждения
578
- return new Promise((resolve, reject) => {
579
- client.sendSocketIOEvent(WsEvents.JOIN_REASONING, { reasoningId }, (joinResponse) => {
580
- if (joinResponse.success === false) {
581
- reject(new Error(`Ошибка при присоединении к сессии рассуждения: ${joinResponse.error || 'Неизвестная ошибка'}`));
582
- return;
583
- }
584
- // Отправляем запрос на запуск рассуждения
585
- client.sendSocketIOEvent(WsEvents.START_REASONING, { reasoningId }, (startResponse) => {
586
- if (startResponse.success === false) {
587
- reject(new Error(`Ошибка при запуске рассуждения: ${startResponse.error || 'Неизвестная ошибка'}`));
588
- return;
589
- }
590
- // Если передан обработчик событий мышления, подписываемся
591
- if (thinkingHandler) {
592
- this.subscribeToThinking(reasoningId, thinkingHandler);
593
- }
594
- resolve(reasoningId);
595
- }, this.namespace);
596
- }, this.namespace);
597
- });
598
- }
599
- }
600
- catch (error) {
601
- throw new Error(`Ошибка при подключении к сессии thinking: ${error instanceof Error ? error.message : String(error)}`);
159
+ async connectWithProject(projectId) {
160
+ if (projectId) {
161
+ this.activeProjectId = projectId;
602
162
  }
603
- }
604
- /**
605
- * Настраивает отладочное логирование для WebSocket клиента
606
- * @param namespace Пространство имен
607
- */
608
- setupDebugLogging(namespace) {
609
- const client = this.clients.get(namespace);
610
- if (!client)
611
- return;
612
- // Добавляем детальное логирование всех событий
613
- client.on('socket.io_event', (data) => {
614
- this.logger('debug', `[WS:${namespace}] Получено Socket.IO событие: ${data.event}`, data.data);
615
- });
616
- // Добавляем обработчик для всех событий (onAny)
617
- client.on('message', (data) => {
618
- if (typeof data === 'string') {
619
- try {
620
- this.logger('debug', `[WS:${namespace}] Получено сырое сообщение`, data);
621
- }
622
- catch (e) {
623
- this.logger('error', `[WS:${namespace}] Ошибка при обработке сырого сообщения`, data);
624
- }
625
- }
626
- });
627
- // Отслеживаем состояние соединения
628
- client.on('connect', () => {
629
- this.logger('info', `[WS:${namespace}] Соединение установлено`);
630
- });
631
- client.on('close', (data) => {
632
- this.logger('info', `[WS:${namespace}] Соединение закрыто: ${data.code}, Причина: ${data.reason}`);
633
- });
634
- client.on('error', (error) => {
635
- this.logger('error', `[WS:${namespace}] Ошибка соединения`, error);
636
- });
637
- // Отслеживаем ping/pong для проверки состояния соединения
638
- client.on('ping', () => this.logger('debug', `[WS:${namespace}] Отправлен ping`));
639
- client.on('pong', () => this.logger('debug', `[WS:${namespace}] Получен pong`));
640
- // Добавляем обработчик для отслеживания ответов на события
641
- client.on('socket.io_raw', (data) => {
642
- this.logger('debug', `[WS:${namespace}] Socket.IO raw пакет`, { type: data.type, data: data.data });
163
+ return super.connect({
164
+ projectId: this.activeProjectId
643
165
  });
644
166
  }
645
167
  /**
646
- * Проверяет наличие callback-функции в данных и правильно вызывает ее
647
- * @param eventName Имя события
648
- * @param data Данные события
649
- * @private
168
+ * Получает ID активного проекта
169
+ * @returns {string | null} ID активного проекта или null
650
170
  */
651
- extractAndCallCallback(eventName, data) {
652
- if (data && typeof data === 'object' && typeof data.callback === 'function') {
653
- try {
654
- // Копируем данные без callback
655
- const dataCopy = { ...data };
656
- delete dataCopy.callback;
657
- // Получаем callback-функцию
658
- const callback = data.callback;
659
- // Вызываем callback
660
- this.logger('debug', `Вызов callback для события ${eventName}`);
661
- // Создаем таймаут для предотвращения зависания
662
- const timeoutId = setTimeout(() => {
663
- this.logger('warn', `Таймаут выполнения callback для события ${eventName}`);
664
- }, 5000);
665
- // Вызываем callback и очищаем таймаут
666
- callback();
667
- clearTimeout(timeoutId);
668
- }
669
- catch (error) {
670
- this.logger('error', `Ошибка при вызове callback для события ${eventName}: ${error instanceof Error ? error.message : String(error)}`);
671
- }
672
- }
171
+ getActiveProjectId() {
172
+ return this.activeProjectId;
673
173
  }
174
+ }
175
+ /**
176
+ * Специализированный WebSocket клиент для пространства имен dependencies
177
+ */
178
+ class DependenciesWsClient extends BaseWebSocketClient {
674
179
  /**
675
- * Включить автоматическую отправку ping-сообщений и сбор статистики
676
- * @param {number} interval - Интервал между ping-сообщениями в миллисекундах
677
- * @param {number} timeoutThreshold - Количество пропущенных pong-сообщений, после которого соединение считается потерянным
678
- * @returns {boolean} - Успешность включения ping/pong
180
+ * Создает новый WebSocket клиент для зависимостей
181
+ * @param {string} baseURL Базовый URL API
182
+ * @param {BaseWebSocketClientOptions} options Опции клиента
679
183
  */
680
- enablePingPong(interval = this.defaultPingInterval, timeoutThreshold = 3) {
681
- // Сохраняем порог таймаута
682
- this.timeoutThreshold = timeoutThreshold;
683
- // Для каждого активного соединения
684
- for (const [namespace, client] of this.clients.entries()) {
685
- try {
686
- // Проверяем, активно ли соединение
687
- if (!client || !this.isConnected(namespace)) {
688
- this.logger('warn', `Невозможно включить ping/pong для неактивного соединения в ${namespace}`);
689
- continue;
690
- }
691
- // Останавливаем существующий таймер, если есть
692
- this.disablePingPong(namespace);
693
- // Инициализируем статистику, если не была создана
694
- if (!this.pingStats.has(namespace)) {
695
- this.pingStats.set(namespace, {
696
- namespace,
697
- socketId: client.getSocketId(),
698
- pingSent: 0,
699
- pongReceived: 0,
700
- averageRtt: 0,
701
- minRtt: Number.MAX_SAFE_INTEGER,
702
- maxRtt: 0,
703
- lastRtt: 0,
704
- lastPongTimestamp: Date.now(),
705
- isConnected: true
706
- });
707
- }
708
- // Сбрасываем счетчик таймаутов
709
- this.pingTimeouts.set(namespace, 0);
710
- // Устанавливаем обработчик для события connection_pong
711
- client.on(WsEvents.CONNECTION_PONG, (data) => {
712
- // Обновляем статистику
713
- const stats = this.pingStats.get(namespace);
714
- if (stats) {
715
- stats.pongReceived++;
716
- stats.lastPongTimestamp = Date.now();
717
- stats.isConnected = true;
718
- // Рассчитываем RTT, если есть метка времени эхо
719
- if (data && data.echo) {
720
- const rtt = Date.now() - data.echo;
721
- stats.lastRtt = rtt;
722
- // Обновляем min и max
723
- stats.minRtt = Math.min(stats.minRtt, rtt);
724
- stats.maxRtt = Math.max(stats.maxRtt, rtt);
725
- // Обновляем среднее значение
726
- stats.averageRtt = (stats.averageRtt * (stats.pongReceived - 1) + rtt) / stats.pongReceived;
727
- }
728
- // Сбрасываем счетчик таймаутов
729
- this.pingTimeouts.set(namespace, 0);
730
- }
731
- // Логируем получение pong
732
- this.logger('debug', `Получен pong для ${namespace}`, {
733
- rtt: stats?.lastRtt,
734
- socketId: client.getSocketId()
735
- });
736
- });
737
- // Устанавливаем интервал отправки ping
738
- const pingInterval = setInterval(() => {
739
- if (this.isConnected(namespace)) {
740
- // Формируем данные ping
741
- const pingData = { timestamp: Date.now() };
742
- // Отправляем ping
743
- const sent = this.send(namespace, WsEvents.CONNECTION_PING, pingData);
744
- // Если успешно отправлено, обновляем статистику
745
- if (sent) {
746
- const stats = this.pingStats.get(namespace);
747
- if (stats) {
748
- stats.pingSent++;
749
- }
750
- this.logger('debug', `Отправлен ping для ${namespace}`, pingData);
751
- }
752
- else {
753
- this.logger('warn', `Не удалось отправить ping для ${namespace}`);
754
- }
755
- // Проверяем таймаут
756
- const timeouts = this.pingTimeouts.get(namespace) || 0;
757
- const stats = this.pingStats.get(namespace);
758
- // Если разница между отправленными и полученными превышает порог,
759
- // или последний pong был получен слишком давно
760
- if ((stats && stats.pingSent - stats.pongReceived > this.timeoutThreshold) ||
761
- (stats && Date.now() - stats.lastPongTimestamp > interval * this.timeoutThreshold)) {
762
- // Увеличиваем счетчик таймаутов
763
- this.pingTimeouts.set(namespace, timeouts + 1);
764
- if (timeouts + 1 >= this.timeoutThreshold) {
765
- // Соединение потеряно
766
- this.logger('error', `Соединение потеряно (таймаут ping/pong) для ${namespace}`);
767
- // Установка флага неактивного соединения
768
- if (stats) {
769
- stats.isConnected = false;
770
- }
771
- // На прямую отправку события через socket
772
- this.send(namespace, 'connection_timeout', {
773
- namespace,
774
- socketId: client.getSocketId(),
775
- timeouts: timeouts + 1,
776
- threshold: this.timeoutThreshold
777
- });
778
- // Также вызываем обработчики событий
779
- const timeoutHandlers = this.pingPongEventHandlers.get('connection_timeout') || [];
780
- timeoutHandlers.forEach(handler => {
781
- try {
782
- handler({
783
- namespace,
784
- socketId: client.getSocketId(),
785
- timeouts: timeouts + 1,
786
- threshold: this.timeoutThreshold
787
- });
788
- }
789
- catch (error) {
790
- this.logger('error', `Ошибка при обработке события connection_timeout`, error);
791
- }
792
- });
793
- }
794
- }
795
- }
796
- }, interval);
797
- // Сохраняем интервал
798
- this.pingIntervals.set(namespace, pingInterval);
799
- this.logger('info', `Включен механизм ping/pong для ${namespace} с интервалом ${interval}ms`);
800
- }
801
- catch (error) {
802
- this.logger('error', `Ошибка при включении ping/pong для ${namespace}`, error);
803
- return false;
804
- }
805
- }
806
- return true;
184
+ constructor(baseURL, options = {}) {
185
+ super(WebSocketNamespace.DEPENDENCIES, baseURL, options);
807
186
  }
808
187
  /**
809
- * Отключить автоматическую отправку ping-сообщений
810
- * @param {WebSocketNamespace} [namespace] - Пространство имен для отключения (если не указано - отключается везде)
188
+ * Подключается к серверу WebSocket с указанным ID проекта
189
+ * @param {string} projectId ID проекта
190
+ * @returns {Promise<boolean>} Успешность подключения
811
191
  */
812
- disablePingPong(namespace) {
813
- if (namespace) {
814
- // Отключаем для указанного namespace
815
- const interval = this.pingIntervals.get(namespace);
816
- if (interval) {
817
- clearInterval(interval);
818
- this.pingIntervals.delete(namespace);
819
- this.logger('info', `Отключен механизм ping/pong для ${namespace}`);
820
- }
821
- }
822
- else {
823
- // Отключаем для всех namespace
824
- for (const [ns, interval] of this.pingIntervals.entries()) {
825
- clearInterval(interval);
826
- this.pingIntervals.delete(ns);
827
- this.logger('info', `Отключен механизм ping/pong для ${ns}`);
828
- }
829
- }
192
+ async connectWithProject(projectId) {
193
+ return super.connect({ projectId });
830
194
  }
195
+ }
196
+ /**
197
+ * Специализированный WebSocket клиент для пространства имен notifications
198
+ */
199
+ class NotificationsWsClient extends BaseWebSocketClient {
831
200
  /**
832
- * Получить статистику ping/pong
833
- * @param {WebSocketNamespace} [namespace] - Пространство имен для получения статистики
834
- * @returns {PingPongStats | PingPongStats[] | null} - Статистика ping/pong
201
+ * Создает новый WebSocket клиент для уведомлений
202
+ * @param {string} baseURL Базовый URL API
203
+ * @param {BaseWebSocketClientOptions} options Опции клиента
835
204
  */
836
- getPingStats(namespace) {
837
- if (namespace) {
838
- // Возвращаем статистику для указанного namespace
839
- return this.pingStats.get(namespace) || null;
840
- }
841
- else {
842
- // Возвращаем статистику для всех namespace
843
- return Array.from(this.pingStats.values());
844
- }
205
+ constructor(baseURL, options = {}) {
206
+ super(WebSocketNamespace.DEFAULT, baseURL, options);
845
207
  }
208
+ }
209
+ /**
210
+ * Сервис для диагностики WebSocket соединений
211
+ */
212
+ class DiagnosticsService {
846
213
  /**
847
- * Добавляет обработчик для событий ping/pong
848
- * @param {string} eventType - Тип события (connection_timeout)
849
- * @param {(data: any) => void} handler - Обработчик события
214
+ * Создает новый сервис диагностики
215
+ * @param {DiagnosticsServiceOptions} options Опции сервиса
850
216
  */
851
- onPingPongEvent(eventType, handler) {
852
- if (!this.pingPongEventHandlers.has(eventType)) {
853
- this.pingPongEventHandlers.set(eventType, []);
854
- }
855
- const handlers = this.pingPongEventHandlers.get(eventType);
856
- if (handlers) {
857
- handlers.push(handler);
858
- }
217
+ constructor(options) {
218
+ this.clients = options.clients;
219
+ this.logger = options.logger;
859
220
  }
860
221
  /**
861
- * Удаляет обработчик для событий ping/pong
862
- * @param {string} eventType - Тип события
863
- * @param {(data: any) => void} [handler] - Обработчик события (если не указан, удаляются все обработчики)
222
+ * Получает диагностическую информацию о всех соединениях
223
+ * @returns {object} Диагностическая информация
864
224
  */
865
- offPingPongEvent(eventType, handler) {
866
- if (!handler) {
867
- // Если обработчик не указан, удаляем все обработчики для этого типа события
868
- this.pingPongEventHandlers.delete(eventType);
869
- }
870
- else {
871
- // Если обработчик указан, удаляем только его
872
- const handlers = this.pingPongEventHandlers.get(eventType);
873
- if (handlers) {
874
- const index = handlers.findIndex(h => h === handler);
875
- if (index !== -1) {
876
- handlers.splice(index, 1);
877
- }
878
- }
225
+ getDiagnostics() {
226
+ const result = {};
227
+ // Собираем информацию о каждом соединении
228
+ for (const [namespace, client] of Object.entries(this.clients)) {
229
+ result[namespace] = {
230
+ isConnected: client.isConnected(),
231
+ socketId: client.getSocketId()
232
+ // При необходимости можно добавить дополнительную информацию
233
+ };
879
234
  }
235
+ return result;
880
236
  }
237
+ }
238
+ /**
239
+ * WebSocket клиент для работы с Code Solver API (фасад)
240
+ */
241
+ export class CodeSolverWebSocketClient {
881
242
  /**
882
- * Возвращает функцию-обработчик для pong-ответов, которая рассчитывает RTT
883
- * @returns {(data: any) => void} Функция-обработчик
243
+ * Создает новый WebSocket клиент для Code Solver API
244
+ * @param {string} baseURL Базовый URL API
245
+ * @param {CodeSolverWebSocketOptions} [options] Опции клиента
884
246
  */
885
- getPongHandler() {
886
- return (data) => {
887
- if (data && data.echo) {
888
- const rtt = Date.now() - data.echo;
889
- console.log(`[PONG] RTT: ${rtt}ms, namespace: ${data.namespace || 'unknown'}`);
890
- return rtt;
247
+ constructor(baseURL, options = {}) {
248
+ this.baseURL = baseURL.replace(/^http/, 'ws');
249
+ this.options = {
250
+ ...options,
251
+ headers: {
252
+ ...(options.headers || {}),
253
+ ...(options.apiKey ? { 'Authorization': `Bearer ${options.apiKey}` } : {})
891
254
  }
892
- return -1;
893
255
  };
256
+ // Создаем логгер
257
+ this.logger = createWebSocketLogger('CodeSolverWebSocket');
258
+ // Трансформируем опции для специализированных клиентов
259
+ const clientOptions = {
260
+ ...this.options,
261
+ enableAutoPing: options.enableAutoPing !== false,
262
+ enableSessionPersistence: options.enableSessionPersistence !== false,
263
+ // Преобразуем функцию логгера в экземпляр Logger, если передана функция
264
+ logger: typeof options.logger === 'function' ?
265
+ createWebSocketLogger('CodeSolverWebSocket', options.logger) :
266
+ options.logger
267
+ };
268
+ // Создаем специализированные клиенты
269
+ this.reasoningClient = new ReasoningWsClient(this.baseURL, { ...clientOptions, logger: this.logger.withPrefix('Reasoning') });
270
+ this.indexingClient = new IndexingWsClient(this.baseURL, { ...clientOptions, logger: this.logger.withPrefix('Indexing') });
271
+ this.dependenciesClient = new DependenciesWsClient(this.baseURL, { ...clientOptions, logger: this.logger.withPrefix('Dependencies') });
272
+ this.notificationsClient = new NotificationsWsClient(this.baseURL, { ...clientOptions, logger: this.logger.withPrefix('Notifications') });
273
+ // Создаем сервис диагностики
274
+ this.diagnosticsService = new DiagnosticsService({
275
+ clients: {
276
+ [WebSocketNamespace.REASONING]: this.reasoningClient,
277
+ [WebSocketNamespace.INDEXING]: this.indexingClient,
278
+ [WebSocketNamespace.DEPENDENCIES]: this.dependenciesClient,
279
+ [WebSocketNamespace.DEFAULT]: this.notificationsClient
280
+ },
281
+ logger: this.logger.withPrefix('Diagnostics')
282
+ });
894
283
  }
895
284
  /**
896
- * Выполняет диагностику соединения и возвращает подробный отчет
897
- * @param {WebSocketNamespace} namespace Пространство имен
898
- * @returns {ConnectionDiagnostics} Объект с диагностической информацией
285
+ * Подключается к пространству имен рассуждений
286
+ * @param {string} reasoningId ID рассуждения (опционально)
287
+ * @param {object} options Дополнительные настройки подключения
288
+ * @returns {Promise<boolean>} Результат подключения
899
289
  */
900
- diagnoseConnection(namespace) {
901
- const client = this.clients.get(namespace);
902
- const stats = this.pingStats.get(namespace);
903
- const connectionState = this.getConnectionState(namespace);
904
- const sessionToken = this.getSessionToken(namespace);
905
- return {
906
- namespace,
907
- isConnected: client?.isConnected() || false,
908
- socketId: client?.getSocketId() || null,
909
- lastActivity: stats?.lastPongTimestamp || 0,
910
- rtt: {
911
- current: stats?.lastRtt || -1,
912
- min: stats?.minRtt === Number.MAX_SAFE_INTEGER ? -1 : (stats?.minRtt || -1),
913
- max: stats?.maxRtt || -1,
914
- avg: stats?.averageRtt || -1
915
- },
916
- pingSent: stats?.pingSent || 0,
917
- pongReceived: stats?.pongReceived || 0,
918
- missedPongs: (stats?.pingSent || 0) - (stats?.pongReceived || 0),
919
- timeoutCount: this.pingTimeouts.get(namespace) || 0,
920
- reconnectAttempts: connectionState.reconnectAttempts,
921
- lastConnectTime: connectionState.lastConnectTime,
922
- sessionRecovery: {
923
- hasSessionToken: !!sessionToken,
924
- tokenLength: sessionToken?.length || 0,
925
- wasRecovered: !!sessionToken && (stats?.pongReceived || 0) > 0
926
- }
927
- };
290
+ async connectToReasoning(reasoningId, options = {}) {
291
+ return this.reasoningClient.connectWithReasoning(reasoningId, options);
928
292
  }
929
293
  /**
930
- * Выполняет диагностику всех активных соединений
931
- * @returns {Record<string, ConnectionDiagnostics>} Объект с диагностической информацией по всем соединениям
294
+ * Подключается к пространству имен индексации
295
+ * @param {string} projectId ID проекта (опционально)
296
+ * @returns {Promise<boolean>} Результат подключения
932
297
  */
933
- diagnoseAllConnections() {
934
- const result = {};
935
- // Проверяем каждое возможное пространство имен
936
- for (const namespace of Object.values(WebSocketNamespace)) {
937
- if (this.clients.has(namespace)) {
938
- result[String(namespace)] = this.diagnoseConnection(namespace);
939
- }
940
- }
941
- return result;
298
+ async connectToIndexing(projectId) {
299
+ return this.indexingClient.connectWithProject(projectId);
942
300
  }
943
301
  /**
944
- * Рассчитывает задержку для переподключения на основе количества попыток и стратегии
945
- * @param {WebSocketNamespace} namespace Пространство имен
946
- * @returns {number} Задержка в миллисекундах
302
+ * Подключается к уведомлениям
303
+ * @returns {Promise<boolean>} Результат подключения
947
304
  */
948
- calculateReconnectDelay(namespace) {
949
- const state = this.getConnectionState(namespace);
950
- const attempts = state.reconnectAttempts;
951
- const strategy = this.options.reconnectStrategy || 'exponential';
952
- const baseDelay = this.options.retryDelay || 1000;
953
- const maxDelay = this.options.maxRetryDelay || 30000;
954
- if (strategy === 'exponential') {
955
- // Экспоненциальный рост с фактором 1.5
956
- const calculatedDelay = Math.min(baseDelay * Math.pow(1.5, attempts), maxDelay);
957
- // Добавляем случайный фактор (jitter) для предотвращения штормов переподключений
958
- return calculatedDelay * (0.8 + Math.random() * 0.4);
959
- }
960
- else {
961
- // Линейный рост
962
- return Math.min(baseDelay * (attempts + 1), maxDelay);
963
- }
305
+ async connectToNotifications() {
306
+ return this.notificationsClient.connect({});
964
307
  }
965
308
  /**
966
- * Принудительно переподключает соединение для указанного пространства имен
967
- * @param {WebSocketNamespace} namespace Пространство имен
968
- * @param {boolean} immediate Выполнить переподключение немедленно, без задержки
969
- * @returns {Promise<boolean>} Успешность операции
309
+ * Подключается к пространству имен dependencies
310
+ * @param {string} projectId ID проекта (опционально)
311
+ * @returns {Promise<boolean>} Результат подключения
970
312
  */
971
- async reconnectNamespace(namespace, immediate = false) {
972
- const client = this.clients.get(namespace);
973
- try {
974
- // Если клиент уже существует, закрываем его
975
- if (client) {
976
- this.logger('info', `Принудительное переподключение для ${namespace}`);
977
- try {
978
- // Отключаем ping/pong для этого namespace
979
- this.disablePingPong(namespace);
980
- // Закрываем соединение
981
- client.close();
982
- }
983
- catch (e) {
984
- this.logger('warn', `Ошибка при закрытии соединения с ${namespace}: ${e instanceof Error ? e.message : String(e)}`);
985
- }
986
- // Удаляем клиент из кэша
987
- this.clients.delete(namespace);
988
- }
989
- else {
990
- this.logger('info', `Инициируем новое подключение для ${namespace}`);
991
- }
992
- // Устанавливаем состояние переподключения
993
- this.setConnectionState(namespace, false, true);
994
- // Инкрементируем счетчик попыток
995
- this.incrementReconnectAttempts(namespace);
996
- // Рассчитываем задержку, если не требуется немедленное переподключение
997
- if (!immediate) {
998
- const delay = this.calculateReconnectDelay(namespace);
999
- this.logger('info', `Переподключение для ${namespace} через ${delay}ms (попытка ${this.getConnectionState(namespace).reconnectAttempts})`);
1000
- // Ждем рассчитанное время
1001
- await new Promise(resolve => setTimeout(resolve, delay));
1002
- }
1003
- // Выполняем подключение с соответствующими параметрами
1004
- let params = {};
1005
- // Проверяем тип пространства имен и добавляем соответствующие параметры
1006
- if (namespace === WebSocketNamespace.REASONING && this.activeReasoningId) {
1007
- params.reasoningId = this.activeReasoningId;
1008
- }
1009
- else if ((namespace === WebSocketNamespace.INDEXING || namespace === WebSocketNamespace.DEPENDENCIES) && this.activeProjectId) {
1010
- params.projectId = this.activeProjectId;
1011
- }
1012
- // Подключаемся
1013
- await this.connect(namespace, params);
1014
- // Если это пространство имен рассуждений и есть активное рассуждение,
1015
- // пытаемся присоединиться к нему
1016
- if (namespace === WebSocketNamespace.REASONING && this.activeReasoningId) {
1017
- await this.connectToReasoning(this.activeReasoningId);
1018
- }
1019
- else if (namespace === WebSocketNamespace.INDEXING && this.activeProjectId) {
1020
- await this.connectToIndexing(this.activeProjectId);
1021
- }
1022
- else if (namespace === WebSocketNamespace.DEPENDENCIES && this.activeProjectId) {
1023
- await this.connectToDependencies(this.activeProjectId);
1024
- }
1025
- return true;
1026
- }
1027
- catch (error) {
1028
- this.logger('error', `Ошибка при переподключении к ${namespace}: ${error instanceof Error ? error.message : String(error)}`);
1029
- return false;
1030
- }
313
+ async connectToDependencies(projectId) {
314
+ return this.dependenciesClient.connectWithProject(projectId);
1031
315
  }
1032
316
  /**
1033
- * Настраивает периодическую проверку здоровья соединения
1034
- * @param {number} [interval=30000] Интервал проверки в миллисекундах
317
+ * Отключается от пространства имен
318
+ * @param {WebSocketNamespace} namespace Пространство имен
1035
319
  */
1036
- setupConnectionHealthCheck(interval = 30000) {
1037
- // Останавливаем существующую проверку, если она есть
1038
- if (this.healthCheckTimer) {
1039
- clearInterval(this.healthCheckTimer);
320
+ disconnect(namespace) {
321
+ switch (namespace) {
322
+ case WebSocketNamespace.REASONING:
323
+ this.reasoningClient.disconnect();
324
+ break;
325
+ case WebSocketNamespace.INDEXING:
326
+ this.indexingClient.disconnect();
327
+ break;
328
+ case WebSocketNamespace.DEPENDENCIES:
329
+ this.dependenciesClient.disconnect();
330
+ break;
331
+ case WebSocketNamespace.DEFAULT:
332
+ this.notificationsClient.disconnect();
333
+ break;
1040
334
  }
1041
- this.healthCheckTimer = setInterval(() => {
1042
- for (const namespace of Object.values(WebSocketNamespace)) {
1043
- const typedNamespace = namespace;
1044
- const client = this.clients.get(typedNamespace);
1045
- if (!client)
1046
- continue;
1047
- // Проверяем соединение через WebSocket клиент
1048
- if (!client.isConnected()) {
1049
- this.logger('warn', `Соединение с ${namespace} не активно, инициируем переподключение`);
1050
- this.reconnectNamespace(typedNamespace, false).catch(() => { });
1051
- continue;
1052
- }
1053
- // Проверяем статистику ping/pong
1054
- const stats = this.pingStats.get(typedNamespace);
1055
- if (stats) {
1056
- const now = Date.now();
1057
- // Если последний pong был получен слишком давно
1058
- if (now - stats.lastPongTimestamp > interval * 2) {
1059
- this.logger('warn', `Долгое отсутствие активности для ${namespace}, проверка соединения...`);
1060
- // Отправляем проверочный ping
1061
- this.send(typedNamespace, 'connection_health_check', { timestamp: now, echo: now });
1062
- // Устанавливаем таймаут для проверки ответа
1063
- setTimeout(() => {
1064
- const currentStats = this.pingStats.get(typedNamespace);
1065
- if (currentStats && now - currentStats.lastPongTimestamp > interval * 2) {
1066
- this.logger('error', `Соединение не отвечает для ${namespace}, инициируем переподключение`);
1067
- this.reconnectNamespace(typedNamespace, false).catch(() => { });
1068
- }
1069
- }, 5000); // Ждем 5 секунд на ответ
1070
- }
1071
- }
1072
- }
1073
- }, interval);
1074
- this.logger('info', 'Настроена периодическая проверка здоровья соединения с интервалом ' + interval + 'ms');
1075
335
  }
1076
336
  /**
1077
- * Сохраняет токен сессии для пространства имен
1078
- * @param {WebSocketNamespace} namespace Пространство имен
1079
- * @param {string} token Токен сессии
337
+ * Отключается от всех пространств имен
1080
338
  */
1081
- saveSessionToken(namespace, token) {
1082
- if (this.options.enableSessionPersistence !== false) {
1083
- this.sessionTokens.set(namespace, token);
1084
- this.logger('info', `Сохранен токен сессии для ${namespace}`, { tokenLength: token.length });
1085
- }
339
+ disconnectAll() {
340
+ this.reasoningClient.disconnect();
341
+ this.indexingClient.disconnect();
342
+ this.dependenciesClient.disconnect();
343
+ this.notificationsClient.disconnect();
1086
344
  }
1087
345
  /**
1088
- * Получает сохраненный токен сессии для пространства имен
346
+ * Проверяет, подключен ли клиент к указанному пространству имен
1089
347
  * @param {WebSocketNamespace} namespace Пространство имен
1090
- * @returns {string | null} Токен сессии или null, если не найден
348
+ * @returns {boolean} Статус подключения
1091
349
  */
1092
- getSessionToken(namespace) {
1093
- if (this.options.enableSessionPersistence === false) {
1094
- return null;
350
+ isConnected(namespace) {
351
+ switch (namespace) {
352
+ case WebSocketNamespace.REASONING:
353
+ return this.reasoningClient.isConnected();
354
+ case WebSocketNamespace.INDEXING:
355
+ return this.indexingClient.isConnected();
356
+ case WebSocketNamespace.DEPENDENCIES:
357
+ return this.dependenciesClient.isConnected();
358
+ case WebSocketNamespace.DEFAULT:
359
+ return this.notificationsClient.isConnected();
360
+ default:
361
+ return false;
1095
362
  }
1096
- return this.sessionTokens.get(namespace) || null;
1097
363
  }
1098
364
  /**
1099
- * Удаляет сохраненный токен сессии для пространства имен
1100
- * @param {WebSocketNamespace} namespace Пространство имен
365
+ * Проверяет, подключен ли клиент к пространству имен рассуждений
366
+ * @returns {boolean} Статус подключения
1101
367
  */
1102
- clearSessionToken(namespace) {
1103
- this.sessionTokens.delete(namespace);
1104
- this.logger('info', `Удален токен сессии для ${namespace}`);
368
+ isConnectedToReasoning() {
369
+ return this.reasoningClient.isConnected();
1105
370
  }
1106
371
  /**
1107
- * Устанавливает состояние подключения для пространства имен
1108
- * @param {WebSocketNamespace} namespace Пространство имен
1109
- * @param {boolean} connected Состояние подключения
1110
- * @param {boolean} reconnecting Состояние переподключения
372
+ * Проверяет, подключен ли клиент к пространству имен индексации
373
+ * @returns {boolean} Статус подключения
1111
374
  */
1112
- setConnectionState(namespace, connected, reconnecting = false) {
1113
- const state = this.getConnectionState(namespace);
1114
- state.connected = connected;
1115
- state.reconnecting = reconnecting;
1116
- if (connected) {
1117
- state.lastConnectTime = Date.now();
1118
- state.reconnectAttempts = 0;
1119
- }
1120
- this.connectionState.set(namespace, state);
375
+ isConnectedToIndexing() {
376
+ return this.indexingClient.isConnected();
1121
377
  }
1122
378
  /**
1123
- * Увеличивает счетчик попыток переподключения для пространства имен
1124
- * @param {WebSocketNamespace} namespace Пространство имен
1125
- * @returns {number} Новое количество попыток
379
+ * Проверяет, подключен ли клиент к пространству имен уведомлений
380
+ * @returns {boolean} Статус подключения
1126
381
  */
1127
- incrementReconnectAttempts(namespace) {
1128
- const state = this.getConnectionState(namespace);
1129
- state.reconnectAttempts++;
1130
- state.reconnecting = true;
1131
- this.connectionState.set(namespace, state);
1132
- return state.reconnectAttempts;
382
+ isConnectedToNotifications() {
383
+ return this.notificationsClient.isConnected();
1133
384
  }
1134
385
  /**
1135
- * Получает состояние подключения для пространства имен
1136
- * @param {WebSocketNamespace} namespace Пространство имен
1137
- * @returns {object} Состояние подключения
386
+ * Получает ID активной сессии рассуждения
387
+ * @returns {string | null} ID активной сессии рассуждения или null
1138
388
  */
1139
- getConnectionState(namespace) {
1140
- return this.connectionState.get(namespace) || {
1141
- lastConnectTime: 0,
1142
- reconnectAttempts: 0,
1143
- connected: false,
1144
- reconnecting: false
1145
- };
389
+ getActiveReasoningId() {
390
+ return this.reasoningClient.getActiveReasoningId();
1146
391
  }
1147
392
  /**
1148
- * Устанавливает ID активной сессии рассуждения
1149
- * @param {string} reasoningId ID сессии рассуждения
1150
- * @returns {boolean} Успешность установки
393
+ * Получает ID активного проекта
394
+ * @returns {string | null} ID активного проекта или null
1151
395
  */
1152
- setActiveReasoningId(reasoningId) {
1153
- if (!reasoningId) {
1154
- this.logger('error', 'Попытка установить пустой reasoningId');
1155
- return false;
1156
- }
1157
- this.activeReasoningId = reasoningId;
1158
- this.logger('info', `Установлен активный reasoningId: ${reasoningId}`);
1159
- // Если мы уже подключены к пространству имен рассуждений, проверяем, нужно ли присоединиться
1160
- const client = this.clients.get(WebSocketNamespace.REASONING);
1161
- if (client && client.isConnected()) {
1162
- // Пробуем присоединиться к рассуждению асинхронно, но не ждем результата
1163
- this.logger('debug', `Автоматическое присоединение к рассуждению: ${reasoningId}`);
1164
- client.emitWithAck(WsEvents.JOIN_REASONING, {
1165
- reasoningId,
1166
- token: this.options.apiKey
1167
- }, 10000).catch(err => {
1168
- this.logger('warn', `Не удалось автоматически присоединиться к рассуждению: ${err.message}`);
1169
- });
1170
- }
1171
- return true;
396
+ getActiveProjectId() {
397
+ return this.indexingClient.getActiveProjectId();
1172
398
  }
1173
399
  /**
1174
- * Устанавливает ID активного проекта
1175
- * @param {string} projectId ID проекта
1176
- * @returns {boolean} Успешность установки
400
+ * Отправляет сообщение в пространство имен
401
+ * @param {WebSocketNamespace} namespace Пространство имен
402
+ * @param {string} eventType Тип события
403
+ * @param {any} [data] Данные сообщения
404
+ * @returns {boolean} Успешно ли отправлено сообщение
1177
405
  */
1178
- setActiveProjectId(projectId) {
1179
- if (!projectId) {
1180
- this.logger('error', 'Попытка установить пустой projectId');
1181
- return false;
406
+ send(namespace, eventType, data) {
407
+ switch (namespace) {
408
+ case WebSocketNamespace.REASONING:
409
+ return this.reasoningClient.send(eventType, data);
410
+ case WebSocketNamespace.INDEXING:
411
+ return this.indexingClient.send(eventType, data);
412
+ case WebSocketNamespace.DEPENDENCIES:
413
+ return this.dependenciesClient.send(eventType, data);
414
+ case WebSocketNamespace.DEFAULT:
415
+ return this.notificationsClient.send(eventType, data);
416
+ default:
417
+ throw new Error(`Неизвестное пространство имен ${namespace}`);
1182
418
  }
1183
- this.activeProjectId = projectId;
1184
- this.logger('info', `Установлен активный projectId: ${projectId}`);
1185
- return true;
1186
419
  }
1187
420
  /**
1188
- * Устанавливает ID активной сессии рассуждения с расширенными возможностями
1189
- * @param {string} reasoningId ID сессии рассуждения
1190
- * @param {boolean} waitForJoin Дождаться результата присоединения
1191
- * @param {boolean} createIfNotExists Создать новое рассуждение, если ID не существует
1192
- * @returns {Promise<boolean>} Результат операции
421
+ * Отправляет сообщение в активную сессию рассуждения
422
+ * @param {string} eventType Тип события
423
+ * @param {any} [data] Данные сообщения
424
+ * @returns {boolean} Успешно ли отправлено сообщение
1193
425
  */
1194
- async setActiveReasoningIdAsync(reasoningId, waitForJoin = false, createIfNotExists = false) {
1195
- if (!reasoningId && !createIfNotExists) {
1196
- this.logger('error', 'Попытка установить пустой reasoningId');
1197
- return false;
1198
- }
1199
- // Если указан createIfNotExists и нет reasoningId, создаем новое рассуждение
1200
- if (createIfNotExists && !reasoningId) {
1201
- try {
1202
- reasoningId = await this.createNewReasoning();
1203
- this.logger('info', `Создано новое рассуждение с ID: ${reasoningId}`);
1204
- }
1205
- catch (error) {
1206
- this.logger('error', 'Не удалось создать новое рассуждение', error);
1207
- return false;
1208
- }
1209
- }
1210
- // Отключаемся от предыдущего рассуждения, если оно отличается
1211
- if (this.activeReasoningId && this.activeReasoningId !== reasoningId) {
1212
- this.logger('debug', `Отписываемся от предыдущего рассуждения: ${this.activeReasoningId}`);
1213
- this.unsubscribeFromThinking(this.activeReasoningId);
1214
- }
1215
- this.activeReasoningId = reasoningId;
1216
- this.logger('info', `Установлен активный reasoningId: ${reasoningId}`);
1217
- // Если соединение уже установлено, присоединяемся к рассуждению
1218
- const client = this.clients.get(WebSocketNamespace.REASONING);
1219
- if (client && client.isConnected()) {
1220
- try {
1221
- if (waitForJoin) {
1222
- this.logger('debug', `Ожидание присоединения к рассуждению: ${reasoningId}`);
1223
- const joinResult = await client.emitWithAck(WsEvents.JOIN_REASONING, {
1224
- reasoningId,
1225
- token: this.options.apiKey
1226
- }, 10000);
1227
- if (joinResult.success === true) {
1228
- this.logger('info', `Успешно присоединились к рассуждению: ${reasoningId}`);
1229
- return true;
1230
- }
1231
- else {
1232
- this.logger('warn', `Не удалось присоединиться к рассуждению: ${reasoningId}`, joinResult.error);
1233
- return false;
1234
- }
1235
- }
1236
- else {
1237
- // Асинхронное присоединение
1238
- this.logger('debug', `Асинхронное присоединение к рассуждению: ${reasoningId}`);
1239
- client.emitWithAck(WsEvents.JOIN_REASONING, {
1240
- reasoningId,
1241
- token: this.options.apiKey
1242
- }, 10000).then(result => {
1243
- if (result.success === true) {
1244
- this.logger('info', `Успешно присоединились к рассуждению: ${reasoningId}`);
1245
- }
1246
- else {
1247
- this.logger('warn', `Не удалось присоединиться к рассуждению: ${reasoningId}`, result.error);
1248
- }
1249
- }).catch(err => {
1250
- this.logger('warn', `Ошибка при присоединении к рассуждению: ${reasoningId}`, err);
1251
- });
1252
- }
1253
- }
1254
- catch (error) {
1255
- this.logger('error', `Ошибка при присоединении к рассуждению: ${reasoningId}`, error);
1256
- if (waitForJoin)
1257
- return false;
1258
- }
1259
- }
1260
- else if (waitForJoin) {
1261
- // Если ждем присоединения, но соединение отсутствует
1262
- this.logger('warn', `Нет активного соединения с сервером для присоединения к рассуждению: ${reasoningId}`);
1263
- return false;
1264
- }
1265
- return true;
426
+ sendToReasoning(eventType, data) {
427
+ return this.reasoningClient.send(eventType, data);
1266
428
  }
1267
429
  /**
1268
- * Создает новое рассуждение на сервере
1269
- * @private
1270
- * @returns {Promise<string>} ID нового рассуждения
430
+ * Отправляет сообщение в активную сессию индексации
431
+ * @param {string} eventType Тип события
432
+ * @param {any} [data] Данные сообщения
433
+ * @returns {boolean} Успешно ли отправлено сообщение
1271
434
  */
1272
- async createNewReasoning() {
1273
- // Подключаемся, если еще не подключены
1274
- if (!this.isConnectedToReasoning()) {
1275
- this.logger('debug', 'Подключение к пространству имен рассуждений для создания нового рассуждения');
1276
- const connected = await this.connectToReasoning();
1277
- if (!connected) {
1278
- throw new Error('Не удалось подключиться к пространству имен рассуждений');
1279
- }
1280
- }
1281
- const client = this.clients.get(WebSocketNamespace.REASONING);
1282
- if (!client) {
1283
- throw new Error('Не удалось получить клиент для пространства имен рассуждений');
1284
- }
1285
- this.logger('debug', 'Отправка запроса на создание нового рассуждения');
1286
- const result = await client.emitWithAck(WsEvents.CREATE_REASONING, {
1287
- token: this.options.apiKey
1288
- }, 10000);
1289
- if (!result.reasoningId) {
1290
- throw new Error(`Сервер не вернул ID рассуждения: ${JSON.stringify(result)}`);
1291
- }
1292
- return result.reasoningId;
435
+ sendToIndexing(eventType, data) {
436
+ return this.indexingClient.send(eventType, data);
1293
437
  }
1294
438
  /**
1295
- * Проверяет существование рассуждения на сервере
1296
- * @param {string} reasoningId ID рассуждения для проверки
1297
- * @returns {Promise<boolean>} Существует ли рассуждение
439
+ * Отправляет сообщение в уведомления
440
+ * @param {string} eventType Тип события
441
+ * @param {any} [data] Данные сообщения
442
+ * @returns {boolean} Успешно ли отправлено сообщение
1298
443
  */
1299
- async checkReasoningExists(reasoningId) {
1300
- if (!reasoningId) {
1301
- return false;
1302
- }
1303
- // Подключаемся, если еще не подключены
1304
- if (!this.isConnectedToReasoning()) {
1305
- const connected = await this.connectToReasoning();
1306
- if (!connected) {
1307
- this.logger('warn', 'Не удалось подключиться к пространству имен рассуждений для проверки существования');
1308
- return false;
1309
- }
1310
- }
1311
- const client = this.clients.get(WebSocketNamespace.REASONING);
1312
- if (!client) {
1313
- this.logger('warn', 'Не удалось получить клиент для пространства имен рассуждений');
1314
- return false;
1315
- }
1316
- try {
1317
- this.logger('debug', `Проверка существования рассуждения: ${reasoningId}`);
1318
- // Используем более правильный подход для проверки существования сессии
1319
- // Пытаемся присоединиться, и если успешно - значит сессия существует
1320
- const result = await client.emitWithAck(WsEvents.JOIN_REASONING, {
1321
- reasoningId,
1322
- token: this.options.apiKey
1323
- }, 10000);
1324
- return result.success === true;
1325
- }
1326
- catch (error) {
1327
- this.logger('warn', `Ошибка при проверке существования рассуждения: ${reasoningId}`, error);
1328
- return false;
1329
- }
444
+ sendToNotifications(eventType, data) {
445
+ return this.notificationsClient.send(eventType, data);
1330
446
  }
1331
447
  /**
1332
- * Подключается к пространству имен рассуждений
1333
- * @param reasoningId ID рассуждения (опционально)
1334
- * @param options Дополнительные настройки подключения
1335
- * @returns Promise с результатом подключения
448
+ * Добавляет обработчик события для пространства имен
449
+ * @param {string} eventType Тип события
450
+ * @param {Function} handler Обработчик события
451
+ * @param {WebSocketNamespace} [namespace] Пространство имен (если не указано, добавляется ко всем активным)
1336
452
  */
1337
- async connectToReasoning(reasoningId, options = {}) {
1338
- try {
1339
- // Значения опций по умолчанию
1340
- const { autoJoin = true, createIfNotExists = false, checkExistence = false, saveSession = this.options.enableSessionPersistence !== false } = options;
1341
- this.logger('info', 'Подключение к пространству имен рассуждений', {
1342
- reasoningId,
1343
- options: { autoJoin, createIfNotExists, checkExistence, saveSession }
1344
- });
1345
- // Если указан ID рассуждения, обрабатываем его
1346
- if (reasoningId) {
1347
- // Если нужно проверить существование
1348
- if (checkExistence) {
1349
- const client = this.clients.get(WebSocketNamespace.REASONING);
1350
- // Если уже подключены, проверяем существование
1351
- if (client && client.isConnected()) {
1352
- const exists = await this.checkReasoningExists(reasoningId);
1353
- if (!exists) {
1354
- if (createIfNotExists) {
1355
- this.logger('info', `Рассуждение ${reasoningId} не существует, создаем новое`);
1356
- reasoningId = await this.createNewReasoning();
1357
- }
1358
- else {
1359
- this.logger('warn', `Рассуждение ${reasoningId} не существует`);
1360
- return false;
1361
- }
1362
- }
1363
- }
1364
- }
1365
- // Устанавливаем ID активного рассуждения
1366
- this.activeReasoningId = reasoningId;
1367
- }
1368
- else if (createIfNotExists) {
1369
- // Если нет ID, но нужно создать, создаем новое рассуждение
1370
- this.logger('info', 'Создание нового рассуждения');
1371
- try {
1372
- // Сначала подключаемся к пространству имен
1373
- const client = await this.connect(WebSocketNamespace.REASONING);
1374
- // Затем создаем новое рассуждение
1375
- const newReasoningId = await this.createNewReasoning();
1376
- this.activeReasoningId = newReasoningId;
1377
- this.logger('info', `Создано новое рассуждение: ${newReasoningId}`);
1378
- }
1379
- catch (error) {
1380
- this.logger('error', 'Ошибка при создании нового рассуждения', error);
1381
- return false;
1382
- }
1383
- }
1384
- // Подключаемся к пространству имен рассуждений
1385
- const client = await this.connect(WebSocketNamespace.REASONING);
1386
- // Аутентифицируемся с увеличенным таймаутом
1387
- try {
1388
- const authResult = await client.emitWithAck(WsEvents.AUTHENTICATE, {
1389
- token: this.options.apiKey,
1390
- reasoningId: this.activeReasoningId
1391
- }, 10000);
1392
- this.logger('debug', 'Результат аутентификации в namespace рассуждений', authResult);
1393
- // Если сервер вернул токен сессии и нужно его сохранить
1394
- if (saveSession && authResult.sessionToken) {
1395
- this.saveSessionToken(WebSocketNamespace.REASONING, authResult.sessionToken);
1396
- }
1397
- }
1398
- catch (error) {
1399
- this.logger('error', 'Ошибка аутентификации в namespace рассуждений', error);
1400
- return false;
1401
- }
1402
- // Если у нас есть ID рассуждения и включено автоматическое присоединение
1403
- if (this.activeReasoningId && autoJoin) {
1404
- try {
1405
- const joinResult = await client.emitWithAck(WsEvents.JOIN_REASONING, {
1406
- reasoningId: this.activeReasoningId,
1407
- token: this.options.apiKey
1408
- }, 10000);
1409
- this.logger('debug', 'Результат присоединения к рассуждению', joinResult);
1410
- if (joinResult.success !== true) {
1411
- this.logger('warn', `Не удалось присоединиться к рассуждению: ${this.activeReasoningId}`, joinResult.error);
1412
- return false;
1413
- }
1414
- }
1415
- catch (error) {
1416
- this.logger('error', 'Ошибка присоединения к рассуждению', error);
1417
- return false;
1418
- }
1419
- }
1420
- // Включаем автоматический ping/pong, если настроено
1421
- if (this.options.enableAutoPing !== false) {
1422
- this.enablePingPong(this.options.pingInterval, this.options.pingTimeoutThreshold);
453
+ on(eventType, handler, namespace) {
454
+ if (namespace) {
455
+ // Если указано пространство имен, добавляем обработчик только к нему
456
+ switch (namespace) {
457
+ case WebSocketNamespace.REASONING:
458
+ this.reasoningClient.on(eventType, handler);
459
+ break;
460
+ case WebSocketNamespace.INDEXING:
461
+ this.indexingClient.on(eventType, handler);
462
+ break;
463
+ case WebSocketNamespace.DEPENDENCIES:
464
+ this.dependenciesClient.on(eventType, handler);
465
+ break;
466
+ case WebSocketNamespace.DEFAULT:
467
+ this.notificationsClient.on(eventType, handler);
468
+ break;
1423
469
  }
1424
- return true;
1425
470
  }
1426
- catch (error) {
1427
- this.logger('error', 'Ошибка подключения к пространству имен рассуждений', error);
1428
- return false;
471
+ else {
472
+ // Если пространство имен не указано, добавляем обработчик ко всем активным
473
+ this.reasoningClient.on(eventType, handler);
474
+ this.indexingClient.on(eventType, handler);
475
+ this.dependenciesClient.on(eventType, handler);
476
+ this.notificationsClient.on(eventType, handler);
1429
477
  }
1430
478
  }
1431
479
  /**
1432
- * Получает текущий статус сессии рассуждения
1433
- * @param {string} reasoningId ID сессии рассуждения (опционально, по умолчанию активная)
1434
- * @returns {Promise<{exists: boolean, isActive: boolean, metadata?: any}>} Статус сессии
480
+ * Удаляет обработчик события для пространства имен
481
+ * @param {string} eventType Тип события
482
+ * @param {Function} [handler] Обработчик события (если не указан, удаляются все обработчики)
483
+ * @param {WebSocketNamespace} [namespace] Пространство имен (если не указано, удаляется из всех активных)
1435
484
  */
1436
- async getReasoningStatus(reasoningId) {
1437
- const targetId = reasoningId || this.activeReasoningId;
1438
- if (!targetId) {
1439
- return { exists: false, isActive: false };
1440
- }
1441
- if (!this.isConnectedToReasoning()) {
1442
- const connected = await this.connectToReasoning(undefined, { autoJoin: false });
1443
- if (!connected) {
1444
- this.logger('warn', 'Не удалось подключиться к пространству имен рассуждений');
1445
- return { exists: false, isActive: false };
1446
- }
1447
- }
1448
- const client = this.clients.get(WebSocketNamespace.REASONING);
1449
- if (!client) {
1450
- return { exists: false, isActive: false };
1451
- }
1452
- try {
1453
- // Используем событие GET_REASONING_STATUS для получения статуса
1454
- // Если сервер не поддерживает это событие, используем более простую проверку
1455
- try {
1456
- const status = await client.emitWithAck('get_reasoning_status', {
1457
- reasoningId: targetId,
1458
- token: this.options.apiKey
1459
- }, 10000);
1460
- return {
1461
- exists: status.exists === true,
1462
- isActive: status.isActive === true,
1463
- metadata: status.metadata || {}
1464
- };
1465
- }
1466
- catch (e) {
1467
- // Если произошла ошибка (скорее всего, сервер не поддерживает этот метод),
1468
- // используем проверку через JOIN_REASONING
1469
- const result = await client.emitWithAck(WsEvents.JOIN_REASONING, {
1470
- reasoningId: targetId,
1471
- token: this.options.apiKey
1472
- }, 10000);
1473
- return {
1474
- exists: result.success === true,
1475
- isActive: result.success === true,
1476
- metadata: result.data
1477
- };
485
+ off(eventType, handler, namespace) {
486
+ if (namespace) {
487
+ // Если указано пространство имен, удаляем обработчик только из него
488
+ switch (namespace) {
489
+ case WebSocketNamespace.REASONING:
490
+ this.reasoningClient.off(eventType, handler);
491
+ break;
492
+ case WebSocketNamespace.INDEXING:
493
+ this.indexingClient.off(eventType, handler);
494
+ break;
495
+ case WebSocketNamespace.DEPENDENCIES:
496
+ this.dependenciesClient.off(eventType, handler);
497
+ break;
498
+ case WebSocketNamespace.DEFAULT:
499
+ this.notificationsClient.off(eventType, handler);
500
+ break;
1478
501
  }
1479
502
  }
1480
- catch (error) {
1481
- this.logger('warn', `Ошибка при получении статуса рассуждения: ${targetId}`, error);
1482
- return { exists: false, isActive: false };
503
+ else {
504
+ // Если пространство имен не указано, удаляем обработчик из всех активных
505
+ this.reasoningClient.off(eventType, handler);
506
+ this.indexingClient.off(eventType, handler);
507
+ this.dependenciesClient.off(eventType, handler);
508
+ this.notificationsClient.off(eventType, handler);
1483
509
  }
1484
510
  }
1485
511
  /**
1486
- * Присоединяется к сессии рассуждения
1487
- * @param {string} reasoningId ID сессии рассуждения
1488
- * @param {boolean} setActive Установить как активное рассуждение
1489
- * @returns {Promise<boolean>} Результат операции
512
+ * Получает ID сокета для указанного пространства имен
513
+ * @param {WebSocketNamespace} [namespace=WebSocketNamespace.REASONING] Пространство имен
514
+ * @returns {string|null} ID сокета или null, если соединение не установлено
1490
515
  */
1491
- async joinReasoning(reasoningId, setActive = true) {
1492
- if (!reasoningId) {
1493
- this.logger('error', 'Попытка присоединиться к пустому reasoningId');
1494
- return false;
1495
- }
1496
- // Если нужно установить как активное, делаем это
1497
- if (setActive) {
1498
- this.activeReasoningId = reasoningId;
1499
- }
1500
- // Если не подключены, подключаемся сначала
1501
- if (!this.isConnectedToReasoning()) {
1502
- const connected = await this.connectToReasoning(reasoningId);
1503
- if (!connected) {
1504
- this.logger('warn', `Не удалось подключиться к пространству имен рассуждений для ${reasoningId}`);
1505
- return false;
1506
- }
1507
- return true; // connectToReasoning уже выполнил join
1508
- }
1509
- // Если уже подключены, отправляем join запрос
1510
- const client = this.clients.get(WebSocketNamespace.REASONING);
1511
- if (!client) {
1512
- this.logger('warn', 'Не удалось получить клиент для пространства имен рассуждений');
1513
- return false;
1514
- }
1515
- try {
1516
- const joinResult = await client.emitWithAck(WsEvents.JOIN_REASONING, {
1517
- reasoningId,
1518
- token: this.options.apiKey
1519
- }, 10000);
1520
- this.logger('debug', `Результат присоединения к рассуждению ${reasoningId}`, joinResult);
1521
- return joinResult.success === true;
1522
- }
1523
- catch (error) {
1524
- this.logger('error', `Ошибка при присоединении к рассуждению ${reasoningId}`, error);
1525
- return false;
516
+ getSocketId(namespace = WebSocketNamespace.REASONING) {
517
+ switch (namespace) {
518
+ case WebSocketNamespace.REASONING:
519
+ return this.reasoningClient.getSocketId();
520
+ case WebSocketNamespace.INDEXING:
521
+ return this.indexingClient.getSocketId();
522
+ case WebSocketNamespace.DEPENDENCIES:
523
+ return this.dependenciesClient.getSocketId();
524
+ case WebSocketNamespace.DEFAULT:
525
+ return this.notificationsClient.getSocketId();
526
+ default:
527
+ return null;
1526
528
  }
1527
529
  }
1528
530
  /**
1529
- * Подключается к пространству имен рассуждений с расширенными опциями
1530
- * @param reasoningId ID рассуждения (опционально)
1531
- * @param options Дополнительные настройки подключения
1532
- * @returns Promise с результатом подключения
531
+ * Получает диагностическую информацию о соединениях
532
+ * @returns {object} Диагностическая информация
1533
533
  */
1534
- async connectToReasoningEx(reasoningId, options = {}) {
1535
- try {
1536
- // Значения опций по умолчанию
1537
- const { autoJoin = true, createIfNotExists = false, checkExistence = false, saveSession = this.options.enableSessionPersistence !== false } = options;
1538
- this.logger('info', 'Подключение к пространству имен рассуждений', {
1539
- reasoningId,
1540
- options: { autoJoin, createIfNotExists, checkExistence, saveSession }
1541
- });
1542
- // Если указан ID рассуждения, обрабатываем его
1543
- if (reasoningId) {
1544
- // Если нужно проверить существование
1545
- if (checkExistence) {
1546
- const client = this.clients.get(WebSocketNamespace.REASONING);
1547
- // Если уже подключены, проверяем существование
1548
- if (client && client.isConnected()) {
1549
- const exists = await this.checkReasoningExists(reasoningId);
1550
- if (!exists) {
1551
- if (createIfNotExists) {
1552
- this.logger('info', `Рассуждение ${reasoningId} не существует, создаем новое`);
1553
- reasoningId = await this.createNewReasoning();
1554
- }
1555
- else {
1556
- this.logger('warn', `Рассуждение ${reasoningId} не существует`);
1557
- return false;
1558
- }
1559
- }
1560
- }
1561
- }
1562
- // Устанавливаем ID активного рассуждения
1563
- this.activeReasoningId = reasoningId;
1564
- }
1565
- else if (createIfNotExists) {
1566
- // Если нет ID, но нужно создать, создаем новое рассуждение
1567
- this.logger('info', 'Создание нового рассуждения');
1568
- try {
1569
- // Сначала подключаемся к пространству имен
1570
- const client = await this.connect(WebSocketNamespace.REASONING);
1571
- // Затем создаем новое рассуждение
1572
- const newReasoningId = await this.createNewReasoning();
1573
- this.activeReasoningId = newReasoningId;
1574
- this.logger('info', `Создано новое рассуждение: ${newReasoningId}`);
1575
- }
1576
- catch (error) {
1577
- this.logger('error', 'Ошибка при создании нового рассуждения', error);
1578
- return false;
1579
- }
1580
- }
1581
- // Подключаемся к пространству имен рассуждений
1582
- const client = await this.connect(WebSocketNamespace.REASONING);
1583
- // Аутентифицируемся с увеличенным таймаутом
1584
- try {
1585
- const authResult = await client.emitWithAck(WsEvents.AUTHENTICATE, {
1586
- token: this.options.apiKey,
1587
- reasoningId: this.activeReasoningId
1588
- }, 10000);
1589
- this.logger('debug', 'Результат аутентификации в namespace рассуждений', authResult);
1590
- // Если сервер вернул токен сессии и нужно его сохранить
1591
- if (saveSession && authResult.sessionToken) {
1592
- this.saveSessionToken(WebSocketNamespace.REASONING, authResult.sessionToken);
1593
- }
1594
- }
1595
- catch (error) {
1596
- this.logger('error', 'Ошибка аутентификации в namespace рассуждений', error);
1597
- return false;
1598
- }
1599
- // Если у нас есть ID рассуждения и включено автоматическое присоединение
1600
- if (this.activeReasoningId && autoJoin) {
1601
- try {
1602
- const joinResult = await client.emitWithAck(WsEvents.JOIN_REASONING, {
1603
- reasoningId: this.activeReasoningId,
1604
- token: this.options.apiKey
1605
- }, 10000);
1606
- this.logger('debug', 'Результат присоединения к рассуждению', joinResult);
1607
- if (joinResult.success !== true) {
1608
- this.logger('warn', `Не удалось присоединиться к рассуждению: ${this.activeReasoningId}`, joinResult.error);
1609
- return false;
1610
- }
1611
- }
1612
- catch (error) {
1613
- this.logger('error', 'Ошибка присоединения к рассуждению', error);
1614
- return false;
1615
- }
1616
- }
1617
- // Включаем автоматический ping/pong, если настроено
1618
- if (this.options.enableAutoPing !== false) {
1619
- this.enablePingPong(this.options.pingInterval, this.options.pingTimeoutThreshold);
1620
- }
1621
- return true;
1622
- }
1623
- catch (error) {
1624
- this.logger('error', 'Ошибка подключения к пространству имен рассуждений', error);
1625
- return false;
1626
- }
534
+ getDiagnostics() {
535
+ return this.diagnosticsService.getDiagnostics();
1627
536
  }
1628
537
  }
1629
538
  //# sourceMappingURL=code-solver-websocket-client.js.map