claude-mpm 3.4.13__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/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/services/socketio_server.py +41 -5
- {claude_mpm-3.4.13.dist-info → claude_mpm-3.4.14.dist-info}/METADATA +2 -1
- {claude_mpm-3.4.13.dist-info → claude_mpm-3.4.14.dist-info}/RECORD +27 -7
- {claude_mpm-3.4.13.dist-info → claude_mpm-3.4.14.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.13.dist-info → claude_mpm-3.4.14.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.4.13.dist-info → claude_mpm-3.4.14.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.4.13.dist-info → claude_mpm-3.4.14.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export Manager Module
|
|
3
|
+
*
|
|
4
|
+
* Handles export functionality and utility functions for the dashboard.
|
|
5
|
+
* Provides data export capabilities and common utility functions used across modules.
|
|
6
|
+
*
|
|
7
|
+
* WHY: Extracted from main dashboard to centralize export logic and utility functions
|
|
8
|
+
* that don't belong to specific functional areas. This provides a clean place for
|
|
9
|
+
* shared utilities while keeping export logic organized and testable.
|
|
10
|
+
*
|
|
11
|
+
* DESIGN DECISION: Combines export functionality with general utilities to avoid
|
|
12
|
+
* creating too many small modules while keeping related functionality together.
|
|
13
|
+
* Provides both data export and UI utility functions.
|
|
14
|
+
*/
|
|
15
|
+
class ExportManager {
|
|
16
|
+
constructor(eventViewer) {
|
|
17
|
+
this.eventViewer = eventViewer;
|
|
18
|
+
this.setupEventHandlers();
|
|
19
|
+
|
|
20
|
+
console.log('Export manager initialized');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Set up event handlers for export functionality
|
|
25
|
+
*/
|
|
26
|
+
setupEventHandlers() {
|
|
27
|
+
const clearBtn = document.querySelector('button[onclick="clearEvents()"]');
|
|
28
|
+
const exportBtn = document.getElementById('export-btn');
|
|
29
|
+
|
|
30
|
+
if (clearBtn) {
|
|
31
|
+
clearBtn.addEventListener('click', () => {
|
|
32
|
+
this.clearEvents();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (exportBtn) {
|
|
37
|
+
exportBtn.addEventListener('click', () => {
|
|
38
|
+
this.exportEvents();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Export current events to file
|
|
45
|
+
* Delegates to the event viewer's export functionality
|
|
46
|
+
*/
|
|
47
|
+
exportEvents() {
|
|
48
|
+
if (this.eventViewer) {
|
|
49
|
+
this.eventViewer.exportEvents();
|
|
50
|
+
} else {
|
|
51
|
+
console.error('Cannot export events: EventViewer not available');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Clear all events and reset dashboard state
|
|
57
|
+
* This is a coordinated clear that notifies all relevant modules
|
|
58
|
+
*/
|
|
59
|
+
clearEvents() {
|
|
60
|
+
// Dispatch event to notify other modules
|
|
61
|
+
document.dispatchEvent(new CustomEvent('eventsClearing'));
|
|
62
|
+
|
|
63
|
+
// Clear events from event viewer
|
|
64
|
+
if (this.eventViewer) {
|
|
65
|
+
this.eventViewer.clearEvents();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Dispatch event to notify clearing is complete
|
|
69
|
+
document.dispatchEvent(new CustomEvent('eventsCleared'));
|
|
70
|
+
|
|
71
|
+
console.log('Events cleared');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Export events with custom filtering
|
|
76
|
+
* @param {Object} options - Export options
|
|
77
|
+
* @param {string} options.format - Export format ('json', 'csv', 'txt')
|
|
78
|
+
* @param {Array} options.events - Events to export (optional, uses all if not provided)
|
|
79
|
+
* @param {string} options.filename - Custom filename (optional)
|
|
80
|
+
*/
|
|
81
|
+
exportEventsCustom(options = {}) {
|
|
82
|
+
const {
|
|
83
|
+
format = 'json',
|
|
84
|
+
events = null,
|
|
85
|
+
filename = null
|
|
86
|
+
} = options;
|
|
87
|
+
|
|
88
|
+
const eventsToExport = events || (this.eventViewer ? this.eventViewer.events : []);
|
|
89
|
+
|
|
90
|
+
if (eventsToExport.length === 0) {
|
|
91
|
+
console.warn('No events to export');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
96
|
+
const defaultFilename = `claude-mpm-events-${timestamp}`;
|
|
97
|
+
const exportFilename = filename || defaultFilename;
|
|
98
|
+
|
|
99
|
+
let content = '';
|
|
100
|
+
let mimeType = '';
|
|
101
|
+
let fileExtension = '';
|
|
102
|
+
|
|
103
|
+
switch (format.toLowerCase()) {
|
|
104
|
+
case 'json':
|
|
105
|
+
content = JSON.stringify(eventsToExport, null, 2);
|
|
106
|
+
mimeType = 'application/json';
|
|
107
|
+
fileExtension = '.json';
|
|
108
|
+
break;
|
|
109
|
+
|
|
110
|
+
case 'csv':
|
|
111
|
+
content = this.convertEventsToCSV(eventsToExport);
|
|
112
|
+
mimeType = 'text/csv';
|
|
113
|
+
fileExtension = '.csv';
|
|
114
|
+
break;
|
|
115
|
+
|
|
116
|
+
case 'txt':
|
|
117
|
+
content = this.convertEventsToText(eventsToExport);
|
|
118
|
+
mimeType = 'text/plain';
|
|
119
|
+
fileExtension = '.txt';
|
|
120
|
+
break;
|
|
121
|
+
|
|
122
|
+
default:
|
|
123
|
+
console.error('Unsupported export format:', format);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.downloadFile(content, exportFilename + fileExtension, mimeType);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Convert events to CSV format
|
|
132
|
+
* @param {Array} events - Events to convert
|
|
133
|
+
* @returns {string} - CSV content
|
|
134
|
+
*/
|
|
135
|
+
convertEventsToCSV(events) {
|
|
136
|
+
if (events.length === 0) return '';
|
|
137
|
+
|
|
138
|
+
// Define CSV headers
|
|
139
|
+
const headers = ['timestamp', 'type', 'subtype', 'tool_name', 'agent_type', 'session_id', 'data'];
|
|
140
|
+
|
|
141
|
+
// Convert events to CSV rows
|
|
142
|
+
const rows = events.map(event => {
|
|
143
|
+
return [
|
|
144
|
+
event.timestamp || '',
|
|
145
|
+
event.type || '',
|
|
146
|
+
event.subtype || '',
|
|
147
|
+
event.tool_name || '',
|
|
148
|
+
event.agent_type || '',
|
|
149
|
+
event.session_id || '',
|
|
150
|
+
JSON.stringify(event.data || {}).replace(/"/g, '""') // Escape quotes for CSV
|
|
151
|
+
];
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Combine headers and rows
|
|
155
|
+
const csvContent = [headers, ...rows]
|
|
156
|
+
.map(row => row.map(field => `"${field}"`).join(','))
|
|
157
|
+
.join('\n');
|
|
158
|
+
|
|
159
|
+
return csvContent;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Convert events to readable text format
|
|
164
|
+
* @param {Array} events - Events to convert
|
|
165
|
+
* @returns {string} - Text content
|
|
166
|
+
*/
|
|
167
|
+
convertEventsToText(events) {
|
|
168
|
+
if (events.length === 0) return 'No events to export.';
|
|
169
|
+
|
|
170
|
+
return events.map((event, index) => {
|
|
171
|
+
const timestamp = this.formatTimestamp(event.timestamp);
|
|
172
|
+
const type = event.type || 'Unknown';
|
|
173
|
+
const subtype = event.subtype ? ` (${event.subtype})` : '';
|
|
174
|
+
const toolName = event.tool_name ? ` - Tool: ${event.tool_name}` : '';
|
|
175
|
+
const agentType = event.agent_type ? ` - Agent: ${event.agent_type}` : '';
|
|
176
|
+
|
|
177
|
+
let content = `Event ${index + 1}: ${type}${subtype}${toolName}${agentType}\n`;
|
|
178
|
+
content += ` Time: ${timestamp}\n`;
|
|
179
|
+
content += ` Session: ${event.session_id || 'Unknown'}\n`;
|
|
180
|
+
|
|
181
|
+
if (event.data && Object.keys(event.data).length > 0) {
|
|
182
|
+
content += ` Data: ${JSON.stringify(event.data, null, 2)}\n`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return content;
|
|
186
|
+
}).join('\n' + '='.repeat(80) + '\n');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Download file with given content
|
|
191
|
+
* @param {string} content - File content
|
|
192
|
+
* @param {string} filename - Filename
|
|
193
|
+
* @param {string} mimeType - MIME type
|
|
194
|
+
*/
|
|
195
|
+
downloadFile(content, filename, mimeType) {
|
|
196
|
+
try {
|
|
197
|
+
const blob = new Blob([content], { type: mimeType });
|
|
198
|
+
const url = window.URL.createObjectURL(blob);
|
|
199
|
+
|
|
200
|
+
const link = document.createElement('a');
|
|
201
|
+
link.href = url;
|
|
202
|
+
link.download = filename;
|
|
203
|
+
link.style.display = 'none';
|
|
204
|
+
|
|
205
|
+
document.body.appendChild(link);
|
|
206
|
+
link.click();
|
|
207
|
+
document.body.removeChild(link);
|
|
208
|
+
|
|
209
|
+
// Clean up the URL object
|
|
210
|
+
window.URL.revokeObjectURL(url);
|
|
211
|
+
|
|
212
|
+
console.log(`File exported: ${filename}`);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error('Failed to export file:', error);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// =================
|
|
219
|
+
// UTILITY FUNCTIONS
|
|
220
|
+
// =================
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Format timestamp for display
|
|
224
|
+
* @param {string|number|Date} timestamp - Timestamp to format
|
|
225
|
+
* @returns {string} - Formatted timestamp
|
|
226
|
+
*/
|
|
227
|
+
formatTimestamp(timestamp) {
|
|
228
|
+
if (!timestamp) return 'Unknown time';
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
const date = new Date(timestamp);
|
|
232
|
+
if (isNaN(date.getTime())) {
|
|
233
|
+
return 'Invalid time';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return date.toLocaleTimeString('en-US', {
|
|
237
|
+
hour12: false,
|
|
238
|
+
hour: '2-digit',
|
|
239
|
+
minute: '2-digit',
|
|
240
|
+
second: '2-digit'
|
|
241
|
+
});
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error('Error formatting timestamp:', error);
|
|
244
|
+
return 'Error formatting time';
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Format full timestamp with date for exports
|
|
250
|
+
* @param {string|number|Date} timestamp - Timestamp to format
|
|
251
|
+
* @returns {string} - Formatted full timestamp
|
|
252
|
+
*/
|
|
253
|
+
formatFullTimestamp(timestamp) {
|
|
254
|
+
if (!timestamp) return 'Unknown time';
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
const date = new Date(timestamp);
|
|
258
|
+
if (isNaN(date.getTime())) {
|
|
259
|
+
return 'Invalid time';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return date.toLocaleString('en-US', {
|
|
263
|
+
year: 'numeric',
|
|
264
|
+
month: '2-digit',
|
|
265
|
+
day: '2-digit',
|
|
266
|
+
hour: '2-digit',
|
|
267
|
+
minute: '2-digit',
|
|
268
|
+
second: '2-digit',
|
|
269
|
+
hour12: false
|
|
270
|
+
});
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error('Error formatting full timestamp:', error);
|
|
273
|
+
return 'Error formatting time';
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Scroll a list element to bottom
|
|
279
|
+
* @param {string} listId - ID of list element to scroll
|
|
280
|
+
*/
|
|
281
|
+
scrollListToBottom(listId) {
|
|
282
|
+
console.log(`[DEBUG] scrollListToBottom called with listId: ${listId}`);
|
|
283
|
+
|
|
284
|
+
// Use setTimeout to ensure DOM updates are completed
|
|
285
|
+
setTimeout(() => {
|
|
286
|
+
const listElement = document.getElementById(listId);
|
|
287
|
+
console.log(`[DEBUG] Element found for ${listId}:`, listElement);
|
|
288
|
+
|
|
289
|
+
if (listElement) {
|
|
290
|
+
console.log(`[DEBUG] Scrolling ${listId} - scrollHeight: ${listElement.scrollHeight}, scrollTop before: ${listElement.scrollTop}`);
|
|
291
|
+
listElement.scrollTop = listElement.scrollHeight;
|
|
292
|
+
console.log(`[DEBUG] Scrolled ${listId} - scrollTop after: ${listElement.scrollTop}`);
|
|
293
|
+
} else {
|
|
294
|
+
console.warn(`[DEBUG] Element with ID '${listId}' not found for scrolling`);
|
|
295
|
+
}
|
|
296
|
+
}, 50); // Small delay to ensure content is rendered
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Debounce function to limit function calls
|
|
301
|
+
* @param {Function} func - Function to debounce
|
|
302
|
+
* @param {number} wait - Wait time in milliseconds
|
|
303
|
+
* @returns {Function} - Debounced function
|
|
304
|
+
*/
|
|
305
|
+
debounce(func, wait) {
|
|
306
|
+
let timeout;
|
|
307
|
+
return function executedFunction(...args) {
|
|
308
|
+
const later = () => {
|
|
309
|
+
clearTimeout(timeout);
|
|
310
|
+
func(...args);
|
|
311
|
+
};
|
|
312
|
+
clearTimeout(timeout);
|
|
313
|
+
timeout = setTimeout(later, wait);
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Throttle function to limit function calls
|
|
319
|
+
* @param {Function} func - Function to throttle
|
|
320
|
+
* @param {number} limit - Limit in milliseconds
|
|
321
|
+
* @returns {Function} - Throttled function
|
|
322
|
+
*/
|
|
323
|
+
throttle(func, limit) {
|
|
324
|
+
let inThrottle;
|
|
325
|
+
return function (...args) {
|
|
326
|
+
if (!inThrottle) {
|
|
327
|
+
func.apply(this, args);
|
|
328
|
+
inThrottle = true;
|
|
329
|
+
setTimeout(() => inThrottle = false, limit);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Generate unique ID
|
|
336
|
+
* @returns {string} - Unique ID
|
|
337
|
+
*/
|
|
338
|
+
generateId() {
|
|
339
|
+
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Deep clone an object
|
|
344
|
+
* @param {*} obj - Object to clone
|
|
345
|
+
* @returns {*} - Cloned object
|
|
346
|
+
*/
|
|
347
|
+
deepClone(obj) {
|
|
348
|
+
if (obj === null || typeof obj !== 'object') return obj;
|
|
349
|
+
if (obj instanceof Date) return new Date(obj.getTime());
|
|
350
|
+
if (obj instanceof Array) return obj.map(item => this.deepClone(item));
|
|
351
|
+
if (typeof obj === 'object') {
|
|
352
|
+
const cloned = {};
|
|
353
|
+
for (const key in obj) {
|
|
354
|
+
if (obj.hasOwnProperty(key)) {
|
|
355
|
+
cloned[key] = this.deepClone(obj[key]);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return cloned;
|
|
359
|
+
}
|
|
360
|
+
return obj;
|
|
361
|
+
}
|
|
362
|
+
}
|