claude-mpm 4.2.9__py3-none-any.whl → 4.2.12__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.
Files changed (51) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/cli/commands/dashboard.py +59 -126
  3. claude_mpm/cli/commands/monitor.py +82 -212
  4. claude_mpm/cli/commands/run.py +33 -33
  5. claude_mpm/cli/parsers/monitor_parser.py +12 -2
  6. claude_mpm/dashboard/static/css/code-tree.css +8 -16
  7. claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
  8. claude_mpm/dashboard/static/dist/components/file-viewer.js +2 -0
  9. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  10. claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +1 -1
  11. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  12. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  13. claude_mpm/dashboard/static/js/components/code-tree.js +692 -114
  14. claude_mpm/dashboard/static/js/components/file-viewer.js +538 -0
  15. claude_mpm/dashboard/static/js/components/module-viewer.js +26 -0
  16. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +166 -14
  17. claude_mpm/dashboard/static/js/dashboard.js +108 -91
  18. claude_mpm/dashboard/static/js/socket-client.js +9 -7
  19. claude_mpm/dashboard/templates/index.html +2 -7
  20. claude_mpm/hooks/claude_hooks/hook_handler.py +1 -11
  21. claude_mpm/hooks/claude_hooks/services/connection_manager.py +54 -59
  22. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +112 -72
  23. claude_mpm/services/agents/deployment/agent_template_builder.py +0 -1
  24. claude_mpm/services/cli/unified_dashboard_manager.py +354 -0
  25. claude_mpm/services/monitor/__init__.py +20 -0
  26. claude_mpm/services/monitor/daemon.py +378 -0
  27. claude_mpm/services/monitor/event_emitter.py +342 -0
  28. claude_mpm/services/monitor/handlers/__init__.py +20 -0
  29. claude_mpm/services/monitor/handlers/code_analysis.py +334 -0
  30. claude_mpm/services/monitor/handlers/dashboard.py +298 -0
  31. claude_mpm/services/monitor/handlers/hooks.py +491 -0
  32. claude_mpm/services/monitor/management/__init__.py +18 -0
  33. claude_mpm/services/monitor/management/health.py +124 -0
  34. claude_mpm/services/monitor/management/lifecycle.py +338 -0
  35. claude_mpm/services/monitor/server.py +596 -0
  36. claude_mpm/tools/code_tree_analyzer.py +33 -17
  37. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/METADATA +1 -1
  38. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/RECORD +42 -37
  39. claude_mpm/cli/commands/socketio_monitor.py +0 -233
  40. claude_mpm/scripts/socketio_daemon.py +0 -571
  41. claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
  42. claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
  43. claude_mpm/scripts/socketio_server_manager.py +0 -349
  44. claude_mpm/services/cli/dashboard_launcher.py +0 -423
  45. claude_mpm/services/cli/socketio_manager.py +0 -595
  46. claude_mpm/services/dashboard/stable_server.py +0 -1020
  47. claude_mpm/services/socketio/monitor_server.py +0 -505
  48. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/WHEEL +0 -0
  49. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/entry_points.txt +0 -0
  50. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/licenses/LICENSE +0 -0
  51. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,354 @@
1
+ """
2
+ Unified Dashboard Manager Service
3
+ =================================
4
+
5
+ WHY: This service provides a centralized way to manage dashboard functionality using
6
+ the UnifiedMonitorDaemon. It replaces the old DashboardLauncher and SocketIOManager
7
+ services with a cleaner implementation that uses the unified daemon architecture.
8
+
9
+ DESIGN DECISIONS:
10
+ - Uses UnifiedMonitorDaemon for all server functionality
11
+ - Provides the same interface as the old services for compatibility
12
+ - Handles browser opening, process management, and status checking
13
+ - Integrates with PortManager for port allocation
14
+ - Thread-safe daemon management
15
+ """
16
+
17
+ import threading
18
+ import time
19
+ import webbrowser
20
+ from abc import ABC, abstractmethod
21
+ from dataclasses import dataclass
22
+ from typing import Optional, Tuple
23
+
24
+ import requests
25
+
26
+ from ...core.logging_config import get_logger
27
+ from ...services.monitor.daemon import UnifiedMonitorDaemon
28
+ from ...services.port_manager import PortManager
29
+
30
+
31
+ @dataclass
32
+ class DashboardInfo:
33
+ """Information about a running dashboard."""
34
+
35
+ url: str
36
+ port: int
37
+ pid: Optional[int] = None
38
+ status: str = "running"
39
+
40
+
41
+ class IUnifiedDashboardManager(ABC):
42
+ """Interface for unified dashboard management."""
43
+
44
+ @abstractmethod
45
+ def start_dashboard(
46
+ self, port: int = 8765, background: bool = False, open_browser: bool = True
47
+ ) -> Tuple[bool, bool]:
48
+ """Start the dashboard using unified daemon."""
49
+
50
+ @abstractmethod
51
+ def stop_dashboard(self, port: int = 8765) -> bool:
52
+ """Stop the dashboard."""
53
+
54
+ @abstractmethod
55
+ def is_dashboard_running(self, port: int = 8765) -> bool:
56
+ """Check if dashboard is running."""
57
+
58
+ @abstractmethod
59
+ def get_dashboard_url(self, port: int = 8765) -> str:
60
+ """Get dashboard URL."""
61
+
62
+ @abstractmethod
63
+ def open_browser(self, url: str) -> bool:
64
+ """Open URL in browser."""
65
+
66
+
67
+ class UnifiedDashboardManager(IUnifiedDashboardManager):
68
+ """Unified dashboard manager using UnifiedMonitorDaemon."""
69
+
70
+ def __init__(self, logger=None):
71
+ """Initialize the unified dashboard manager."""
72
+ self.logger = logger or get_logger("UnifiedDashboardManager")
73
+ self.port_manager = PortManager()
74
+ self._background_daemons = {} # port -> daemon instance
75
+ self._lock = threading.Lock()
76
+
77
+ def start_dashboard(
78
+ self, port: int = 8765, background: bool = False, open_browser: bool = True
79
+ ) -> Tuple[bool, bool]:
80
+ """
81
+ Start the dashboard using unified daemon.
82
+
83
+ Args:
84
+ port: Port to run dashboard on
85
+ background: Whether to run in background mode
86
+ open_browser: Whether to open browser automatically
87
+
88
+ Returns:
89
+ Tuple of (success, browser_opened)
90
+ """
91
+ try:
92
+ # Check if already running
93
+ if self.is_dashboard_running(port):
94
+ self.logger.info(f"Dashboard already running on port {port}")
95
+ browser_opened = False
96
+ if open_browser:
97
+ browser_opened = self.open_browser(self.get_dashboard_url(port))
98
+ return True, browser_opened
99
+
100
+ self.logger.info(
101
+ f"Starting unified dashboard on port {port} (background: {background})"
102
+ )
103
+
104
+ if background:
105
+ # Start daemon in background mode
106
+ daemon = UnifiedMonitorDaemon(
107
+ host="localhost", port=port, daemon_mode=True
108
+ )
109
+
110
+ success = daemon.start()
111
+ if success:
112
+ with self._lock:
113
+ self._background_daemons[port] = daemon
114
+
115
+ # Wait for daemon to be ready
116
+ if self._wait_for_dashboard(port, timeout=10):
117
+ browser_opened = False
118
+ if open_browser:
119
+ browser_opened = self.open_browser(
120
+ self.get_dashboard_url(port)
121
+ )
122
+ return True, browser_opened
123
+ self.logger.error("Dashboard daemon started but not responding")
124
+ return False, False
125
+ self.logger.error("Failed to start dashboard daemon")
126
+ return False, False
127
+ # For foreground mode, the caller should handle the daemon directly
128
+ # This is used by the CLI command that runs in foreground
129
+ self.logger.info("Foreground mode should be handled by caller")
130
+ return True, False
131
+
132
+ except Exception as e:
133
+ self.logger.error(f"Error starting dashboard: {e}")
134
+ return False, False
135
+
136
+ def stop_dashboard(self, port: int = 8765) -> bool:
137
+ """
138
+ Stop the dashboard.
139
+
140
+ Args:
141
+ port: Port of dashboard to stop
142
+
143
+ Returns:
144
+ True if successfully stopped
145
+ """
146
+ try:
147
+ # Check if we have a background daemon for this port
148
+ with self._lock:
149
+ daemon = self._background_daemons.get(port)
150
+ if daemon:
151
+ daemon.stop()
152
+ del self._background_daemons[port]
153
+ self.logger.info(f"Stopped background daemon on port {port}")
154
+ return True
155
+
156
+ # Try to stop via process management
157
+ if self.port_manager.is_port_in_use(port):
158
+ # Use port manager to find and stop the process
159
+ active_instances = self.port_manager.list_active_instances()
160
+ for instance in active_instances:
161
+ if instance.get("port") == port:
162
+ pid = instance.get("pid")
163
+ if pid:
164
+ try:
165
+ import psutil
166
+
167
+ process = psutil.Process(pid)
168
+ process.terminate()
169
+ process.wait(timeout=5)
170
+ self.logger.info(
171
+ f"Terminated dashboard process {pid} on port {port}"
172
+ )
173
+ return True
174
+ except Exception as e:
175
+ self.logger.warning(
176
+ f"Failed to terminate process {pid}: {e}"
177
+ )
178
+
179
+ self.logger.warning(f"No dashboard found running on port {port}")
180
+ return False
181
+
182
+ except Exception as e:
183
+ self.logger.error(f"Error stopping dashboard: {e}")
184
+ return False
185
+
186
+ def is_dashboard_running(self, port: int = 8765) -> bool:
187
+ """
188
+ Check if dashboard is running.
189
+
190
+ Args:
191
+ port: Port to check
192
+
193
+ Returns:
194
+ True if dashboard is running
195
+ """
196
+ try:
197
+ response = requests.get(f"http://localhost:{port}/health", timeout=2)
198
+ return response.status_code == 200
199
+ except requests.exceptions.RequestException:
200
+ return False
201
+
202
+ def get_dashboard_url(self, port: int = 8765) -> str:
203
+ """
204
+ Get dashboard URL.
205
+
206
+ Args:
207
+ port: Port number
208
+
209
+ Returns:
210
+ Dashboard URL
211
+ """
212
+ return f"http://localhost:{port}"
213
+
214
+ def open_browser(self, url: str) -> bool:
215
+ """
216
+ Open URL in browser.
217
+
218
+ Args:
219
+ url: URL to open
220
+
221
+ Returns:
222
+ True if browser was opened successfully
223
+ """
224
+ try:
225
+ self.logger.info(f"Opening browser to {url}")
226
+ webbrowser.open(url)
227
+ return True
228
+ except Exception as e:
229
+ self.logger.warning(f"Failed to open browser: {e}")
230
+ return False
231
+
232
+ def _wait_for_dashboard(self, port: int, timeout: int = 30) -> bool:
233
+ """
234
+ Wait for dashboard to be ready.
235
+
236
+ Args:
237
+ port: Port to check
238
+ timeout: Maximum time to wait
239
+
240
+ Returns:
241
+ True if dashboard became ready
242
+ """
243
+ start_time = time.time()
244
+ while time.time() - start_time < timeout:
245
+ if self.is_dashboard_running(port):
246
+ return True
247
+ time.sleep(0.5)
248
+ return False
249
+
250
+ def get_dashboard_info(self, port: int = 8765) -> Optional[DashboardInfo]:
251
+ """
252
+ Get information about running dashboard.
253
+
254
+ Args:
255
+ port: Port to check
256
+
257
+ Returns:
258
+ DashboardInfo if running, None otherwise
259
+ """
260
+ if self.is_dashboard_running(port):
261
+ return DashboardInfo(
262
+ url=self.get_dashboard_url(port), port=port, status="running"
263
+ )
264
+ return None
265
+
266
+ def ensure_dependencies(self) -> Tuple[bool, Optional[str]]:
267
+ """
268
+ Ensure required dependencies are available.
269
+
270
+ Returns:
271
+ Tuple of (dependencies_ok, error_message)
272
+ """
273
+ try:
274
+ import aiohttp
275
+ import socketio
276
+
277
+ return True, None
278
+ except ImportError as e:
279
+ error_msg = f"Required dependencies missing: {e}"
280
+ return False, error_msg
281
+
282
+ def find_available_port(self, preferred_port: int = 8765) -> int:
283
+ """
284
+ Find an available port starting from the preferred port.
285
+
286
+ Args:
287
+ preferred_port: Preferred port to start checking from
288
+
289
+ Returns:
290
+ Available port number
291
+ """
292
+ return self.port_manager.find_available_port(preferred_port)
293
+
294
+ def start_server(
295
+ self, port: Optional[int] = None, timeout: int = 30
296
+ ) -> Tuple[bool, DashboardInfo]:
297
+ """
298
+ Start the server (compatibility method for SocketIOManager interface).
299
+
300
+ Args:
301
+ port: Port to use (finds available if None)
302
+ timeout: Timeout for startup
303
+
304
+ Returns:
305
+ Tuple of (success, DashboardInfo)
306
+ """
307
+ if port is None:
308
+ port = self.find_available_port()
309
+
310
+ success, browser_opened = self.start_dashboard(
311
+ port=port, background=True, open_browser=False
312
+ )
313
+
314
+ if success:
315
+ dashboard_info = DashboardInfo(
316
+ url=self.get_dashboard_url(port), port=port, status="running"
317
+ )
318
+ return True, dashboard_info
319
+ return False, DashboardInfo(url="", port=port, status="failed")
320
+
321
+ def is_server_running(self, port: int) -> bool:
322
+ """
323
+ Check if server is running (compatibility method for SocketIOManager interface).
324
+
325
+ Args:
326
+ port: Port to check
327
+
328
+ Returns:
329
+ True if server is running
330
+ """
331
+ return self.is_dashboard_running(port)
332
+
333
+ def stop_server(self, port: Optional[int] = None, timeout: int = 10) -> bool:
334
+ """
335
+ Stop the server (compatibility method for SocketIOManager interface).
336
+
337
+ Args:
338
+ port: Port to stop (stops all if None)
339
+ timeout: Timeout for shutdown
340
+
341
+ Returns:
342
+ True if stopped successfully
343
+ """
344
+ if port is None:
345
+ # Stop all background daemons
346
+ with self._lock:
347
+ ports_to_stop = list(self._background_daemons.keys())
348
+
349
+ success = True
350
+ for p in ports_to_stop:
351
+ if not self.stop_dashboard(p):
352
+ success = False
353
+ return success
354
+ return self.stop_dashboard(port)
@@ -0,0 +1,20 @@
1
+ """
2
+ Unified Monitor Service for Claude MPM
3
+ =====================================
4
+
5
+ WHY: This module provides a single, stable daemon process that combines all
6
+ monitoring functionality into one cohesive service. It replaces the multiple
7
+ competing server implementations with a unified solution.
8
+
9
+ DESIGN DECISIONS:
10
+ - Single process handles HTTP dashboard, Socket.IO events, and real AST analysis
11
+ - Uses proven aiohttp + socketio foundation
12
+ - Integrates real CodeTreeAnalyzer instead of mock data
13
+ - Built for daemon operation with proper lifecycle management
14
+ - Single port (8765) for all functionality
15
+ """
16
+
17
+ from .daemon import UnifiedMonitorDaemon
18
+ from .server import UnifiedMonitorServer
19
+
20
+ __all__ = ["UnifiedMonitorDaemon", "UnifiedMonitorServer"]