claude-mpm 4.2.7__py3-none-any.whl → 4.2.11__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 (57) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/cli/commands/dashboard.py +62 -120
  3. claude_mpm/cli/commands/monitor.py +71 -212
  4. claude_mpm/cli/commands/run.py +33 -33
  5. claude_mpm/cli/parser.py +79 -2
  6. claude_mpm/cli/parsers/__init__.py +29 -0
  7. claude_mpm/dashboard/static/css/code-tree.css +16 -4
  8. claude_mpm/dashboard/static/css/dashboard.css +15 -1
  9. claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
  10. claude_mpm/dashboard/static/dist/components/file-viewer.js +2 -0
  11. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  12. claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +1 -1
  13. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  14. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  15. claude_mpm/dashboard/static/js/components/code-tree.js +775 -142
  16. claude_mpm/dashboard/static/js/components/file-viewer.js +538 -0
  17. claude_mpm/dashboard/static/js/components/module-viewer.js +26 -0
  18. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +166 -14
  19. claude_mpm/dashboard/static/js/dashboard.js +108 -91
  20. claude_mpm/dashboard/static/js/socket-client.js +9 -7
  21. claude_mpm/dashboard/templates/index.html +5 -2
  22. claude_mpm/hooks/claude_hooks/hook_handler.py +1 -11
  23. claude_mpm/hooks/claude_hooks/services/connection_manager.py +54 -59
  24. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +112 -72
  25. claude_mpm/services/agents/deployment/agent_format_converter.py +3 -3
  26. claude_mpm/services/agents/deployment/agent_template_builder.py +3 -5
  27. claude_mpm/services/cli/unified_dashboard_manager.py +354 -0
  28. claude_mpm/services/monitor/__init__.py +20 -0
  29. claude_mpm/services/monitor/daemon.py +256 -0
  30. claude_mpm/services/monitor/event_emitter.py +279 -0
  31. claude_mpm/services/monitor/handlers/__init__.py +20 -0
  32. claude_mpm/services/monitor/handlers/code_analysis.py +334 -0
  33. claude_mpm/services/monitor/handlers/dashboard.py +298 -0
  34. claude_mpm/services/monitor/handlers/hooks.py +491 -0
  35. claude_mpm/services/monitor/management/__init__.py +18 -0
  36. claude_mpm/services/monitor/management/health.py +124 -0
  37. claude_mpm/services/monitor/management/lifecycle.py +298 -0
  38. claude_mpm/services/monitor/server.py +442 -0
  39. claude_mpm/services/socketio/client_proxy.py +20 -12
  40. claude_mpm/services/socketio/dashboard_server.py +4 -4
  41. claude_mpm/services/socketio/monitor_client.py +4 -6
  42. claude_mpm/tools/code_tree_analyzer.py +33 -17
  43. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/METADATA +1 -1
  44. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/RECORD +48 -43
  45. claude_mpm/cli/commands/socketio_monitor.py +0 -233
  46. claude_mpm/scripts/socketio_daemon.py +0 -571
  47. claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
  48. claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
  49. claude_mpm/scripts/socketio_server_manager.py +0 -349
  50. claude_mpm/services/cli/dashboard_launcher.py +0 -423
  51. claude_mpm/services/cli/socketio_manager.py +0 -595
  52. claude_mpm/services/dashboard/stable_server.py +0 -962
  53. claude_mpm/services/socketio/monitor_server.py +0 -505
  54. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/WHEEL +0 -0
  55. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/entry_points.txt +0 -0
  56. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/licenses/LICENSE +0 -0
  57. {claude_mpm-4.2.7.dist-info → claude_mpm-4.2.11.dist-info}/top_level.txt +0 -0
@@ -1,78 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Wrapper script to use the hardened daemon by default while maintaining compatibility.
4
-
5
- WHY: Provides a smooth transition path from the original daemon to the hardened version
6
- without breaking existing workflows or scripts.
7
- """
8
-
9
- import os
10
- import subprocess
11
- import sys
12
- from pathlib import Path
13
-
14
- # Determine which daemon to use
15
- USE_HARDENED = os.environ.get("SOCKETIO_USE_HARDENED", "true").lower() == "true"
16
-
17
- # Find the appropriate daemon script using proper resource resolution
18
- try:
19
- # Try using importlib.resources for proper package resource access
20
- try:
21
- from importlib import resources
22
-
23
- # We're in the same package, so try direct import
24
- import claude_mpm.scripts
25
-
26
- scripts_package = claude_mpm.scripts
27
-
28
- if USE_HARDENED:
29
- try:
30
- with resources.path(
31
- scripts_package, "socketio_daemon_hardened.py"
32
- ) as p:
33
- daemon_script = p
34
- print("Using hardened Socket.IO daemon for improved reliability")
35
- except (FileNotFoundError, ModuleNotFoundError):
36
- # Fall back to original if hardened doesn't exist
37
- with resources.path(scripts_package, "socketio_daemon.py") as p:
38
- daemon_script = p
39
- else:
40
- with resources.path(scripts_package, "socketio_daemon.py") as p:
41
- daemon_script = p
42
- print(
43
- "Using original Socket.IO daemon (set SOCKETIO_USE_HARDENED=true for hardened version)"
44
- )
45
- except (ImportError, AttributeError, ModuleNotFoundError):
46
- # Fallback for older Python versions or if resources not available
47
- script_dir = Path(__file__).parent
48
-
49
- if USE_HARDENED:
50
- daemon_script = script_dir / "socketio_daemon_hardened.py"
51
- if not daemon_script.exists():
52
- # Fall back to original if hardened doesn't exist
53
- daemon_script = script_dir / "socketio_daemon.py"
54
- else:
55
- print("Using hardened Socket.IO daemon for improved reliability")
56
- else:
57
- daemon_script = script_dir / "socketio_daemon.py"
58
- print(
59
- "Using original Socket.IO daemon (set SOCKETIO_USE_HARDENED=true for hardened version)"
60
- )
61
- except Exception:
62
- # Ultimate fallback - try relative to current file
63
- script_dir = Path(__file__).parent
64
- daemon_script = script_dir / (
65
- "socketio_daemon_hardened.py" if USE_HARDENED else "socketio_daemon.py"
66
- )
67
- if not daemon_script.exists():
68
- daemon_script = script_dir / "socketio_daemon.py"
69
-
70
- # Pass through all arguments to the selected daemon
71
- if daemon_script.exists():
72
- result = subprocess.run(
73
- [sys.executable, str(daemon_script)] + sys.argv[1:], check=False
74
- )
75
- sys.exit(result.returncode)
76
- else:
77
- print(f"Error: Daemon script not found at {daemon_script}")
78
- sys.exit(1)
@@ -1,349 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Socket.IO Server Manager wrapper for claude-mpm.
4
-
5
- WHY: This module provides a wrapper around the ServerManager class from
6
- tools/admin/socketio_server_manager.py to make it accessible from the
7
- scripts directory as expected by the CLI monitor command.
8
-
9
- DESIGN DECISION: Rather than duplicating code, we import and re-export
10
- the ServerManager class from its actual location in tools/admin. This
11
- ensures consistency and maintainability.
12
- """
13
-
14
- import sys
15
- from pathlib import Path
16
-
17
- # Add the tools/admin directory to Python path to import ServerManager
18
- script_dir = Path(__file__).parent
19
- project_root = script_dir.parent.parent.parent # Up to project root
20
- tools_admin_dir = project_root / "tools" / "admin"
21
-
22
- if tools_admin_dir.exists() and str(tools_admin_dir) not in sys.path:
23
- sys.path.insert(0, str(tools_admin_dir))
24
-
25
- try:
26
- # Import ServerManager from the actual implementation
27
- import subprocess
28
-
29
- from socketio_server_manager import ServerManager as BaseServerManager
30
-
31
- # Patch the ServerManager to use project-local PID file and daemon
32
- class ServerManager(BaseServerManager):
33
- """Patched ServerManager that uses project-local PID file and daemon.
34
-
35
- WHY: The daemon stores its PID file in the project's .claude-mpm
36
- directory, not in the home directory. This patch ensures the
37
- ServerManager looks in the correct location and uses the daemon
38
- for starting servers when the standalone module is not available.
39
- """
40
-
41
- def __init__(self):
42
- super().__init__()
43
- # Override the daemon PID file path to use project root
44
- from claude_mpm.core.unified_paths import get_project_root
45
-
46
- self.daemon_pidfile_path = (
47
- get_project_root() / ".claude-mpm" / "socketio-server.pid"
48
- )
49
- self.daemon_script = script_dir / "socketio_daemon.py"
50
-
51
- def start_server(
52
- self, port: int = None, host: str = "localhost", server_id: str = None
53
- ) -> bool:
54
- """Start Socket.IO server, preferring daemon over standalone.
55
-
56
- WHY: When the standalone_socketio_server module is not available,
57
- we fallback to using the daemon script which is always present.
58
- """
59
- # First check if the daemon is already running
60
- if self.daemon_pidfile_path.exists():
61
- try:
62
- with open(self.daemon_pidfile_path) as f:
63
- pid = int(f.read().strip())
64
- # Check if process is running
65
- import psutil
66
-
67
- process = psutil.Process(pid)
68
- if process.is_running():
69
- print(
70
- f"Socket.IO daemon server is already running (PID: {pid})"
71
- )
72
- return True
73
- except:
74
- pass
75
-
76
- # Try to use the daemon script for starting
77
- if self.daemon_script.exists():
78
- print(
79
- f"Starting server on {host}:{port or self.base_port} using daemon..."
80
- )
81
- try:
82
- result = subprocess.run(
83
- [sys.executable, str(self.daemon_script), "start"],
84
- capture_output=True,
85
- text=True,
86
- check=False,
87
- )
88
-
89
- if result.returncode == 0:
90
- # Check if output contains success message
91
- if "started successfully" in result.stdout.lower():
92
- print(result.stdout)
93
- return True
94
- # Even if no explicit success message, check if process is running
95
- if self.daemon_pidfile_path.exists():
96
- print("Socket.IO daemon server started")
97
- return True
98
- else:
99
- # If daemon fails, try parent's start_server method
100
- if result.stderr:
101
- print(f"Daemon error: {result.stderr}")
102
- print("Trying standalone method...")
103
- except Exception as e:
104
- print(f"Error running daemon: {e}")
105
-
106
- # Fall back to parent implementation
107
- return super().start_server(port=port, host=host, server_id=server_id)
108
-
109
- def stop_server(self, port: int = None) -> bool:
110
- """Stop Socket.IO server, handling daemon-style servers.
111
-
112
- WHY: The daemon server needs special handling for stopping.
113
- """
114
- # Check if daemon is running
115
- if self.daemon_pidfile_path.exists():
116
- print("🔄 Stopping daemon server", end="")
117
- try:
118
- with open(self.daemon_pidfile_path) as f:
119
- pid = int(f.read().strip())
120
- print(f" (PID: {pid})...")
121
-
122
- result = subprocess.run(
123
- [sys.executable, str(self.daemon_script), "stop"],
124
- capture_output=True,
125
- text=True,
126
- check=False,
127
- )
128
-
129
- if result.returncode == 0:
130
- print("✅ Daemon server stopped successfully")
131
- return True
132
- print(f"❌ Failed to stop daemon: {result.stderr}")
133
- except Exception as e:
134
- print(f"❌ Error stopping daemon: {e}")
135
-
136
- # Fall back to parent implementation for non-daemon servers
137
- return super().stop_server(port=port)
138
-
139
- def restart_server(self, port: int = None) -> bool:
140
- """Restart Socket.IO server, handling daemon-style servers.
141
-
142
- WHY: The daemon server needs special handling for restarting.
143
- """
144
- # Check if daemon is running
145
- if self.daemon_pidfile_path.exists():
146
- try:
147
- with open(self.daemon_pidfile_path) as f:
148
- pid = int(f.read().strip())
149
-
150
- print(f"🔄 Stopping daemon server (PID: {pid})...")
151
-
152
- # Stop the daemon
153
- result = subprocess.run(
154
- [sys.executable, str(self.daemon_script), "stop"],
155
- capture_output=True,
156
- text=True,
157
- check=False,
158
- )
159
-
160
- if result.returncode == 0:
161
- print("✅ Daemon server stopped successfully")
162
- # Wait a moment for clean shutdown
163
- import time
164
-
165
- time.sleep(1)
166
-
167
- # Start it again
168
- return self.start_server(port=port)
169
- print(f"❌ Failed to stop daemon: {result.stderr}")
170
- return False
171
-
172
- except Exception as e:
173
- print(f"❌ Error restarting daemon: {e}")
174
- return False
175
- else:
176
- print(
177
- f"❌ No daemon server found (no PID file at {self.daemon_pidfile_path})"
178
- )
179
-
180
- # Fall back to parent implementation for non-daemon servers
181
- return super().restart_server(port=port)
182
-
183
- # Re-export for use by CLI
184
- __all__ = ["ServerManager"]
185
-
186
- except ImportError:
187
- # Fallback: If tools/admin version not available, provide basic implementation
188
- # that delegates to socketio_daemon.py
189
-
190
- import subprocess
191
- from typing import Any, Dict, List, Optional
192
-
193
- class ServerManager:
194
- """Fallback ServerManager that uses socketio_daemon.py.
195
-
196
- WHY: This fallback ensures the monitor CLI command works even if
197
- the full ServerManager from tools/admin is not available. It provides
198
- basic functionality by delegating to the socketio_daemon.py script.
199
- """
200
-
201
- def __init__(self):
202
- self.daemon_script = script_dir / "socketio_daemon.py"
203
- self.base_port = 8765
204
-
205
- def start_server(
206
- self,
207
- port: Optional[int] = None,
208
- host: str = "localhost",
209
- server_id: Optional[str] = None,
210
- ) -> bool:
211
- """Start the Socket.IO server using daemon script.
212
-
213
- WHY: The daemon script handles port selection and process management,
214
- so we delegate to it for starting servers.
215
- """
216
- if port and port != self.base_port:
217
- print(f"Note: Daemon only supports default port {self.base_port}")
218
- print(f"Starting on port {self.base_port} instead...")
219
-
220
- try:
221
- result = subprocess.run(
222
- [sys.executable, str(self.daemon_script), "start"],
223
- capture_output=True,
224
- text=True,
225
- check=False,
226
- )
227
-
228
- if result.returncode == 0:
229
- print("Socket.IO server started successfully")
230
- return True
231
- print(f"Failed to start server: {result.stderr}")
232
- return False
233
-
234
- except Exception as e:
235
- print(f"Error starting server: {e}")
236
- return False
237
-
238
- def stop_server(self, port: Optional[int] = None) -> bool:
239
- """Stop the Socket.IO server using daemon script."""
240
- if port and port != self.base_port:
241
- print(f"Note: Daemon only supports default port {self.base_port}")
242
-
243
- try:
244
- result = subprocess.run(
245
- [sys.executable, str(self.daemon_script), "stop"],
246
- capture_output=True,
247
- text=True,
248
- check=False,
249
- )
250
-
251
- if result.returncode == 0:
252
- print("Socket.IO server stopped successfully")
253
- return True
254
- print(f"Failed to stop server: {result.stderr}")
255
- return False
256
-
257
- except Exception as e:
258
- print(f"Error stopping server: {e}")
259
- return False
260
-
261
- def restart_server(self, port: Optional[int] = None) -> bool:
262
- """Restart the Socket.IO server using daemon script."""
263
- if port and port != self.base_port:
264
- print(f"Note: Daemon only supports default port {self.base_port}")
265
-
266
- try:
267
- result = subprocess.run(
268
- [sys.executable, str(self.daemon_script), "restart"],
269
- capture_output=True,
270
- text=True,
271
- check=False,
272
- )
273
-
274
- if result.returncode == 0:
275
- print("Socket.IO server restarted successfully")
276
- return True
277
- print(f"Failed to restart server: {result.stderr}")
278
- return False
279
-
280
- except Exception as e:
281
- print(f"Error restarting server: {e}")
282
- return False
283
-
284
- def list_running_servers(self) -> List[Dict[str, Any]]:
285
- """List running servers using daemon script status."""
286
- try:
287
- result = subprocess.run(
288
- [sys.executable, str(self.daemon_script), "status"],
289
- capture_output=True,
290
- text=True,
291
- check=False,
292
- )
293
-
294
- # Parse status output to determine if server is running
295
- if "is running" in result.stdout:
296
- # Extract port from output if possible
297
- port = self.base_port
298
- for line in result.stdout.split("\n"):
299
- if "port" in line.lower():
300
- try:
301
- # Try to extract port number
302
- import re
303
-
304
- match = re.search(r"port\s+(\d+)", line.lower())
305
- if match:
306
- port = int(match.group(1))
307
- except:
308
- pass
309
-
310
- return [
311
- {
312
- "port": port,
313
- "server_id": "daemon-socketio",
314
- "status": "running",
315
- }
316
- ]
317
- return []
318
-
319
- except Exception as e:
320
- print(f"Error checking server status: {e}")
321
- return []
322
-
323
- def get_server_info(self, port: int) -> Optional[Dict[str, Any]]:
324
- """Get information about a server on a specific port."""
325
- servers = self.list_running_servers()
326
- for server in servers:
327
- if server.get("port") == port:
328
- return server
329
- return None
330
-
331
-
332
- # If running as a script, delegate to the actual socketio_server_manager.py
333
- if __name__ == "__main__":
334
- try:
335
- # Try to run the actual script from tools/admin
336
- import subprocess
337
-
338
- tools_script = tools_admin_dir / "socketio_server_manager.py"
339
- if tools_script.exists():
340
- subprocess.run(
341
- [sys.executable, str(tools_script)] + sys.argv[1:], check=False
342
- )
343
- else:
344
- print("Socket.IO server manager not found in tools/admin")
345
- print("Please ensure claude-mpm is properly installed")
346
- sys.exit(1)
347
- except Exception as e:
348
- print(f"Error running server manager: {e}")
349
- sys.exit(1)