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.
- claude_mpm/VERSION +1 -1
- claude_mpm/cli/commands/dashboard.py +59 -126
- claude_mpm/cli/commands/monitor.py +82 -212
- claude_mpm/cli/commands/run.py +33 -33
- claude_mpm/cli/parsers/monitor_parser.py +12 -2
- claude_mpm/dashboard/static/css/code-tree.css +8 -16
- claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/file-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/code-tree.js +692 -114
- claude_mpm/dashboard/static/js/components/file-viewer.js +538 -0
- claude_mpm/dashboard/static/js/components/module-viewer.js +26 -0
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +166 -14
- claude_mpm/dashboard/static/js/dashboard.js +108 -91
- claude_mpm/dashboard/static/js/socket-client.js +9 -7
- claude_mpm/dashboard/templates/index.html +2 -7
- claude_mpm/hooks/claude_hooks/hook_handler.py +1 -11
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +54 -59
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +112 -72
- claude_mpm/services/agents/deployment/agent_template_builder.py +0 -1
- claude_mpm/services/cli/unified_dashboard_manager.py +354 -0
- claude_mpm/services/monitor/__init__.py +20 -0
- claude_mpm/services/monitor/daemon.py +378 -0
- claude_mpm/services/monitor/event_emitter.py +342 -0
- claude_mpm/services/monitor/handlers/__init__.py +20 -0
- claude_mpm/services/monitor/handlers/code_analysis.py +334 -0
- claude_mpm/services/monitor/handlers/dashboard.py +298 -0
- claude_mpm/services/monitor/handlers/hooks.py +491 -0
- claude_mpm/services/monitor/management/__init__.py +18 -0
- claude_mpm/services/monitor/management/health.py +124 -0
- claude_mpm/services/monitor/management/lifecycle.py +338 -0
- claude_mpm/services/monitor/server.py +596 -0
- claude_mpm/tools/code_tree_analyzer.py +33 -17
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/RECORD +42 -37
- claude_mpm/cli/commands/socketio_monitor.py +0 -233
- claude_mpm/scripts/socketio_daemon.py +0 -571
- claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
- claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
- claude_mpm/scripts/socketio_server_manager.py +0 -349
- claude_mpm/services/cli/dashboard_launcher.py +0 -423
- claude_mpm/services/cli/socketio_manager.py +0 -595
- claude_mpm/services/dashboard/stable_server.py +0 -1020
- claude_mpm/services/socketio/monitor_server.py +0 -505
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.12.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.2.
|
|
1
|
+
4.2.11
|
|
@@ -5,21 +5,20 @@ WHY: This module provides CLI commands for managing the web dashboard interface,
|
|
|
5
5
|
allowing users to start, stop, check status, and open the dashboard in a browser.
|
|
6
6
|
|
|
7
7
|
DESIGN DECISIONS:
|
|
8
|
-
- Use
|
|
8
|
+
- Use UnifiedMonitorDaemon for integrated dashboard and monitoring
|
|
9
9
|
- Support both foreground and background operation modes
|
|
10
|
-
- Integrate with
|
|
10
|
+
- Integrate with EventBus for real-time event streaming
|
|
11
11
|
- Provide browser auto-opening functionality
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
import signal
|
|
15
15
|
import sys
|
|
16
|
-
import time
|
|
17
16
|
from typing import Optional
|
|
18
17
|
|
|
19
18
|
from ...constants import DashboardCommands
|
|
20
|
-
from ...services.cli.
|
|
19
|
+
from ...services.cli.unified_dashboard_manager import UnifiedDashboardManager
|
|
20
|
+
from ...services.monitor.daemon import UnifiedMonitorDaemon
|
|
21
21
|
from ...services.port_manager import PortManager
|
|
22
|
-
from ...services.socketio.dashboard_server import DashboardServer
|
|
23
22
|
from ..shared import BaseCommand, CommandResult
|
|
24
23
|
|
|
25
24
|
|
|
@@ -28,7 +27,7 @@ class DashboardCommand(BaseCommand):
|
|
|
28
27
|
|
|
29
28
|
def __init__(self):
|
|
30
29
|
super().__init__("dashboard")
|
|
31
|
-
self.
|
|
30
|
+
self.dashboard_manager = UnifiedDashboardManager(self.logger)
|
|
32
31
|
self.port_manager = PortManager()
|
|
33
32
|
self.server = None
|
|
34
33
|
|
|
@@ -79,20 +78,20 @@ class DashboardCommand(BaseCommand):
|
|
|
79
78
|
)
|
|
80
79
|
|
|
81
80
|
# Check if dashboard is already running
|
|
82
|
-
if self.
|
|
83
|
-
dashboard_url = self.
|
|
81
|
+
if self.dashboard_manager.is_dashboard_running(port):
|
|
82
|
+
dashboard_url = self.dashboard_manager.get_dashboard_url(port)
|
|
84
83
|
return CommandResult.success_result(
|
|
85
84
|
f"Dashboard already running at {dashboard_url}",
|
|
86
85
|
data={"url": dashboard_url, "port": port},
|
|
87
86
|
)
|
|
88
87
|
|
|
89
88
|
if background:
|
|
90
|
-
# Use the dashboard
|
|
91
|
-
success, browser_opened = self.
|
|
92
|
-
port=port,
|
|
89
|
+
# Use the unified dashboard manager for background mode
|
|
90
|
+
success, browser_opened = self.dashboard_manager.start_dashboard(
|
|
91
|
+
port=port, background=True, open_browser=True
|
|
93
92
|
)
|
|
94
93
|
if success:
|
|
95
|
-
dashboard_url = self.
|
|
94
|
+
dashboard_url = self.dashboard_manager.get_dashboard_url(port)
|
|
96
95
|
return CommandResult.success_result(
|
|
97
96
|
f"Dashboard started at {dashboard_url}",
|
|
98
97
|
data={
|
|
@@ -103,110 +102,44 @@ class DashboardCommand(BaseCommand):
|
|
|
103
102
|
)
|
|
104
103
|
return CommandResult.error_result("Failed to start dashboard in background")
|
|
105
104
|
|
|
106
|
-
# Run in foreground mode
|
|
107
|
-
|
|
105
|
+
# Run in foreground mode using unified monitor daemon
|
|
106
|
+
try:
|
|
107
|
+
self.logger.info("Starting unified monitor daemon with dashboard...")
|
|
108
|
+
print(f"Starting unified dashboard and monitor on {host}:{port}...")
|
|
109
|
+
print("Press Ctrl+C to stop the server")
|
|
110
|
+
print(
|
|
111
|
+
"\n✅ Using unified daemon - includes dashboard, monitoring, and EventBus integration\n"
|
|
112
|
+
)
|
|
108
113
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
try:
|
|
112
|
-
self.logger.info(
|
|
113
|
-
"Starting stable dashboard server (no monitor dependency)..."
|
|
114
|
-
)
|
|
115
|
-
print(f"Starting stable dashboard server on {host}:{port}...")
|
|
116
|
-
print("Press Ctrl+C to stop the server")
|
|
117
|
-
print("\n✅ Using stable server - works without monitor service\n")
|
|
118
|
-
|
|
119
|
-
# Create and run the stable server
|
|
120
|
-
from ...services.dashboard.stable_server import StableDashboardServer
|
|
121
|
-
|
|
122
|
-
stable_server = StableDashboardServer(host=host, port=port, debug=debug)
|
|
123
|
-
|
|
124
|
-
# Set up signal handlers for graceful shutdown
|
|
125
|
-
def signal_handler(signum, frame):
|
|
126
|
-
print("\nShutting down dashboard server...")
|
|
127
|
-
sys.exit(0)
|
|
128
|
-
|
|
129
|
-
signal.signal(signal.SIGINT, signal_handler)
|
|
130
|
-
signal.signal(signal.SIGTERM, signal_handler)
|
|
131
|
-
|
|
132
|
-
# Run the server (blocking)
|
|
133
|
-
result = stable_server.run()
|
|
134
|
-
if result:
|
|
135
|
-
# Server ran successfully and stopped normally
|
|
136
|
-
server_started = True
|
|
137
|
-
return CommandResult.success_result("Dashboard server stopped")
|
|
138
|
-
# Server failed to start (e.g., couldn't find templates)
|
|
139
|
-
server_started = False
|
|
140
|
-
self.logger.warning(
|
|
141
|
-
"Stable server failed to start, trying advanced server..."
|
|
142
|
-
)
|
|
114
|
+
# Create and start the unified monitor daemon
|
|
115
|
+
daemon = UnifiedMonitorDaemon(host=host, port=port, daemon_mode=False)
|
|
143
116
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
# Set up signal handlers for graceful shutdown
|
|
171
|
-
def signal_handler(signum, frame):
|
|
172
|
-
print("\nShutting down dashboard server...")
|
|
173
|
-
if self.server:
|
|
174
|
-
self.server.stop_sync()
|
|
175
|
-
sys.exit(0)
|
|
176
|
-
|
|
177
|
-
signal.signal(signal.SIGINT, signal_handler)
|
|
178
|
-
signal.signal(signal.SIGTERM, signal_handler)
|
|
179
|
-
|
|
180
|
-
# Start the server (this starts in background thread)
|
|
181
|
-
self.server.start_sync()
|
|
182
|
-
|
|
183
|
-
# Keep the main thread alive while server is running
|
|
184
|
-
# The server runs in a background thread, so we need to block here
|
|
185
|
-
try:
|
|
186
|
-
while self.server.is_running():
|
|
187
|
-
time.sleep(1)
|
|
188
|
-
except KeyboardInterrupt:
|
|
189
|
-
# Ctrl+C pressed, stop the server
|
|
190
|
-
pass
|
|
191
|
-
|
|
192
|
-
# Server has stopped or user interrupted
|
|
193
|
-
if self.server:
|
|
194
|
-
self.server.stop_sync()
|
|
195
|
-
|
|
196
|
-
return CommandResult.success_result("Dashboard server stopped")
|
|
197
|
-
|
|
198
|
-
except KeyboardInterrupt:
|
|
199
|
-
print("\nDashboard server stopped by user")
|
|
200
|
-
return CommandResult.success_result("Dashboard server stopped")
|
|
201
|
-
except Exception as e:
|
|
202
|
-
# If both servers fail, provide helpful error message
|
|
203
|
-
error_msg = f"Failed to start dashboard: {e}\n\n"
|
|
204
|
-
error_msg += "💡 Troubleshooting tips:\n"
|
|
205
|
-
error_msg += f" - Check if port {port} is already in use\n"
|
|
206
|
-
error_msg += " - Try running with --stable flag for standalone mode\n"
|
|
207
|
-
error_msg += " - Use --debug flag for more details\n"
|
|
208
|
-
return CommandResult.error_result(error_msg)
|
|
209
|
-
return None
|
|
117
|
+
# Set up signal handlers for graceful shutdown
|
|
118
|
+
def signal_handler(signum, frame):
|
|
119
|
+
print("\nShutting down unified monitor daemon...")
|
|
120
|
+
daemon.stop()
|
|
121
|
+
sys.exit(0)
|
|
122
|
+
|
|
123
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
124
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
125
|
+
|
|
126
|
+
# Start the daemon (blocking in foreground mode)
|
|
127
|
+
success = daemon.start()
|
|
128
|
+
if success:
|
|
129
|
+
return CommandResult.success_result("Unified monitor daemon stopped")
|
|
130
|
+
return CommandResult.error_result("Failed to start unified monitor daemon")
|
|
131
|
+
|
|
132
|
+
except KeyboardInterrupt:
|
|
133
|
+
print("\nUnified monitor daemon stopped by user")
|
|
134
|
+
return CommandResult.success_result("Unified monitor daemon stopped")
|
|
135
|
+
except Exception as e:
|
|
136
|
+
self.logger.error(f"Unified monitor daemon failed: {e}")
|
|
137
|
+
error_msg = f"Failed to start unified monitor daemon: {e}\n\n"
|
|
138
|
+
error_msg += "💡 Troubleshooting tips:\n"
|
|
139
|
+
error_msg += f" - Check if port {port} is already in use\n"
|
|
140
|
+
error_msg += " - Use --debug flag for more details\n"
|
|
141
|
+
error_msg += " - Check that all dependencies are installed\n"
|
|
142
|
+
return CommandResult.error_result(error_msg)
|
|
210
143
|
|
|
211
144
|
def _stop_dashboard(self, args) -> CommandResult:
|
|
212
145
|
"""Stop the dashboard server."""
|
|
@@ -214,10 +147,10 @@ class DashboardCommand(BaseCommand):
|
|
|
214
147
|
|
|
215
148
|
self.logger.info(f"Stopping dashboard on port {port}")
|
|
216
149
|
|
|
217
|
-
if not self.
|
|
150
|
+
if not self.dashboard_manager.is_dashboard_running(port):
|
|
218
151
|
return CommandResult.success_result(f"No dashboard running on port {port}")
|
|
219
152
|
|
|
220
|
-
if self.
|
|
153
|
+
if self.dashboard_manager.stop_dashboard(port):
|
|
221
154
|
return CommandResult.success_result(f"Dashboard stopped on port {port}")
|
|
222
155
|
|
|
223
156
|
return CommandResult.error_result(f"Failed to stop dashboard on port {port}")
|
|
@@ -229,7 +162,7 @@ class DashboardCommand(BaseCommand):
|
|
|
229
162
|
|
|
230
163
|
# Check default port first
|
|
231
164
|
default_port = 8765
|
|
232
|
-
dashboard_running = self.
|
|
165
|
+
dashboard_running = self.dashboard_manager.is_dashboard_running(default_port)
|
|
233
166
|
|
|
234
167
|
status_data = {
|
|
235
168
|
"running": dashboard_running,
|
|
@@ -237,17 +170,17 @@ class DashboardCommand(BaseCommand):
|
|
|
237
170
|
}
|
|
238
171
|
|
|
239
172
|
if dashboard_running:
|
|
240
|
-
status_data["url"] = self.
|
|
173
|
+
status_data["url"] = self.dashboard_manager.get_dashboard_url(default_port)
|
|
241
174
|
|
|
242
175
|
# Check all ports if requested
|
|
243
176
|
if show_ports:
|
|
244
177
|
port_status = {}
|
|
245
178
|
for port in range(8765, 8786):
|
|
246
|
-
is_running = self.
|
|
179
|
+
is_running = self.dashboard_manager.is_dashboard_running(port)
|
|
247
180
|
port_status[port] = {
|
|
248
181
|
"running": is_running,
|
|
249
182
|
"url": (
|
|
250
|
-
self.
|
|
183
|
+
self.dashboard_manager.get_dashboard_url(port)
|
|
251
184
|
if is_running
|
|
252
185
|
else None
|
|
253
186
|
),
|
|
@@ -280,14 +213,14 @@ class DashboardCommand(BaseCommand):
|
|
|
280
213
|
port = getattr(args, "port", 8765)
|
|
281
214
|
|
|
282
215
|
# Check if dashboard is running
|
|
283
|
-
if not self.
|
|
216
|
+
if not self.dashboard_manager.is_dashboard_running(port):
|
|
284
217
|
self.logger.info("Dashboard not running, starting it first...")
|
|
285
218
|
# Start dashboard in background
|
|
286
|
-
success, browser_opened = self.
|
|
287
|
-
port=port,
|
|
219
|
+
success, browser_opened = self.dashboard_manager.start_dashboard(
|
|
220
|
+
port=port, background=True, open_browser=True
|
|
288
221
|
)
|
|
289
222
|
if success:
|
|
290
|
-
dashboard_url = self.
|
|
223
|
+
dashboard_url = self.dashboard_manager.get_dashboard_url(port)
|
|
291
224
|
return CommandResult.success_result(
|
|
292
225
|
f"Dashboard started and opened at {dashboard_url}",
|
|
293
226
|
data={
|
|
@@ -298,8 +231,8 @@ class DashboardCommand(BaseCommand):
|
|
|
298
231
|
)
|
|
299
232
|
return CommandResult.error_result("Failed to start and open dashboard")
|
|
300
233
|
# Dashboard already running, just open browser
|
|
301
|
-
dashboard_url = self.
|
|
302
|
-
if self.
|
|
234
|
+
dashboard_url = self.dashboard_manager.get_dashboard_url(port)
|
|
235
|
+
if self.dashboard_manager.open_browser(dashboard_url):
|
|
303
236
|
return CommandResult.success_result(
|
|
304
237
|
f"Opened dashboard at {dashboard_url}",
|
|
305
238
|
data={"url": dashboard_url, "port": port},
|
|
@@ -1,36 +1,31 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Monitor command implementation for claude-mpm.
|
|
3
3
|
|
|
4
|
-
WHY: This module provides CLI commands for managing the
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
WHY: This module provides CLI commands for managing the unified monitoring daemon,
|
|
5
|
+
providing a single stable way to launch all monitoring functionality including
|
|
6
|
+
HTTP dashboard, Socket.IO events, real AST analysis, and Claude Code hook ingestion.
|
|
7
7
|
|
|
8
8
|
DESIGN DECISIONS:
|
|
9
|
-
- Use
|
|
10
|
-
-
|
|
11
|
-
- Support
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
- Use UnifiedMonitorDaemon for single stable monitoring service
|
|
10
|
+
- Single port (8765) for all functionality
|
|
11
|
+
- Support both foreground and daemon modes
|
|
12
|
+
- Real AST analysis using CodeTreeAnalyzer
|
|
13
|
+
- Integrated dashboard and Socket.IO server
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
import signal
|
|
17
|
-
import sys
|
|
18
|
-
import time
|
|
19
16
|
from typing import Optional
|
|
20
17
|
|
|
21
18
|
from ...constants import MonitorCommands
|
|
22
|
-
from ...services.
|
|
23
|
-
from ...services.socketio.monitor_server import MonitorServer
|
|
19
|
+
from ...services.monitor.daemon import UnifiedMonitorDaemon
|
|
24
20
|
from ..shared import BaseCommand, CommandResult
|
|
25
21
|
|
|
26
22
|
|
|
27
23
|
class MonitorCommand(BaseCommand):
|
|
28
|
-
"""Monitor command for managing the
|
|
24
|
+
"""Monitor command for managing the unified monitoring daemon."""
|
|
29
25
|
|
|
30
26
|
def __init__(self):
|
|
31
27
|
super().__init__("monitor")
|
|
32
|
-
self.
|
|
33
|
-
self.server = None
|
|
28
|
+
self.daemon = None
|
|
34
29
|
|
|
35
30
|
def validate_args(self, args) -> Optional[str]:
|
|
36
31
|
"""Validate command arguments."""
|
|
@@ -43,9 +38,9 @@ class MonitorCommand(BaseCommand):
|
|
|
43
38
|
return None
|
|
44
39
|
|
|
45
40
|
def run(self, args) -> CommandResult:
|
|
46
|
-
"""Execute the monitor command using
|
|
41
|
+
"""Execute the monitor command using unified monitoring daemon."""
|
|
47
42
|
try:
|
|
48
|
-
self.logger.info("Monitor command using
|
|
43
|
+
self.logger.info("Monitor command using unified monitoring daemon")
|
|
49
44
|
|
|
50
45
|
# Handle default case (no subcommand) - default to status
|
|
51
46
|
if not hasattr(args, "monitor_command") or not args.monitor_command:
|
|
@@ -60,8 +55,6 @@ class MonitorCommand(BaseCommand):
|
|
|
60
55
|
return self._restart_monitor(args)
|
|
61
56
|
if args.monitor_command == MonitorCommands.STATUS.value:
|
|
62
57
|
return self._status_monitor(args)
|
|
63
|
-
if args.monitor_command == MonitorCommands.PORT.value:
|
|
64
|
-
return self._start_monitor_on_port(args)
|
|
65
58
|
|
|
66
59
|
return CommandResult.error_result(
|
|
67
60
|
f"Unknown monitor command: {args.monitor_command}"
|
|
@@ -72,229 +65,106 @@ class MonitorCommand(BaseCommand):
|
|
|
72
65
|
return CommandResult.error_result(f"Error executing monitor command: {e}")
|
|
73
66
|
|
|
74
67
|
def _start_monitor(self, args) -> CommandResult:
|
|
75
|
-
"""Start the monitor
|
|
76
|
-
port = getattr(args, "port",
|
|
68
|
+
"""Start the unified monitor daemon."""
|
|
69
|
+
port = getattr(args, "port", None)
|
|
70
|
+
if port is None:
|
|
71
|
+
port = 8765 # Default to 8765 for unified monitor
|
|
77
72
|
host = getattr(args, "host", "localhost")
|
|
78
|
-
background = getattr(
|
|
79
|
-
args, "background", True
|
|
80
|
-
) # Default to background for monitor
|
|
81
73
|
|
|
74
|
+
# Check for explicit foreground flag first, then background flag
|
|
75
|
+
# Default to daemon/background mode if neither specified
|
|
76
|
+
if getattr(args, "foreground", False):
|
|
77
|
+
daemon_mode = False
|
|
78
|
+
elif getattr(args, "background", None) is not None:
|
|
79
|
+
daemon_mode = getattr(args, "background", False)
|
|
80
|
+
else:
|
|
81
|
+
# Default to daemon/background mode
|
|
82
|
+
daemon_mode = True
|
|
83
|
+
|
|
84
|
+
mode_str = "background/daemon" if daemon_mode else "foreground"
|
|
82
85
|
self.logger.info(
|
|
83
|
-
f"Starting monitor
|
|
86
|
+
f"Starting unified monitor daemon on {host}:{port} (mode: {mode_str})"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Create unified monitor daemon
|
|
90
|
+
self.daemon = UnifiedMonitorDaemon(
|
|
91
|
+
host=host, port=port, daemon_mode=daemon_mode
|
|
84
92
|
)
|
|
85
93
|
|
|
86
|
-
# Check if
|
|
87
|
-
if self.
|
|
94
|
+
# Check if already running
|
|
95
|
+
if self.daemon.lifecycle.is_running():
|
|
96
|
+
existing_pid = self.daemon.lifecycle.get_pid()
|
|
88
97
|
return CommandResult.success_result(
|
|
89
|
-
f"
|
|
90
|
-
data={
|
|
98
|
+
f"Unified monitor daemon already running with PID {existing_pid}",
|
|
99
|
+
data={
|
|
100
|
+
"url": f"http://{host}:{port}",
|
|
101
|
+
"port": port,
|
|
102
|
+
"pid": existing_pid,
|
|
103
|
+
},
|
|
91
104
|
)
|
|
92
105
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
self.server = MonitorServer(host=host, port=port)
|
|
97
|
-
if self.server.start_sync():
|
|
98
|
-
return CommandResult.success_result(
|
|
99
|
-
f"Monitor server started on {host}:{port}",
|
|
100
|
-
data={"url": f"http://{host}:{port}", "port": port},
|
|
101
|
-
)
|
|
102
|
-
return CommandResult.error_result("Failed to start monitor server")
|
|
103
|
-
except Exception as e:
|
|
104
|
-
return CommandResult.error_result(
|
|
105
|
-
f"Failed to start monitor server: {e}"
|
|
106
|
-
)
|
|
107
|
-
else:
|
|
108
|
-
# Run monitor in foreground mode
|
|
109
|
-
try:
|
|
110
|
-
print(f"Starting monitor server on {host}:{port}...")
|
|
111
|
-
print("Press Ctrl+C to stop the server")
|
|
112
|
-
|
|
113
|
-
self.server = MonitorServer(host=host, port=port)
|
|
114
|
-
|
|
115
|
-
# Set up signal handlers for graceful shutdown
|
|
116
|
-
def signal_handler(signum, frame):
|
|
117
|
-
print("\nShutting down monitor server...")
|
|
118
|
-
if self.server:
|
|
119
|
-
self.server.stop_sync()
|
|
120
|
-
sys.exit(0)
|
|
121
|
-
|
|
122
|
-
signal.signal(signal.SIGINT, signal_handler)
|
|
123
|
-
signal.signal(signal.SIGTERM, signal_handler)
|
|
124
|
-
|
|
125
|
-
# Start the server
|
|
126
|
-
if not self.server.start_sync():
|
|
127
|
-
return CommandResult.error_result("Failed to start monitor server")
|
|
128
|
-
|
|
129
|
-
# Keep the main thread alive while server is running
|
|
130
|
-
try:
|
|
131
|
-
while self.server.is_running():
|
|
132
|
-
time.sleep(1)
|
|
133
|
-
except KeyboardInterrupt:
|
|
134
|
-
pass
|
|
135
|
-
|
|
136
|
-
# Stop the server
|
|
137
|
-
if self.server:
|
|
138
|
-
self.server.stop_sync()
|
|
139
|
-
|
|
140
|
-
return CommandResult.success_result("Monitor server stopped")
|
|
141
|
-
|
|
142
|
-
except KeyboardInterrupt:
|
|
143
|
-
print("\nMonitor server stopped by user")
|
|
144
|
-
return CommandResult.success_result("Monitor server stopped")
|
|
145
|
-
except Exception as e:
|
|
146
|
-
return CommandResult.error_result(
|
|
147
|
-
f"Failed to start monitor server: {e}"
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
def _stop_monitor(self, args) -> CommandResult:
|
|
151
|
-
"""Stop the monitor server."""
|
|
152
|
-
port = getattr(args, "port", 8766)
|
|
153
|
-
|
|
154
|
-
self.logger.info(f"Stopping monitor server on port {port}")
|
|
155
|
-
|
|
156
|
-
if not self._is_monitor_running(port):
|
|
106
|
+
# Start the daemon
|
|
107
|
+
if self.daemon.start():
|
|
108
|
+
mode_info = " in background" if daemon_mode else " in foreground"
|
|
157
109
|
return CommandResult.success_result(
|
|
158
|
-
f"
|
|
110
|
+
f"Unified monitor daemon started on {host}:{port}{mode_info}",
|
|
111
|
+
data={"url": f"http://{host}:{port}", "port": port, "mode": mode_str},
|
|
159
112
|
)
|
|
113
|
+
return CommandResult.error_result("Failed to start unified monitor daemon")
|
|
160
114
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
self.server.stop_sync()
|
|
165
|
-
return CommandResult.success_result(
|
|
166
|
-
f"Monitor server stopped on port {port}"
|
|
167
|
-
)
|
|
168
|
-
except Exception as e:
|
|
169
|
-
return CommandResult.error_result(f"Error stopping monitor server: {e}")
|
|
170
|
-
|
|
171
|
-
# If we don't have a server instance, try port manager cleanup
|
|
172
|
-
try:
|
|
173
|
-
self.port_manager.cleanup_dead_instances()
|
|
174
|
-
active_instances = self.port_manager.list_active_instances()
|
|
175
|
-
|
|
176
|
-
# Look for instances on the target port
|
|
177
|
-
for instance in active_instances:
|
|
178
|
-
if (
|
|
179
|
-
instance.get("port") == port
|
|
180
|
-
and instance.get("service_type") == "monitor"
|
|
181
|
-
):
|
|
182
|
-
# Found an instance, but we can't stop it directly
|
|
183
|
-
# This would need to be implemented with a proper process manager
|
|
184
|
-
return CommandResult.error_result(
|
|
185
|
-
f"Monitor server found on port {port} but cannot be stopped "
|
|
186
|
-
"(no direct control - you may need to kill the process manually)"
|
|
187
|
-
)
|
|
115
|
+
def _stop_monitor(self, args) -> CommandResult:
|
|
116
|
+
"""Stop the unified monitor daemon."""
|
|
117
|
+
self.logger.info("Stopping unified monitor daemon")
|
|
188
118
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
119
|
+
# Create daemon instance to check status and stop
|
|
120
|
+
daemon = UnifiedMonitorDaemon()
|
|
192
121
|
|
|
193
|
-
|
|
194
|
-
return CommandResult.
|
|
195
|
-
|
|
196
|
-
|
|
122
|
+
if not daemon.lifecycle.is_running():
|
|
123
|
+
return CommandResult.success_result("No unified monitor daemon running")
|
|
124
|
+
|
|
125
|
+
# Stop the daemon by PID (works for both daemon and foreground mode)
|
|
126
|
+
if daemon.lifecycle.stop_daemon():
|
|
127
|
+
return CommandResult.success_result("Unified monitor daemon stopped")
|
|
128
|
+
return CommandResult.error_result("Failed to stop unified monitor daemon")
|
|
197
129
|
|
|
198
130
|
def _restart_monitor(self, args) -> CommandResult:
|
|
199
|
-
"""Restart the monitor
|
|
200
|
-
self.logger.info("Restarting monitor
|
|
201
|
-
|
|
202
|
-
# Stop first
|
|
203
|
-
stop_result = self._stop_monitor(args)
|
|
204
|
-
if not stop_result.success:
|
|
205
|
-
self.logger.warning(
|
|
206
|
-
"Failed to stop monitor server for restart, proceeding anyway"
|
|
207
|
-
)
|
|
131
|
+
"""Restart the unified monitor daemon."""
|
|
132
|
+
self.logger.info("Restarting unified monitor daemon")
|
|
208
133
|
|
|
209
|
-
#
|
|
210
|
-
|
|
134
|
+
# Create daemon instance
|
|
135
|
+
daemon = UnifiedMonitorDaemon()
|
|
211
136
|
|
|
212
|
-
#
|
|
213
|
-
|
|
137
|
+
# Restart the daemon
|
|
138
|
+
if daemon.restart():
|
|
139
|
+
return CommandResult.success_result("Unified monitor daemon restarted")
|
|
140
|
+
return CommandResult.error_result("Failed to restart unified monitor daemon")
|
|
214
141
|
|
|
215
142
|
def _status_monitor(self, args) -> CommandResult:
|
|
216
|
-
"""Get monitor
|
|
217
|
-
port = getattr(args, "port", 8766)
|
|
143
|
+
"""Get unified monitor daemon status."""
|
|
218
144
|
verbose = getattr(args, "verbose", False)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
status_data = {
|
|
225
|
-
"running": monitor_running,
|
|
226
|
-
"default_port": port,
|
|
227
|
-
"service_type": "monitor",
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if monitor_running:
|
|
231
|
-
status_data["url"] = f"http://localhost:{port}"
|
|
232
|
-
|
|
233
|
-
# Check all ports if requested
|
|
234
|
-
if show_ports:
|
|
235
|
-
port_status = {}
|
|
236
|
-
for check_port in range(8766, 8776): # Monitor port range
|
|
237
|
-
is_running = self._is_monitor_running(check_port)
|
|
238
|
-
port_status[check_port] = {
|
|
239
|
-
"running": is_running,
|
|
240
|
-
"url": f"http://localhost:{check_port}" if is_running else None,
|
|
241
|
-
}
|
|
242
|
-
status_data["ports"] = port_status
|
|
243
|
-
|
|
244
|
-
# Get active instances from port manager
|
|
245
|
-
self.port_manager.cleanup_dead_instances()
|
|
246
|
-
active_instances = self.port_manager.list_active_instances()
|
|
247
|
-
if active_instances:
|
|
248
|
-
monitor_instances = [
|
|
249
|
-
inst
|
|
250
|
-
for inst in active_instances
|
|
251
|
-
if inst.get("service_type") == "monitor"
|
|
252
|
-
]
|
|
253
|
-
if monitor_instances:
|
|
254
|
-
status_data["active_instances"] = monitor_instances
|
|
255
|
-
|
|
256
|
-
if verbose and self.server:
|
|
257
|
-
status_data["server_stats"] = self.server.get_stats()
|
|
145
|
+
|
|
146
|
+
# Create daemon instance to check status
|
|
147
|
+
daemon = UnifiedMonitorDaemon()
|
|
148
|
+
status_data = daemon.status()
|
|
258
149
|
|
|
259
150
|
# Format output message
|
|
260
|
-
if
|
|
261
|
-
message = f"
|
|
151
|
+
if status_data["running"]:
|
|
152
|
+
message = f"Unified monitor daemon is running at http://{status_data['host']}:{status_data['port']}"
|
|
153
|
+
if status_data.get("pid"):
|
|
154
|
+
message += f" (PID: {status_data['pid']})"
|
|
262
155
|
else:
|
|
263
|
-
message = "
|
|
156
|
+
message = "Unified monitor daemon is not running"
|
|
264
157
|
|
|
265
158
|
return CommandResult.success_result(message, data=status_data)
|
|
266
159
|
|
|
267
|
-
def _start_monitor_on_port(self, args) -> CommandResult:
|
|
268
|
-
"""Start monitor server on specific port."""
|
|
269
|
-
port = getattr(args, "port", 8766)
|
|
270
|
-
self.logger.info(f"Starting monitor server on port {port}")
|
|
271
|
-
|
|
272
|
-
# Ensure background mode for port-specific starts
|
|
273
|
-
if not hasattr(args, "background"):
|
|
274
|
-
args.background = True
|
|
275
|
-
|
|
276
|
-
return self._start_monitor(args)
|
|
277
|
-
|
|
278
|
-
def _is_monitor_running(self, port: int) -> bool:
|
|
279
|
-
"""Check if monitor server is running on given port."""
|
|
280
|
-
import socket
|
|
281
|
-
|
|
282
|
-
try:
|
|
283
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
284
|
-
s.settimeout(1)
|
|
285
|
-
result = s.connect_ex(("localhost", port))
|
|
286
|
-
return result == 0
|
|
287
|
-
except Exception:
|
|
288
|
-
return False
|
|
289
|
-
|
|
290
160
|
|
|
291
161
|
def manage_monitor(args):
|
|
292
162
|
"""
|
|
293
163
|
Main entry point for monitor command.
|
|
294
164
|
|
|
295
|
-
The monitor command manages
|
|
296
|
-
|
|
297
|
-
|
|
165
|
+
The monitor command manages the unified monitoring daemon that provides
|
|
166
|
+
a single stable way to launch all monitoring functionality including
|
|
167
|
+
HTTP dashboard, Socket.IO events, real AST analysis, and Claude Code hooks.
|
|
298
168
|
"""
|
|
299
169
|
command = MonitorCommand()
|
|
300
170
|
error = command.validate_args(args)
|