agentgui 1.0.211 → 1.0.213

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,34 +1,40 @@
1
- /**
2
- * WebSocket Manager
3
- * Handles WebSocket connection, auto-reconnect, message buffering,
4
- * and event distribution for streaming events
5
- */
6
-
7
1
  class WebSocketManager {
8
2
  constructor(config = {}) {
9
- // Configuration
10
3
  this.config = {
11
4
  url: config.url || this.getWebSocketURL(),
12
- reconnectDelays: config.reconnectDelays || [1000, 2000, 4000, 8000, 16000],
5
+ reconnectDelays: config.reconnectDelays || [500, 1000, 2000, 4000, 8000, 15000, 30000],
13
6
  maxReconnectDelay: config.maxReconnectDelay || 30000,
14
- heartbeatInterval: config.heartbeatInterval || 30000,
7
+ heartbeatInterval: config.heartbeatInterval || 15000,
15
8
  messageTimeout: config.messageTimeout || 60000,
16
9
  maxBufferedMessages: config.maxBufferedMessages || 1000,
10
+ pongTimeout: config.pongTimeout || 5000,
11
+ latencyWindowSize: config.latencyWindowSize || 10,
17
12
  ...config
18
13
  };
19
14
 
20
- // State
21
15
  this.ws = null;
22
16
  this.isConnected = false;
23
17
  this.isConnecting = false;
18
+ this.isManuallyDisconnected = false;
24
19
  this.reconnectCount = 0;
20
+ this.reconnectTimer = null;
25
21
  this.messageBuffer = [];
26
22
  this.requestMap = new Map();
27
23
  this.heartbeatTimer = null;
28
24
  this.connectionState = 'disconnected';
29
25
  this.activeSubscriptions = new Set();
26
+ this.connectionEstablishedAt = 0;
27
+
28
+ this.latency = {
29
+ samples: [],
30
+ current: 0,
31
+ avg: 0,
32
+ jitter: 0,
33
+ quality: 'unknown',
34
+ missedPongs: 0,
35
+ pingCounter: 0
36
+ };
30
37
 
31
- // Statistics
32
38
  this.stats = {
33
39
  totalConnections: 0,
34
40
  totalReconnects: 0,
@@ -41,100 +47,77 @@ class WebSocketManager {
41
47
  connectionDuration: 0
42
48
  };
43
49
 
44
- // Event listeners
50
+ this.lastSeqBySession = {};
45
51
  this.listeners = {};
52
+
53
+ this._onVisibilityChange = this._handleVisibilityChange.bind(this);
54
+ this._onOnline = this._handleOnline.bind(this);
55
+ if (typeof document !== 'undefined') {
56
+ document.addEventListener('visibilitychange', this._onVisibilityChange);
57
+ }
58
+ if (typeof window !== 'undefined') {
59
+ window.addEventListener('online', this._onOnline);
60
+ }
46
61
  }
47
62
 
48
- /**
49
- * Get WebSocket URL from current window location
50
- */
51
63
  getWebSocketURL() {
52
64
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
53
65
  const baseURL = window.__BASE_URL || '/gm';
54
66
  return `${protocol}//${window.location.host}${baseURL}/sync`;
55
67
  }
56
68
 
57
- /**
58
- * Connect to WebSocket server
59
- */
60
69
  async connect() {
61
- if (this.isConnected || this.isConnecting) {
62
- return this.ws;
63
- }
64
-
70
+ if (this.isConnected || this.isConnecting) return this.ws;
71
+ this.isManuallyDisconnected = false;
65
72
  this.isConnecting = true;
66
73
  this.setConnectionState('connecting');
67
74
 
68
75
  try {
69
- console.log('WebSocket connecting to:', this.config.url);
70
-
71
76
  this.ws = new WebSocket(this.config.url);
72
-
73
77
  this.ws.onopen = () => this.onOpen();
74
78
  this.ws.onmessage = (event) => this.onMessage(event);
75
79
  this.ws.onerror = (error) => this.onError(error);
76
80
  this.ws.onclose = () => this.onClose();
77
-
78
- // Wait for connection with timeout
79
81
  return await this.waitForConnection(this.config.messageTimeout);
80
82
  } catch (error) {
81
- console.error('WebSocket connection error:', error);
82
83
  this.isConnecting = false;
83
84
  this.stats.totalErrors++;
84
- await this.scheduleReconnect();
85
+ this.scheduleReconnect();
85
86
  throw error;
86
87
  }
87
88
  }
88
89
 
89
- /**
90
- * Wait for connection to establish
91
- */
92
90
  waitForConnection(timeout = 5000) {
93
91
  return new Promise((resolve, reject) => {
94
- const timer = setTimeout(() => {
95
- reject(new Error('WebSocket connection timeout'));
96
- }, timeout);
97
-
98
- const checkConnection = () => {
99
- if (this.isConnected) {
100
- clearTimeout(timer);
101
- resolve(this.ws);
102
- } else if (this.ws?.readyState === WebSocket.OPEN) {
92
+ const timer = setTimeout(() => reject(new Error('WebSocket connection timeout')), timeout);
93
+ const check = () => {
94
+ if (this.isConnected || this.ws?.readyState === WebSocket.OPEN) {
103
95
  clearTimeout(timer);
104
96
  resolve(this.ws);
105
97
  } else {
106
- setTimeout(checkConnection, 50);
98
+ setTimeout(check, 50);
107
99
  }
108
100
  };
109
-
110
- checkConnection();
101
+ check();
111
102
  });
112
103
  }
113
104
 
114
- /**
115
- * Handle WebSocket open
116
- */
117
105
  onOpen() {
118
- console.log('WebSocket connected');
119
106
  this.isConnected = true;
120
107
  this.isConnecting = false;
121
- this.reconnectCount = 0;
108
+ this.connectionEstablishedAt = Date.now();
122
109
  this.stats.totalConnections++;
123
110
  this.stats.lastConnectedTime = Date.now();
111
+ this.latency.missedPongs = 0;
124
112
  this.setConnectionState('connected');
125
113
 
126
- // Flush buffered messages
127
114
  this.flushMessageBuffer();
128
115
  this.resubscribeAll();
129
-
130
116
  this.startHeartbeat();
131
117
 
132
118
  this.emit('connected', { timestamp: Date.now() });
133
119
  }
134
120
 
135
- /**
136
- * Handle WebSocket message
137
- */
138
121
  onMessage(event) {
139
122
  try {
140
123
  const parsed = JSON.parse(event.data);
@@ -143,139 +126,221 @@ class WebSocketManager {
143
126
 
144
127
  for (const data of messages) {
145
128
  if (data.type === 'pong') {
146
- const requestId = data.requestId;
147
- if (requestId && this.requestMap.has(requestId)) {
148
- const request = this.requestMap.get(requestId);
149
- request.resolve({ latency: Date.now() - request.sentTime });
150
- this.requestMap.delete(requestId);
151
- }
129
+ this._handlePong(data);
152
130
  continue;
153
131
  }
154
132
 
133
+ if (data.seq !== undefined && data.sessionId) {
134
+ this.lastSeqBySession[data.sessionId] = Math.max(
135
+ this.lastSeqBySession[data.sessionId] || -1, data.seq
136
+ );
137
+ }
138
+
155
139
  this.emit('message', data);
156
- if (data.type) this.emit(`message:${data.type}`, data);
140
+ if (data.type) this.emit('message:' + data.type, data);
157
141
  }
158
142
  } catch (error) {
159
- console.error('WebSocket message parse error:', error);
160
143
  this.stats.totalErrors++;
161
144
  }
162
145
  }
163
146
 
164
- /**
165
- * Handle WebSocket error
166
- */
147
+ _handlePong(data) {
148
+ this.latency.missedPongs = 0;
149
+ const requestId = data.requestId;
150
+ if (requestId && this.requestMap.has(requestId)) {
151
+ const request = this.requestMap.get(requestId);
152
+ const rtt = Date.now() - request.sentTime;
153
+ this.requestMap.delete(requestId);
154
+ this._recordLatency(rtt);
155
+ if (request.resolve) request.resolve({ latency: rtt });
156
+ }
157
+ }
158
+
159
+ _recordLatency(rtt) {
160
+ const samples = this.latency.samples;
161
+ samples.push(rtt);
162
+ if (samples.length > this.config.latencyWindowSize) samples.shift();
163
+
164
+ this.latency.current = rtt;
165
+ this.latency.avg = samples.reduce((a, b) => a + b, 0) / samples.length;
166
+
167
+ if (samples.length > 1) {
168
+ const mean = this.latency.avg;
169
+ const variance = samples.reduce((sum, s) => sum + Math.pow(s - mean, 2), 0) / samples.length;
170
+ this.latency.jitter = Math.sqrt(variance);
171
+ }
172
+
173
+ const prev = this.latency.quality;
174
+ this.latency.quality = this._qualityTier(this.latency.avg);
175
+ this.stats.avgLatency = this.latency.avg;
176
+
177
+ this.emit('latency_update', {
178
+ latency: rtt,
179
+ avg: this.latency.avg,
180
+ jitter: this.latency.jitter,
181
+ quality: this.latency.quality
182
+ });
183
+
184
+ if (rtt > this.latency.avg * 3 && samples.length >= 3) {
185
+ this.emit('latency_spike', { latency: rtt, avg: this.latency.avg });
186
+ }
187
+ }
188
+
189
+ _qualityTier(avg) {
190
+ if (avg < 50) return 'excellent';
191
+ if (avg < 150) return 'good';
192
+ if (avg < 300) return 'fair';
193
+ if (avg < 500) return 'poor';
194
+ return 'bad';
195
+ }
196
+
167
197
  onError(error) {
168
- console.error('WebSocket error:', error);
169
198
  this.stats.totalErrors++;
170
199
  this.emit('error', { error, timestamp: Date.now() });
171
200
  }
172
201
 
173
- /**
174
- * Handle WebSocket close
175
- */
176
202
  onClose() {
177
- console.log('WebSocket disconnected');
178
203
  this.isConnected = false;
179
204
  this.isConnecting = false;
180
205
  this.setConnectionState('disconnected');
206
+ this.stopHeartbeat();
181
207
 
182
- // Stop heartbeat
183
- if (this.heartbeatTimer) {
184
- clearTimeout(this.heartbeatTimer);
185
- }
186
-
187
- // Update connection duration
188
208
  if (this.stats.lastConnectedTime) {
189
209
  this.stats.connectionDuration = Date.now() - this.stats.lastConnectedTime;
190
210
  }
191
211
 
192
212
  this.emit('disconnected', { timestamp: Date.now() });
193
213
 
194
- // Attempt reconnect
195
214
  if (!this.isManuallyDisconnected) {
196
215
  this.scheduleReconnect();
197
216
  }
198
217
  }
199
218
 
200
- /**
201
- * Schedule reconnection with exponential backoff
202
- */
203
- async scheduleReconnect() {
204
- if (this.reconnectCount >= this.config.reconnectDelays.length) {
205
- this.setConnectionState('reconnect_failed');
206
- console.error('Max reconnection attempts reached');
207
- this.emit('reconnect_failed', { attempts: this.reconnectCount });
208
- return;
209
- }
219
+ scheduleReconnect() {
220
+ if (this.isManuallyDisconnected) return;
221
+ if (this.reconnectTimer) return;
222
+
223
+ const delays = this.config.reconnectDelays;
224
+ const baseDelay = this.reconnectCount < delays.length
225
+ ? delays[this.reconnectCount]
226
+ : this.config.maxReconnectDelay;
227
+
228
+ const jitter = Math.random() * 0.3 * baseDelay;
229
+ const delay = Math.round(baseDelay + jitter);
210
230
 
211
- const delay = this.config.reconnectDelays[this.reconnectCount];
212
231
  this.reconnectCount++;
213
232
  this.stats.totalReconnects++;
214
233
  this.setConnectionState('reconnecting');
215
234
 
216
- console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectCount}/${this.config.reconnectDelays.length})`);
217
-
218
- this.emit('reconnecting', { delay, attempt: this.reconnectCount });
219
-
220
- return new Promise((resolve) => {
221
- setTimeout(() => {
222
- this.connect().catch((error) => {
223
- console.error('Reconnection attempt failed:', error);
224
- });
225
- resolve();
226
- }, delay);
235
+ this.emit('reconnecting', {
236
+ delay,
237
+ attempt: this.reconnectCount,
238
+ nextAttemptAt: Date.now() + delay
227
239
  });
240
+
241
+ this.reconnectTimer = setTimeout(() => {
242
+ this.reconnectTimer = null;
243
+ this.connect().catch(() => {});
244
+ }, delay);
228
245
  }
229
246
 
230
- /**
231
- * Start heartbeat/keepalive
232
- */
233
247
  startHeartbeat() {
234
- if (this.heartbeatTimer) clearTimeout(this.heartbeatTimer);
248
+ this.stopHeartbeat();
249
+ const tick = () => {
250
+ if (!this.isConnected) return;
251
+ if (typeof document !== 'undefined' && document.hidden) {
252
+ this.heartbeatTimer = setTimeout(tick, this.config.heartbeatInterval);
253
+ return;
254
+ }
255
+ this.latency.pingCounter++;
256
+ this.ping().catch(() => {
257
+ this.latency.missedPongs++;
258
+ if (this.latency.missedPongs >= 3) {
259
+ this.latency.missedPongs = 0;
260
+ if (this.ws) {
261
+ try { this.ws.close(); } catch (_) {}
262
+ }
263
+ }
264
+ });
265
+ if (this.latency.pingCounter % 10 === 0) {
266
+ this._reportLatency();
267
+ }
268
+ this.heartbeatTimer = setTimeout(tick, this.config.heartbeatInterval);
269
+ };
270
+ this.heartbeatTimer = setTimeout(tick, this.config.heartbeatInterval);
271
+ }
272
+
273
+ stopHeartbeat() {
274
+ if (this.heartbeatTimer) {
275
+ clearTimeout(this.heartbeatTimer);
276
+ this.heartbeatTimer = null;
277
+ }
278
+ }
279
+
280
+ _reportLatency() {
281
+ if (this.latency.avg > 0) {
282
+ this.sendMessage({
283
+ type: 'latency_report',
284
+ avg: Math.round(this.latency.avg),
285
+ jitter: Math.round(this.latency.jitter),
286
+ quality: this.latency.quality
287
+ });
288
+ }
289
+ }
290
+
291
+ _handleVisibilityChange() {
292
+ if (typeof document !== 'undefined' && document.hidden) return;
293
+ if (!this.isConnected && !this.isConnecting && !this.isManuallyDisconnected) {
294
+ if (this.reconnectTimer) {
295
+ clearTimeout(this.reconnectTimer);
296
+ this.reconnectTimer = null;
297
+ }
298
+ this.connect().catch(() => {});
299
+ }
300
+ if (this.isConnected) {
301
+ const stableFor = Date.now() - this.connectionEstablishedAt;
302
+ if (stableFor > 10000) this.reconnectCount = 0;
303
+ }
304
+ }
305
+
306
+ _handleOnline() {
307
+ if (!this.isConnected && !this.isConnecting && !this.isManuallyDisconnected) {
308
+ if (this.reconnectTimer) {
309
+ clearTimeout(this.reconnectTimer);
310
+ this.reconnectTimer = null;
311
+ }
312
+ this.connect().catch(() => {});
313
+ }
235
314
  }
236
315
 
237
- /**
238
- * Send ping message
239
- */
240
316
  ping() {
241
- const requestId = `ping-${Date.now()}-${Math.random()}`;
242
- const request = {
243
- sentTime: Date.now(),
244
- resolve: null
245
- };
317
+ const requestId = 'ping-' + Date.now() + '-' + Math.random();
318
+ const request = { sentTime: Date.now(), resolve: null };
246
319
 
247
- const promise = new Promise((resolve) => {
320
+ const promise = new Promise((resolve, reject) => {
248
321
  request.resolve = resolve;
322
+ setTimeout(() => {
323
+ if (this.requestMap.has(requestId)) {
324
+ this.stats.totalTimeouts++;
325
+ this.requestMap.delete(requestId);
326
+ reject(new Error('ping timeout'));
327
+ }
328
+ }, this.config.pongTimeout);
249
329
  });
250
330
 
251
331
  this.requestMap.set(requestId, request);
252
-
253
- // Timeout if no response
254
- setTimeout(() => {
255
- if (this.requestMap.has(requestId)) {
256
- this.stats.totalTimeouts++;
257
- this.requestMap.delete(requestId);
258
- this.emit('ping_timeout', { requestId });
259
- }
260
- }, 5000);
261
-
262
332
  this.sendMessage({ type: 'ping', requestId });
263
333
  return promise;
264
334
  }
265
335
 
266
- /**
267
- * Send message through WebSocket
268
- */
269
336
  sendMessage(data) {
270
- if (!data || typeof data !== 'object') {
271
- throw new Error('Invalid message data');
272
- }
337
+ if (!data || typeof data !== 'object') throw new Error('Invalid message data');
273
338
 
274
339
  if (data.type === 'subscribe') {
275
- const key = data.sessionId ? `session:${data.sessionId}` : `conv:${data.conversationId}`;
340
+ const key = data.sessionId ? 'session:' + data.sessionId : 'conv:' + data.conversationId;
276
341
  this.activeSubscriptions.add(key);
277
342
  } else if (data.type === 'unsubscribe') {
278
- const key = data.sessionId ? `session:${data.sessionId}` : `conv:${data.conversationId}`;
343
+ const key = data.sessionId ? 'session:' + data.sessionId : 'conv:' + data.conversationId;
279
344
  this.activeSubscriptions.delete(key);
280
345
  }
281
346
 
@@ -289,57 +354,37 @@ class WebSocketManager {
289
354
  this.stats.totalMessagesSent++;
290
355
  return true;
291
356
  } catch (error) {
292
- console.error('WebSocket send error:', error);
293
357
  this.stats.totalErrors++;
294
358
  this.bufferMessage(data);
295
359
  return false;
296
360
  }
297
361
  }
298
362
 
299
- /**
300
- * Buffer message for sending when connected
301
- */
302
363
  bufferMessage(data) {
303
364
  if (this.messageBuffer.length >= this.config.maxBufferedMessages) {
304
- console.warn('Message buffer full, dropping oldest message');
305
365
  this.messageBuffer.shift();
306
366
  }
307
367
  this.messageBuffer.push(data);
308
368
  this.emit('message_buffered', { bufferLength: this.messageBuffer.length });
309
369
  }
310
370
 
311
- /**
312
- * Flush buffered messages
313
- */
314
371
  flushMessageBuffer() {
315
372
  if (this.messageBuffer.length === 0) return;
316
-
317
- console.log(`Flushing ${this.messageBuffer.length} buffered messages`);
318
373
  const messages = [...this.messageBuffer];
319
374
  this.messageBuffer = [];
320
-
321
375
  for (const message of messages) {
322
376
  try {
323
377
  this.ws.send(JSON.stringify(message));
324
378
  this.stats.totalMessagesSent++;
325
379
  } catch (error) {
326
- console.error('Error sending buffered message:', error);
327
380
  this.bufferMessage(message);
328
381
  }
329
382
  }
330
-
331
383
  this.emit('buffer_flushed', { count: messages.length });
332
384
  }
333
385
 
334
- /**
335
- * Subscribe to streaming session
336
- */
337
386
  subscribeToSession(sessionId) {
338
- return this.sendMessage({
339
- type: 'subscribe',
340
- sessionId,
341
- timestamp: Date.now()
342
- });
387
+ return this.sendMessage({ type: 'subscribe', sessionId, timestamp: Date.now() });
343
388
  }
344
389
 
345
390
  resubscribeAll() {
@@ -355,24 +400,13 @@ class WebSocketManager {
355
400
  }
356
401
  }
357
402
 
358
- /**
359
- * Unsubscribe from streaming session
360
- */
361
403
  unsubscribeFromSession(sessionId) {
362
- return this.sendMessage({
363
- type: 'unsubscribe',
364
- sessionId,
365
- timestamp: Date.now()
366
- });
404
+ return this.sendMessage({ type: 'unsubscribe', sessionId, timestamp: Date.now() });
367
405
  }
368
406
 
369
- /**
370
- * Request session history
371
- */
372
407
  requestSessionHistory(sessionId, limit = 1000, offset = 0) {
373
408
  return new Promise((resolve, reject) => {
374
- const requestId = `history-${Date.now()}-${Math.random()}`;
375
-
409
+ const requestId = 'history-' + Date.now() + '-' + Math.random();
376
410
  const timeout = setTimeout(() => {
377
411
  this.requestMap.delete(requestId);
378
412
  this.stats.totalTimeouts++;
@@ -381,55 +415,39 @@ class WebSocketManager {
381
415
 
382
416
  this.requestMap.set(requestId, {
383
417
  type: 'history',
384
- resolve: (data) => {
385
- clearTimeout(timeout);
386
- resolve(data);
387
- },
418
+ resolve: (d) => { clearTimeout(timeout); resolve(d); },
388
419
  reject
389
420
  });
390
421
 
391
422
  this.sendMessage({
392
- type: 'request_history',
393
- requestId,
394
- sessionId,
395
- limit,
396
- offset,
397
- timestamp: Date.now()
423
+ type: 'request_history', requestId, sessionId, limit, offset, timestamp: Date.now()
398
424
  });
399
425
  });
400
426
  }
401
427
 
402
- /**
403
- * Set connection state
404
- */
428
+ getLastSeq(sessionId) {
429
+ return this.lastSeqBySession[sessionId] || -1;
430
+ }
431
+
405
432
  setConnectionState(state) {
406
433
  this.connectionState = state;
407
434
  this.emit('state_change', { state, timestamp: Date.now() });
408
435
  }
409
436
 
410
- /**
411
- * Disconnect manually
412
- */
413
437
  disconnect() {
414
438
  this.isManuallyDisconnected = true;
415
439
  this.reconnectCount = 0;
416
-
417
- if (this.heartbeatTimer) {
418
- clearTimeout(this.heartbeatTimer);
419
- }
420
-
421
- if (this.ws) {
422
- this.ws.close();
440
+ this.stopHeartbeat();
441
+ if (this.reconnectTimer) {
442
+ clearTimeout(this.reconnectTimer);
443
+ this.reconnectTimer = null;
423
444
  }
424
-
445
+ if (this.ws) this.ws.close();
425
446
  this.messageBuffer = [];
426
447
  this.requestMap.clear();
427
448
  this.setConnectionState('disconnected');
428
449
  }
429
450
 
430
- /**
431
- * Get connection status
432
- */
433
451
  getStatus() {
434
452
  return {
435
453
  isConnected: this.isConnected,
@@ -437,55 +455,41 @@ class WebSocketManager {
437
455
  connectionState: this.connectionState,
438
456
  reconnectCount: this.reconnectCount,
439
457
  bufferLength: this.messageBuffer.length,
458
+ latency: { ...this.latency, samples: undefined },
440
459
  stats: { ...this.stats }
441
460
  };
442
461
  }
443
462
 
444
- /**
445
- * Add event listener
446
- */
447
463
  on(event, callback) {
448
- if (!this.listeners[event]) {
449
- this.listeners[event] = [];
450
- }
464
+ if (!this.listeners[event]) this.listeners[event] = [];
451
465
  this.listeners[event].push(callback);
452
466
  }
453
467
 
454
- /**
455
- * Remove event listener
456
- */
457
468
  off(event, callback) {
458
469
  if (!this.listeners[event]) return;
459
470
  const index = this.listeners[event].indexOf(callback);
460
- if (index > -1) {
461
- this.listeners[event].splice(index, 1);
462
- }
471
+ if (index > -1) this.listeners[event].splice(index, 1);
463
472
  }
464
473
 
465
- /**
466
- * Emit event
467
- */
468
474
  emit(event, data) {
469
475
  if (!this.listeners[event]) return;
470
- this.listeners[event].forEach((callback) => {
471
- try {
472
- callback(data);
473
- } catch (error) {
474
- console.error(`Listener error for event ${event}:`, error);
475
- }
476
+ this.listeners[event].forEach((cb) => {
477
+ try { cb(data); } catch (error) {}
476
478
  });
477
479
  }
478
480
 
479
- /**
480
- * Cleanup resources
481
- */
482
481
  destroy() {
482
+ if (typeof document !== 'undefined') {
483
+ document.removeEventListener('visibilitychange', this._onVisibilityChange);
484
+ }
485
+ if (typeof window !== 'undefined') {
486
+ window.removeEventListener('online', this._onOnline);
487
+ }
483
488
  this.disconnect();
484
489
  this.listeners = {};
485
490
  }
486
491
  }
487
492
 
488
- // Export for use in browser
489
493
  if (typeof module !== 'undefined' && module.exports) {
490
494
  module.exports = WebSocketManager;
491
495
  }