claude-mpm 3.4.10__py3-none-any.whl → 3.4.14__py3-none-any.whl
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.
- claude_mpm/cli/commands/run.py +10 -10
- claude_mpm/dashboard/index.html +13 -0
- claude_mpm/dashboard/static/css/dashboard.css +2722 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +619 -0
- claude_mpm/dashboard/static/js/components/event-processor.js +641 -0
- claude_mpm/dashboard/static/js/components/event-viewer.js +914 -0
- claude_mpm/dashboard/static/js/components/export-manager.js +362 -0
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +611 -0
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +211 -0
- claude_mpm/dashboard/static/js/components/hud-manager.js +671 -0
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +1718 -0
- claude_mpm/dashboard/static/js/components/module-viewer.js +2701 -0
- claude_mpm/dashboard/static/js/components/session-manager.js +520 -0
- claude_mpm/dashboard/static/js/components/socket-manager.js +343 -0
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +427 -0
- claude_mpm/dashboard/static/js/components/working-directory.js +866 -0
- claude_mpm/dashboard/static/js/dashboard-original.js +4134 -0
- claude_mpm/dashboard/static/js/dashboard.js +1978 -0
- claude_mpm/dashboard/static/js/socket-client.js +537 -0
- claude_mpm/dashboard/templates/index.html +346 -0
- claude_mpm/dashboard/test_dashboard.html +372 -0
- claude_mpm/scripts/socketio_daemon.py +51 -6
- claude_mpm/services/socketio_server.py +41 -5
- {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/METADATA +2 -1
- {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/RECORD +29 -9
- {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Socket.IO Client for Claude MPM Dashboard
|
|
3
|
+
* Handles WebSocket connections and event processing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class SocketClient {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.socket = null;
|
|
9
|
+
this.port = null; // Store the current port
|
|
10
|
+
this.connectionCallbacks = {
|
|
11
|
+
connect: [],
|
|
12
|
+
disconnect: [],
|
|
13
|
+
error: [],
|
|
14
|
+
event: []
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Connection state
|
|
18
|
+
this.isConnected = false;
|
|
19
|
+
this.isConnecting = false;
|
|
20
|
+
|
|
21
|
+
// Event processing
|
|
22
|
+
this.events = [];
|
|
23
|
+
this.sessions = new Map();
|
|
24
|
+
this.currentSessionId = null;
|
|
25
|
+
|
|
26
|
+
// Start periodic status check as fallback mechanism
|
|
27
|
+
this.startStatusCheckFallback();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Connect to Socket.IO server
|
|
32
|
+
* @param {string} port - Port number to connect to
|
|
33
|
+
*/
|
|
34
|
+
connect(port = '8765') {
|
|
35
|
+
// Store the port for later use
|
|
36
|
+
this.port = port;
|
|
37
|
+
const url = `http://localhost:${port}`;
|
|
38
|
+
|
|
39
|
+
// Prevent multiple simultaneous connections
|
|
40
|
+
if (this.socket && (this.socket.connected || this.socket.connecting)) {
|
|
41
|
+
console.log('Already connected or connecting, disconnecting first...');
|
|
42
|
+
this.socket.disconnect();
|
|
43
|
+
// Wait a moment for cleanup
|
|
44
|
+
setTimeout(() => this.doConnect(url), 100);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.doConnect(url);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Perform the actual connection
|
|
53
|
+
* @param {string} url - Socket.IO server URL
|
|
54
|
+
*/
|
|
55
|
+
doConnect(url) {
|
|
56
|
+
console.log(`Connecting to Socket.IO server at ${url}`);
|
|
57
|
+
this.isConnecting = true;
|
|
58
|
+
this.notifyConnectionStatus('Connecting...', 'connecting');
|
|
59
|
+
|
|
60
|
+
this.socket = io(url, {
|
|
61
|
+
autoConnect: true,
|
|
62
|
+
reconnection: true,
|
|
63
|
+
reconnectionDelay: 1000,
|
|
64
|
+
reconnectionDelayMax: 10000,
|
|
65
|
+
maxReconnectionAttempts: 10,
|
|
66
|
+
timeout: 10000,
|
|
67
|
+
forceNew: true,
|
|
68
|
+
transports: ['websocket', 'polling']
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
this.setupSocketHandlers();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Setup Socket.IO event handlers
|
|
76
|
+
*/
|
|
77
|
+
setupSocketHandlers() {
|
|
78
|
+
this.socket.on('connect', () => {
|
|
79
|
+
console.log('Connected to Socket.IO server');
|
|
80
|
+
this.isConnected = true;
|
|
81
|
+
this.isConnecting = false;
|
|
82
|
+
this.notifyConnectionStatus('Connected', 'connected');
|
|
83
|
+
|
|
84
|
+
// Emit connect callback
|
|
85
|
+
this.connectionCallbacks.connect.forEach(callback =>
|
|
86
|
+
callback(this.socket.id)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
this.requestStatus();
|
|
90
|
+
// History is now automatically sent by server on connection
|
|
91
|
+
// No need to explicitly request it
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
this.socket.on('disconnect', (reason) => {
|
|
95
|
+
console.log('Disconnected from server:', reason);
|
|
96
|
+
this.isConnected = false;
|
|
97
|
+
this.isConnecting = false;
|
|
98
|
+
this.notifyConnectionStatus(`Disconnected: ${reason}`, 'disconnected');
|
|
99
|
+
|
|
100
|
+
// Emit disconnect callback
|
|
101
|
+
this.connectionCallbacks.disconnect.forEach(callback =>
|
|
102
|
+
callback(reason)
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
this.socket.on('connect_error', (error) => {
|
|
107
|
+
console.error('Connection error:', error);
|
|
108
|
+
this.isConnecting = false;
|
|
109
|
+
const errorMsg = error.message || error.description || 'Unknown error';
|
|
110
|
+
this.notifyConnectionStatus(`Connection Error: ${errorMsg}`, 'disconnected');
|
|
111
|
+
|
|
112
|
+
// Add error event
|
|
113
|
+
this.addEvent({
|
|
114
|
+
type: 'connection.error',
|
|
115
|
+
timestamp: new Date().toISOString(),
|
|
116
|
+
data: { error: errorMsg, url: this.socket.io.uri }
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Emit error callback
|
|
120
|
+
this.connectionCallbacks.error.forEach(callback =>
|
|
121
|
+
callback(errorMsg)
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Primary event handler - this is what the server actually emits
|
|
126
|
+
this.socket.on('claude_event', (data) => {
|
|
127
|
+
console.log('Received claude_event:', data);
|
|
128
|
+
|
|
129
|
+
// Transform event to match expected format
|
|
130
|
+
const transformedEvent = this.transformEvent(data);
|
|
131
|
+
console.log('Transformed event:', transformedEvent);
|
|
132
|
+
this.addEvent(transformedEvent);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Session and event handlers (legacy/fallback)
|
|
136
|
+
this.socket.on('session.started', (data) => {
|
|
137
|
+
this.addEvent({ type: 'session', subtype: 'started', timestamp: new Date().toISOString(), data });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
this.socket.on('session.ended', (data) => {
|
|
141
|
+
this.addEvent({ type: 'session', subtype: 'ended', timestamp: new Date().toISOString(), data });
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
this.socket.on('claude.request', (data) => {
|
|
145
|
+
this.addEvent({ type: 'claude', subtype: 'request', timestamp: new Date().toISOString(), data });
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
this.socket.on('claude.response', (data) => {
|
|
149
|
+
this.addEvent({ type: 'claude', subtype: 'response', timestamp: new Date().toISOString(), data });
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
this.socket.on('agent.loaded', (data) => {
|
|
153
|
+
this.addEvent({ type: 'agent', subtype: 'loaded', timestamp: new Date().toISOString(), data });
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
this.socket.on('agent.executed', (data) => {
|
|
157
|
+
this.addEvent({ type: 'agent', subtype: 'executed', timestamp: new Date().toISOString(), data });
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
this.socket.on('hook.pre', (data) => {
|
|
161
|
+
this.addEvent({ type: 'hook', subtype: 'pre', timestamp: new Date().toISOString(), data });
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
this.socket.on('hook.post', (data) => {
|
|
165
|
+
this.addEvent({ type: 'hook', subtype: 'post', timestamp: new Date().toISOString(), data });
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
this.socket.on('todo.updated', (data) => {
|
|
169
|
+
this.addEvent({ type: 'todo', subtype: 'updated', timestamp: new Date().toISOString(), data });
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
this.socket.on('memory.operation', (data) => {
|
|
173
|
+
this.addEvent({ type: 'memory', subtype: 'operation', timestamp: new Date().toISOString(), data });
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
this.socket.on('log.entry', (data) => {
|
|
177
|
+
this.addEvent({ type: 'log', subtype: 'entry', timestamp: new Date().toISOString(), data });
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
this.socket.on('history', (data) => {
|
|
181
|
+
console.log('Received event history:', data);
|
|
182
|
+
if (data && Array.isArray(data.events)) {
|
|
183
|
+
console.log(`Processing ${data.events.length} historical events (${data.count} sent, ${data.total_available} total available)`);
|
|
184
|
+
// Add events in the order received (should already be chronological - oldest first)
|
|
185
|
+
// Transform each historical event to match expected format
|
|
186
|
+
data.events.forEach(event => {
|
|
187
|
+
const transformedEvent = this.transformEvent(event);
|
|
188
|
+
this.addEvent(transformedEvent, false);
|
|
189
|
+
});
|
|
190
|
+
this.notifyEventUpdate();
|
|
191
|
+
console.log(`Event history loaded: ${data.events.length} events added to dashboard`);
|
|
192
|
+
} else if (Array.isArray(data)) {
|
|
193
|
+
// Handle legacy format for backward compatibility
|
|
194
|
+
console.log('Received legacy event history format:', data.length, 'events');
|
|
195
|
+
data.forEach(event => {
|
|
196
|
+
const transformedEvent = this.transformEvent(event);
|
|
197
|
+
this.addEvent(transformedEvent, false);
|
|
198
|
+
});
|
|
199
|
+
this.notifyEventUpdate();
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
this.socket.on('system.status', (data) => {
|
|
204
|
+
console.log('Received system status:', data);
|
|
205
|
+
if (data.sessions) {
|
|
206
|
+
this.updateSessions(data.sessions);
|
|
207
|
+
}
|
|
208
|
+
if (data.current_session) {
|
|
209
|
+
this.currentSessionId = data.current_session;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Disconnect from Socket.IO server
|
|
216
|
+
*/
|
|
217
|
+
disconnect() {
|
|
218
|
+
if (this.socket) {
|
|
219
|
+
this.socket.disconnect();
|
|
220
|
+
this.socket = null;
|
|
221
|
+
}
|
|
222
|
+
this.port = null; // Clear the stored port
|
|
223
|
+
this.isConnected = false;
|
|
224
|
+
this.isConnecting = false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Request server status
|
|
229
|
+
*/
|
|
230
|
+
requestStatus() {
|
|
231
|
+
if (this.socket && this.socket.connected) {
|
|
232
|
+
console.log('Requesting server status...');
|
|
233
|
+
this.socket.emit('request.status');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Request event history from server
|
|
239
|
+
* @param {Object} options - History request options
|
|
240
|
+
* @param {number} options.limit - Maximum number of events to retrieve (default: 50)
|
|
241
|
+
* @param {Array<string>} options.event_types - Optional filter by event types
|
|
242
|
+
*/
|
|
243
|
+
requestHistory(options = {}) {
|
|
244
|
+
if (this.socket && this.socket.connected) {
|
|
245
|
+
const params = {
|
|
246
|
+
limit: options.limit || 50,
|
|
247
|
+
event_types: options.event_types || []
|
|
248
|
+
};
|
|
249
|
+
console.log('Requesting event history...', params);
|
|
250
|
+
this.socket.emit('get_history', params);
|
|
251
|
+
} else {
|
|
252
|
+
console.warn('Cannot request history: not connected to server');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Add event to local storage and notify listeners
|
|
258
|
+
* @param {Object} eventData - Event data
|
|
259
|
+
* @param {boolean} notify - Whether to notify listeners (default: true)
|
|
260
|
+
*/
|
|
261
|
+
addEvent(eventData, notify = true) {
|
|
262
|
+
// Ensure event has required fields
|
|
263
|
+
if (!eventData.timestamp) {
|
|
264
|
+
eventData.timestamp = new Date().toISOString();
|
|
265
|
+
}
|
|
266
|
+
if (!eventData.id) {
|
|
267
|
+
eventData.id = Date.now() + Math.random();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
this.events.push(eventData);
|
|
271
|
+
|
|
272
|
+
// Update session tracking
|
|
273
|
+
if (eventData.data && eventData.data.session_id) {
|
|
274
|
+
const sessionId = eventData.data.session_id;
|
|
275
|
+
if (!this.sessions.has(sessionId)) {
|
|
276
|
+
this.sessions.set(sessionId, {
|
|
277
|
+
id: sessionId,
|
|
278
|
+
startTime: eventData.timestamp,
|
|
279
|
+
lastActivity: eventData.timestamp,
|
|
280
|
+
eventCount: 0
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
const session = this.sessions.get(sessionId);
|
|
284
|
+
session.lastActivity = eventData.timestamp;
|
|
285
|
+
session.eventCount++;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (notify) {
|
|
289
|
+
this.notifyEventUpdate();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Update sessions from server data
|
|
295
|
+
* @param {Array} sessionsData - Sessions data from server
|
|
296
|
+
*/
|
|
297
|
+
updateSessions(sessionsData) {
|
|
298
|
+
if (Array.isArray(sessionsData)) {
|
|
299
|
+
sessionsData.forEach(session => {
|
|
300
|
+
this.sessions.set(session.id, session);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Clear all events
|
|
307
|
+
*/
|
|
308
|
+
clearEvents() {
|
|
309
|
+
this.events = [];
|
|
310
|
+
this.sessions.clear();
|
|
311
|
+
this.notifyEventUpdate();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Clear events and request fresh history from server
|
|
316
|
+
* @param {Object} options - History request options (same as requestHistory)
|
|
317
|
+
*/
|
|
318
|
+
refreshHistory(options = {}) {
|
|
319
|
+
this.clearEvents();
|
|
320
|
+
this.requestHistory(options);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Get filtered events by session
|
|
325
|
+
* @param {string} sessionId - Session ID to filter by (null for all)
|
|
326
|
+
* @returns {Array} Filtered events
|
|
327
|
+
*/
|
|
328
|
+
getEventsBySession(sessionId = null) {
|
|
329
|
+
if (!sessionId) {
|
|
330
|
+
return this.events;
|
|
331
|
+
}
|
|
332
|
+
return this.events.filter(event =>
|
|
333
|
+
event.data && event.data.session_id === sessionId
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Register callback for connection events
|
|
339
|
+
* @param {string} eventType - Type of event (connect, disconnect, error)
|
|
340
|
+
* @param {Function} callback - Callback function
|
|
341
|
+
*/
|
|
342
|
+
onConnection(eventType, callback) {
|
|
343
|
+
if (this.connectionCallbacks[eventType]) {
|
|
344
|
+
this.connectionCallbacks[eventType].push(callback);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Register callback for event updates
|
|
350
|
+
* @param {Function} callback - Callback function
|
|
351
|
+
*/
|
|
352
|
+
onEventUpdate(callback) {
|
|
353
|
+
this.connectionCallbacks.event.push(callback);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Notify connection status change
|
|
358
|
+
* @param {string} status - Status message
|
|
359
|
+
* @param {string} type - Status type (connected, disconnected, connecting)
|
|
360
|
+
*/
|
|
361
|
+
notifyConnectionStatus(status, type) {
|
|
362
|
+
console.log(`SocketClient: Connection status changed to '${status}' (${type})`);
|
|
363
|
+
|
|
364
|
+
// Direct DOM update - immediate and reliable
|
|
365
|
+
this.updateConnectionStatusDOM(status, type);
|
|
366
|
+
|
|
367
|
+
// Also dispatch custom event for other modules
|
|
368
|
+
document.dispatchEvent(new CustomEvent('socketConnectionStatus', {
|
|
369
|
+
detail: { status, type }
|
|
370
|
+
}));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Directly update the connection status DOM element
|
|
375
|
+
* @param {string} status - Status message
|
|
376
|
+
* @param {string} type - Status type (connected, disconnected, connecting)
|
|
377
|
+
*/
|
|
378
|
+
updateConnectionStatusDOM(status, type) {
|
|
379
|
+
const statusElement = document.getElementById('connection-status');
|
|
380
|
+
if (statusElement) {
|
|
381
|
+
// Update the text content while preserving the indicator span
|
|
382
|
+
statusElement.innerHTML = `<span>●</span> ${status}`;
|
|
383
|
+
|
|
384
|
+
// Update the CSS class for styling
|
|
385
|
+
statusElement.className = `status-badge status-${type}`;
|
|
386
|
+
|
|
387
|
+
console.log(`SocketClient: Direct DOM update - status: '${status}' (${type})`);
|
|
388
|
+
} else {
|
|
389
|
+
console.warn('SocketClient: Could not find connection-status element in DOM');
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Notify event update
|
|
395
|
+
*/
|
|
396
|
+
notifyEventUpdate() {
|
|
397
|
+
this.connectionCallbacks.event.forEach(callback =>
|
|
398
|
+
callback(this.events, this.sessions)
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
// Also dispatch custom event
|
|
402
|
+
document.dispatchEvent(new CustomEvent('socketEventUpdate', {
|
|
403
|
+
detail: { events: this.events, sessions: this.sessions }
|
|
404
|
+
}));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Get connection state
|
|
409
|
+
* @returns {Object} Connection state
|
|
410
|
+
*/
|
|
411
|
+
getConnectionState() {
|
|
412
|
+
return {
|
|
413
|
+
isConnected: this.isConnected,
|
|
414
|
+
isConnecting: this.isConnecting,
|
|
415
|
+
socketId: this.socket ? this.socket.id : null
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Transform received event to match expected dashboard format
|
|
421
|
+
* @param {Object} eventData - Raw event data from server
|
|
422
|
+
* @returns {Object} Transformed event
|
|
423
|
+
*/
|
|
424
|
+
transformEvent(eventData) {
|
|
425
|
+
// Handle the actual event structure sent from hook_handler.py:
|
|
426
|
+
// { type: 'hook.pre_tool', timestamp: '...', data: { tool_name: '...', agent_type: '...', etc } }
|
|
427
|
+
|
|
428
|
+
if (!eventData || !eventData.type) {
|
|
429
|
+
return eventData; // Return as-is if malformed
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const type = eventData.type;
|
|
433
|
+
let transformedEvent = { ...eventData };
|
|
434
|
+
|
|
435
|
+
// Transform 'hook.subtype' format to separate type and subtype
|
|
436
|
+
if (type.startsWith('hook.')) {
|
|
437
|
+
const subtype = type.substring(5); // Remove 'hook.' prefix
|
|
438
|
+
transformedEvent.type = 'hook';
|
|
439
|
+
transformedEvent.subtype = subtype;
|
|
440
|
+
}
|
|
441
|
+
// Transform other dotted types like 'session.started' -> type: 'session', subtype: 'started'
|
|
442
|
+
else if (type.includes('.')) {
|
|
443
|
+
const [mainType, ...subtypeParts] = type.split('.');
|
|
444
|
+
transformedEvent.type = mainType;
|
|
445
|
+
transformedEvent.subtype = subtypeParts.join('.');
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Extract and flatten data fields to top level for dashboard compatibility
|
|
449
|
+
// The dashboard expects fields like tool_name, agent_type, etc. at the top level
|
|
450
|
+
if (eventData.data && typeof eventData.data === 'object') {
|
|
451
|
+
// Copy all data fields to the top level
|
|
452
|
+
Object.keys(eventData.data).forEach(key => {
|
|
453
|
+
// Only copy if the key doesn't already exist at top level
|
|
454
|
+
if (!(key in transformedEvent)) {
|
|
455
|
+
transformedEvent[key] = eventData.data[key];
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return transformedEvent;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Get current events and sessions
|
|
465
|
+
* @returns {Object} Current state
|
|
466
|
+
*/
|
|
467
|
+
getState() {
|
|
468
|
+
return {
|
|
469
|
+
events: this.events,
|
|
470
|
+
sessions: this.sessions,
|
|
471
|
+
currentSessionId: this.currentSessionId
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Start periodic status check as fallback mechanism
|
|
477
|
+
* This ensures the UI stays in sync with actual socket state
|
|
478
|
+
*/
|
|
479
|
+
startStatusCheckFallback() {
|
|
480
|
+
// Check status every 2 seconds
|
|
481
|
+
setInterval(() => {
|
|
482
|
+
this.checkAndUpdateStatus();
|
|
483
|
+
}, 2000);
|
|
484
|
+
|
|
485
|
+
// Initial check after DOM is ready
|
|
486
|
+
if (document.readyState === 'loading') {
|
|
487
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
488
|
+
setTimeout(() => this.checkAndUpdateStatus(), 100);
|
|
489
|
+
});
|
|
490
|
+
} else {
|
|
491
|
+
setTimeout(() => this.checkAndUpdateStatus(), 100);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Check actual socket state and update UI if necessary
|
|
497
|
+
*/
|
|
498
|
+
checkAndUpdateStatus() {
|
|
499
|
+
let actualStatus = 'Disconnected';
|
|
500
|
+
let actualType = 'disconnected';
|
|
501
|
+
|
|
502
|
+
if (this.socket) {
|
|
503
|
+
if (this.socket.connected) {
|
|
504
|
+
actualStatus = 'Connected';
|
|
505
|
+
actualType = 'connected';
|
|
506
|
+
this.isConnected = true;
|
|
507
|
+
this.isConnecting = false;
|
|
508
|
+
} else if (this.socket.connecting || this.isConnecting) {
|
|
509
|
+
actualStatus = 'Connecting...';
|
|
510
|
+
actualType = 'connecting';
|
|
511
|
+
this.isConnected = false;
|
|
512
|
+
} else {
|
|
513
|
+
actualStatus = 'Disconnected';
|
|
514
|
+
actualType = 'disconnected';
|
|
515
|
+
this.isConnected = false;
|
|
516
|
+
this.isConnecting = false;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Check if UI needs updating
|
|
521
|
+
const statusElement = document.getElementById('connection-status');
|
|
522
|
+
if (statusElement) {
|
|
523
|
+
const currentText = statusElement.textContent.replace('●', '').trim();
|
|
524
|
+
const currentClass = statusElement.className;
|
|
525
|
+
const expectedClass = `status-badge status-${actualType}`;
|
|
526
|
+
|
|
527
|
+
// Update if status text or class doesn't match
|
|
528
|
+
if (currentText !== actualStatus || currentClass !== expectedClass) {
|
|
529
|
+
console.log(`SocketClient: Fallback update - was '${currentText}' (${currentClass}), now '${actualStatus}' (${expectedClass})`);
|
|
530
|
+
this.updateConnectionStatusDOM(actualStatus, actualType);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Export for use in other modules
|
|
537
|
+
window.SocketClient = SocketClient;
|