claude-mpm 3.4.3__py3-none-any.whl → 3.4.6__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.
@@ -1,753 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Socket.IO Server Manager - Deployment-agnostic server management.
3
-
4
- This script provides unified management for Socket.IO servers across different deployment scenarios:
5
- - Local development
6
- - PyPI installation
7
- - Docker containers
8
- - System service installation
9
-
10
- Features:
11
- - Start/stop/restart standalone servers
12
- - Version compatibility checking
13
- - Health monitoring and diagnostics
14
- - Multi-instance management
15
- - Automatic dependency installation
16
- """
17
-
18
- import argparse
19
- import json
20
- import os
21
- import signal
22
- import subprocess
23
- import sys
24
- import time
25
- from pathlib import Path
26
- from typing import Dict, List, Optional
27
-
28
- try:
29
- import requests
30
- REQUESTS_AVAILABLE = True
31
- except ImportError:
32
- REQUESTS_AVAILABLE = False
33
-
34
- try:
35
- import psutil
36
- PSUTIL_AVAILABLE = True
37
- except ImportError:
38
- PSUTIL_AVAILABLE = False
39
-
40
-
41
- class ServerManager:
42
- """Manages Socket.IO server instances across different deployment modes."""
43
-
44
- def __init__(self):
45
- self.base_port = 8765
46
- self.max_instances = 5
47
- self.script_dir = Path(__file__).parent
48
- self.project_root = self.script_dir.parent
49
- # Daemon PID file location (used by socketio_daemon.py)
50
- self.daemon_pidfile_path = Path.home() / ".claude-mpm" / "socketio-server.pid"
51
- # Standalone server PID file location pattern
52
- self.standalone_pidfile_pattern = "/tmp/claude_mpm_socketio_{port}.pid"
53
-
54
- def get_server_info(self, port: int) -> Optional[Dict]:
55
- """Get server information from a running instance with daemon compatibility."""
56
- if not REQUESTS_AVAILABLE:
57
- return self._check_daemon_fallback(port)
58
-
59
- try:
60
- response = requests.get(f"http://localhost:{port}/health", timeout=2.0)
61
- if response.status_code == 200:
62
- data = response.json()
63
- # Check if this is a daemon-style response (no 'pid' field)
64
- if 'pid' not in data and 'status' in data:
65
- # Try to get PID from daemon PID file
66
- daemon_pid = self._get_daemon_pid()
67
- if daemon_pid:
68
- data['pid'] = daemon_pid
69
- data['management_style'] = 'daemon'
70
- return data
71
- except Exception as e:
72
- # If HTTP fails, try daemon fallback
73
- return self._check_daemon_fallback(port)
74
- return None
75
-
76
- def list_running_servers(self) -> List[Dict]:
77
- """List all running Socket.IO servers including daemon-style servers."""
78
- running_servers = []
79
-
80
- # Check standard port range
81
- for port in range(self.base_port, self.base_port + self.max_instances):
82
- server_info = self.get_server_info(port)
83
- if server_info:
84
- server_info['port'] = port
85
- running_servers.append(server_info)
86
-
87
- # Also check for daemon-style server specifically
88
- daemon_info = self._get_daemon_server_info()
89
- if daemon_info and not any(s['port'] == daemon_info.get('port', self.base_port) for s in running_servers):
90
- running_servers.append(daemon_info)
91
-
92
- return running_servers
93
-
94
- def find_available_port(self, start_port: int = None) -> int:
95
- """Find the next available port for a new server."""
96
- start_port = start_port or self.base_port
97
-
98
- for port in range(start_port, start_port + self.max_instances):
99
- if not self.get_server_info(port):
100
- return port
101
-
102
- raise RuntimeError(f"No available ports found in range {start_port}-{start_port + self.max_instances}")
103
-
104
- def start_server(self, port: int = None, server_id: str = None,
105
- host: str = "localhost") -> bool:
106
- """Start a standalone Socket.IO server with conflict detection."""
107
-
108
- # Find available port if not specified
109
- if port is None:
110
- try:
111
- port = self.find_available_port()
112
- except RuntimeError as e:
113
- print(f"Error: {e}")
114
- return False
115
-
116
- # Check if server is already running on this port
117
- existing_server = self.get_server_info(port)
118
- if existing_server:
119
- management_style = existing_server.get('management_style', 'http')
120
- server_id_existing = existing_server.get('server_id', 'unknown')
121
-
122
- print(f"❌ Server already running on port {port}")
123
- print(f" Existing server: {server_id_existing} ({management_style}-managed)")
124
-
125
- if management_style == 'daemon':
126
- print(f"💡 To stop daemon server: {self.project_root / 'src' / 'claude_mpm' / 'scripts' / 'socketio_daemon.py'} stop")
127
- else:
128
- print(f"💡 To stop server: {sys.executable} {__file__} stop --port {port}")
129
-
130
- return False
131
-
132
- # Warn if daemon server exists on default port but we're starting on different port
133
- if port != self.base_port and self._get_daemon_pid():
134
- print(f"⚠️ Warning: Daemon server is running on port {self.base_port}, you're starting on port {port}")
135
- print(f" This may cause conflicts. Consider stopping daemon first.")
136
-
137
- # Try different ways to start the server based on deployment
138
- success = False
139
-
140
- # Method 1: Try installed claude-mpm package
141
- try:
142
- cmd = [
143
- sys.executable, "-m", "claude_mpm.services.standalone_socketio_server",
144
- "--host", host,
145
- "--port", str(port)
146
- ]
147
- if server_id:
148
- cmd.extend(["--server-id", server_id])
149
-
150
- print(f"Starting server on {host}:{port} using installed package...")
151
-
152
- # Start in background
153
- subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
154
-
155
- # Wait for server to start
156
- for _ in range(10): # Wait up to 10 seconds
157
- time.sleep(1)
158
- if self.get_server_info(port):
159
- success = True
160
- break
161
-
162
- except Exception as e:
163
- print(f"Failed to start via installed package: {e}")
164
-
165
- # Method 2: Try local development mode
166
- if not success:
167
- try:
168
- server_path = self.project_root / "src" / "claude_mpm" / "services" / "standalone_socketio_server.py"
169
- if server_path.exists():
170
- cmd = [
171
- sys.executable, str(server_path),
172
- "--host", host,
173
- "--port", str(port)
174
- ]
175
- if server_id:
176
- cmd.extend(["--server-id", server_id])
177
-
178
- print(f"Starting server using local development mode...")
179
-
180
- # Set PYTHONPATH for local development
181
- env = os.environ.copy()
182
- src_path = str(self.project_root / "src")
183
- if "PYTHONPATH" in env:
184
- env["PYTHONPATH"] = f"{src_path}:{env['PYTHONPATH']}"
185
- else:
186
- env["PYTHONPATH"] = src_path
187
-
188
- subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=env)
189
-
190
- # Wait for server to start
191
- for _ in range(10):
192
- time.sleep(1)
193
- if self.get_server_info(port):
194
- success = True
195
- break
196
-
197
- except Exception as e:
198
- print(f"Failed to start in development mode: {e}")
199
-
200
- if success:
201
- print(f"✅ Server started successfully on {host}:{port}")
202
- print(f"💡 Management commands:")
203
- print(f" Status: {sys.executable} {__file__} status")
204
- print(f" Stop: {sys.executable} {__file__} stop --port {port}")
205
- return True
206
- else:
207
- print(f"❌ Failed to start server on {host}:{port}")
208
- print(f"💡 Troubleshooting:")
209
- print(f" • Check if port {port} is already in use: lsof -i :{port}")
210
- print(f" • Check server status: {sys.executable} {__file__} status")
211
- print(f" • Try different port: {sys.executable} {__file__} start --port {port + 1}")
212
- return False
213
-
214
- def stop_server(self, port: int = None, server_id: str = None) -> bool:
215
- """Stop a running Socket.IO server with daemon compatibility."""
216
-
217
- if port is None and server_id is None:
218
- print("Must specify either port or server_id")
219
- return False
220
-
221
- # Find server by ID if port not specified
222
- if port is None:
223
- running_servers = self.list_running_servers()
224
- for server in running_servers:
225
- if server.get('server_id') == server_id:
226
- port = server['port']
227
- break
228
-
229
- if port is None:
230
- print(f"Server with ID '{server_id}' not found")
231
- return False
232
-
233
- # Get server info
234
- server_info = self.get_server_info(port)
235
- if not server_info:
236
- # Try daemon-specific stop as fallback
237
- return self._try_daemon_stop(port)
238
-
239
- # Determine management style
240
- management_style = server_info.get('management_style', 'http')
241
-
242
- # Try HTTP-based stop first
243
- pid = server_info.get('pid')
244
- if pid:
245
- try:
246
- # Validate PID before attempting to kill
247
- if self._validate_pid(pid):
248
- os.kill(pid, signal.SIGTERM)
249
- print(f"✅ Sent termination signal to server (PID: {pid})")
250
-
251
- # Wait for server to stop
252
- for i in range(10):
253
- time.sleep(1)
254
- if not self.get_server_info(port):
255
- print(f"✅ Server stopped successfully")
256
- return True
257
- if i == 5: # After 5 seconds, show progress
258
- print(f"⏳ Waiting for server to stop...")
259
-
260
- # Force kill if still running
261
- try:
262
- if self._validate_pid(pid):
263
- os.kill(pid, signal.SIGKILL)
264
- print(f"⚠️ Force killed server (PID: {pid})")
265
- return True
266
- except OSError:
267
- pass
268
-
269
- else:
270
- print(f"⚠️ PID {pid} is no longer valid, trying daemon stop...")
271
- return self._try_daemon_stop(port)
272
-
273
- except OSError as e:
274
- print(f"Error stopping server via PID {pid}: {e}")
275
- if management_style == 'daemon':
276
- print("🔄 Trying daemon-style stop...")
277
- return self._try_daemon_stop(port)
278
-
279
- # If HTTP method failed, try daemon stop
280
- if management_style == 'daemon' or not pid:
281
- print("🔄 Attempting daemon-style stop...")
282
- return self._try_daemon_stop(port)
283
-
284
- print(f"❌ Failed to stop server on port {port}")
285
- print(f"💡 Try using the socketio_daemon.py stop command if this is a daemon-managed server")
286
- return False
287
-
288
- def restart_server(self, port: int = None, server_id: str = None) -> bool:
289
- """Restart a Socket.IO server."""
290
-
291
- # Stop the server first
292
- if self.stop_server(port, server_id):
293
- time.sleep(2) # Give it time to fully stop
294
-
295
- # Start it again
296
- if port is None:
297
- port = self.find_available_port()
298
-
299
- return self.start_server(port)
300
-
301
- return False
302
-
303
- def status(self, verbose: bool = False) -> None:
304
- """Show status of all Socket.IO servers with management style info."""
305
- running_servers = self.list_running_servers()
306
-
307
- if not running_servers:
308
- print("No Socket.IO servers currently running")
309
- print()
310
- print("💡 Management options:")
311
- print(f" • Start with manager: {sys.executable} {__file__} start")
312
- print(f" • Start with daemon: {self.project_root / 'src' / 'claude_mpm' / 'scripts' / 'socketio_daemon.py'} start")
313
- return
314
-
315
- print(f"Found {len(running_servers)} running server(s):")
316
- print()
317
-
318
- for server in running_servers:
319
- port = server['port']
320
- server_id = server.get('server_id', 'unknown')
321
- version = server.get('server_version', 'unknown')
322
- uptime = server.get('uptime_seconds', 0)
323
- clients = server.get('clients_connected', 0)
324
- management_style = server.get('management_style', 'http')
325
-
326
- # Different icons based on management style
327
- icon = "🖥️" if management_style == 'http' else "🔧"
328
-
329
- print(f"{icon} Server ID: {server_id}")
330
- print(f" Port: {port}")
331
- print(f" Version: {version}")
332
- print(f" Management: {management_style}")
333
- print(f" Uptime: {self._format_uptime(uptime)}")
334
- print(f" Clients: {clients}")
335
-
336
- if verbose:
337
- print(f" PID: {server.get('pid', 'unknown')}")
338
- print(f" Host: {server.get('host', 'unknown')}")
339
-
340
- # Show appropriate stop command
341
- if management_style == 'daemon':
342
- print(f" Stop command: {self.project_root / 'src' / 'claude_mpm' / 'scripts' / 'socketio_daemon.py'} stop")
343
- else:
344
- print(f" Stop command: {sys.executable} {__file__} stop --port {port}")
345
-
346
- # Get additional stats (only for HTTP-style servers)
347
- if management_style == 'http':
348
- stats = self._get_server_stats(port)
349
- if stats:
350
- events_processed = stats.get('events', {}).get('total_processed', 0)
351
- clients_served = stats.get('connections', {}).get('total_served', 0)
352
- print(f" Events processed: {events_processed}")
353
- print(f" Total clients served: {clients_served}")
354
-
355
- print()
356
-
357
- def health_check(self, port: int = None) -> bool:
358
- """Perform health check on server(s) with management style awareness."""
359
-
360
- if port:
361
- # Check specific server
362
- server_info = self.get_server_info(port)
363
- if server_info:
364
- status = server_info.get('status', 'unknown')
365
- management_style = server_info.get('management_style', 'http')
366
- server_id = server_info.get('server_id', 'unknown')
367
-
368
- print(f"Server {server_id} on port {port}: {status} ({management_style}-managed)")
369
-
370
- # Additional health info for daemon servers
371
- if management_style == 'daemon':
372
- pid = server_info.get('pid')
373
- if pid and self._validate_pid(pid):
374
- print(f" ✅ Process {pid} is running")
375
- else:
376
- print(f" ❌ Process {pid} is not running")
377
- return False
378
-
379
- return status in ['healthy', 'running']
380
- else:
381
- print(f"No server found on port {port}")
382
- # Try daemon fallback for default port
383
- if port == self.base_port:
384
- daemon_info = self._get_daemon_server_info()
385
- if daemon_info:
386
- print(f" Found daemon server: {daemon_info['server_id']}")
387
- return True
388
- return False
389
- else:
390
- # Check all servers
391
- running_servers = self.list_running_servers()
392
- if not running_servers:
393
- print("No servers running")
394
- print(f"💡 Start a server with: {sys.executable} {__file__} start")
395
- return False
396
-
397
- all_healthy = True
398
- for server in running_servers:
399
- port = server['port']
400
- status = server.get('status', 'unknown')
401
- server_id = server.get('server_id', 'unknown')
402
- management_style = server.get('management_style', 'http')
403
-
404
- health_status = status in ['healthy', 'running']
405
- icon = "✅" if health_status else "❌"
406
-
407
- print(f"{icon} Server {server_id} (port {port}): {status} ({management_style}-managed)")
408
-
409
- if not health_status:
410
- all_healthy = False
411
-
412
- return all_healthy
413
-
414
- def install_dependencies(self) -> bool:
415
- """Install required dependencies for Socket.IO server."""
416
- dependencies = ['python-socketio>=5.11.0', 'aiohttp>=3.9.0', 'requests>=2.25.0']
417
-
418
- print("Installing Socket.IO server dependencies...")
419
-
420
- try:
421
- cmd = [sys.executable, '-m', 'pip', 'install'] + dependencies
422
- result = subprocess.run(cmd, capture_output=True, text=True)
423
-
424
- if result.returncode == 0:
425
- print("✅ Dependencies installed successfully")
426
- return True
427
- else:
428
- print(f"❌ Failed to install dependencies: {result.stderr}")
429
- return False
430
-
431
- except Exception as e:
432
- print(f"❌ Error installing dependencies: {e}")
433
- return False
434
-
435
- def _format_uptime(self, seconds: float) -> str:
436
- """Format uptime in a human-readable way."""
437
- if seconds < 60:
438
- return f"{seconds:.1f}s"
439
- elif seconds < 3600:
440
- return f"{seconds/60:.1f}m"
441
- else:
442
- return f"{seconds/3600:.1f}h"
443
-
444
- def _get_server_stats(self, port: int) -> Optional[Dict]:
445
- """Get detailed server statistics."""
446
- if not REQUESTS_AVAILABLE:
447
- return None
448
-
449
- try:
450
- response = requests.get(f"http://localhost:{port}/stats", timeout=2.0)
451
- if response.status_code == 200:
452
- return response.json()
453
- except Exception:
454
- pass
455
- return None
456
-
457
- def _check_daemon_fallback(self, port: int) -> Optional[Dict]:
458
- """Check for daemon-style server when HTTP fails."""
459
- if port == self.base_port: # Only check daemon for default port
460
- return self._get_daemon_server_info()
461
- return None
462
-
463
- def _get_daemon_pid(self) -> Optional[int]:
464
- """Get PID from daemon PID file."""
465
- try:
466
- if self.daemon_pidfile_path.exists():
467
- with open(self.daemon_pidfile_path, 'r') as f:
468
- content = f.read().strip()
469
- if content.isdigit():
470
- pid = int(content)
471
- # Validate the PID exists
472
- if self._validate_pid(pid):
473
- return pid
474
- except Exception:
475
- pass
476
- return None
477
-
478
- def _get_daemon_server_info(self) -> Optional[Dict]:
479
- """Get server info for daemon-style server."""
480
- daemon_pid = self._get_daemon_pid()
481
- if daemon_pid:
482
- # Basic server info for daemon
483
- info = {
484
- 'pid': daemon_pid,
485
- 'server_id': 'daemon-socketio',
486
- 'management_style': 'daemon',
487
- 'status': 'running',
488
- 'port': self.base_port,
489
- 'server_version': 'daemon-managed'
490
- }
491
-
492
- # Try to get additional process info if psutil is available
493
- if PSUTIL_AVAILABLE:
494
- try:
495
- process = psutil.Process(daemon_pid)
496
- info.update({
497
- 'uptime_seconds': time.time() - process.create_time(),
498
- 'host': 'localhost',
499
- 'process_name': process.name()
500
- })
501
- except:
502
- pass
503
-
504
- return info
505
- return None
506
-
507
- def _validate_pid(self, pid: int) -> bool:
508
- """Validate that a PID represents a running process."""
509
- try:
510
- # Check if process exists
511
- os.kill(pid, 0)
512
- return True
513
- except OSError:
514
- return False
515
-
516
- def _try_daemon_stop(self, port: int) -> bool:
517
- """Try to stop daemon-style server."""
518
- if port != self.base_port:
519
- print(f"⚠️ Daemon management only supports default port {self.base_port}, not {port}")
520
- return False
521
-
522
- daemon_pid = self._get_daemon_pid()
523
- if not daemon_pid:
524
- print(f"❌ No daemon server found (no PID file at {self.daemon_pidfile_path})")
525
- return False
526
-
527
- try:
528
- print(f"🔄 Stopping daemon server (PID: {daemon_pid})...")
529
- os.kill(daemon_pid, signal.SIGTERM)
530
-
531
- # Wait for daemon to stop
532
- for i in range(10):
533
- time.sleep(1)
534
- if not self._validate_pid(daemon_pid):
535
- print(f"✅ Daemon server stopped successfully")
536
- # Clean up PID file
537
- try:
538
- self.daemon_pidfile_path.unlink(missing_ok=True)
539
- except:
540
- pass
541
- return True
542
- if i == 5:
543
- print(f"⏳ Waiting for daemon to stop...")
544
-
545
- # Force kill if still running
546
- if self._validate_pid(daemon_pid):
547
- print(f"⚠️ Force killing daemon server...")
548
- os.kill(daemon_pid, signal.SIGKILL)
549
- time.sleep(1)
550
- if not self._validate_pid(daemon_pid):
551
- print(f"✅ Daemon server force stopped")
552
- try:
553
- self.daemon_pidfile_path.unlink(missing_ok=True)
554
- except:
555
- pass
556
- return True
557
-
558
- except OSError as e:
559
- print(f"❌ Error stopping daemon server: {e}")
560
- return False
561
-
562
- print(f"❌ Failed to stop daemon server")
563
- return False
564
-
565
- def diagnose_conflicts(self, port: int = None) -> None:
566
- """Diagnose server management conflicts and suggest resolutions."""
567
- if port is None:
568
- port = self.base_port
569
-
570
- print(f"🔍 Diagnosing Socket.IO server management on port {port}")
571
- print("=" * 60)
572
-
573
- # Check HTTP-managed server
574
- http_server = None
575
- daemon_server = None
576
-
577
- try:
578
- if REQUESTS_AVAILABLE:
579
- response = requests.get(f"http://localhost:{port}/health", timeout=2.0)
580
- if response.status_code == 200:
581
- data = response.json()
582
- if 'pid' in data:
583
- http_server = data
584
- except:
585
- pass
586
-
587
- # Check daemon-managed server
588
- daemon_pid = self._get_daemon_pid()
589
- if daemon_pid and port == self.base_port:
590
- daemon_server = self._get_daemon_server_info()
591
-
592
- # Analysis
593
- print("📊 Server Analysis:")
594
-
595
- if http_server and daemon_server:
596
- print("⚠️ CONFLICT DETECTED: Both HTTP and daemon servers found!")
597
- print(f" HTTP server: PID {http_server.get('pid')}, ID {http_server.get('server_id')}")
598
- print(f" Daemon server: PID {daemon_server.get('pid')}, ID {daemon_server.get('server_id')}")
599
- print()
600
- print("🔧 Resolution Steps:")
601
- print(" 1. Choose one management approach:")
602
- print(f" • Keep HTTP: {sys.executable} {__file__} stop --port {port} (stops daemon)")
603
- print(f" • Keep daemon: Stop HTTP server first, then use daemon commands")
604
- print()
605
-
606
- elif http_server:
607
- print(f"✅ HTTP-managed server found")
608
- print(f" Server ID: {http_server.get('server_id')}")
609
- print(f" PID: {http_server.get('pid')}")
610
- print(f" Status: {http_server.get('status')}")
611
- print()
612
- print("🔧 Management Commands:")
613
- print(f" • Stop: {sys.executable} {__file__} stop --port {port}")
614
- print(f" • Status: {sys.executable} {__file__} status")
615
- print()
616
-
617
- elif daemon_server:
618
- print(f"✅ Daemon-managed server found")
619
- print(f" PID: {daemon_server.get('pid')}")
620
- print(f" PID file: {self.daemon_pidfile_path}")
621
- print()
622
- print("🔧 Management Commands:")
623
- daemon_script = self.project_root / "src" / "claude_mpm" / "scripts" / "socketio_daemon.py"
624
- print(f" • Stop: {daemon_script} stop")
625
- print(f" • Status: {daemon_script} status")
626
- print(f" • Restart: {daemon_script} restart")
627
- print()
628
-
629
- else:
630
- print("❌ No servers found on specified port")
631
- print()
632
- print("🔧 Start a server:")
633
- print(f" • HTTP-managed: {sys.executable} {__file__} start --port {port}")
634
- daemon_script = self.project_root / "src" / "claude_mpm" / "scripts" / "socketio_daemon.py"
635
- if port == self.base_port:
636
- print(f" • Daemon-managed: {daemon_script} start")
637
- print()
638
-
639
- # Port conflict check
640
- try:
641
- import socket
642
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
643
- result = s.connect_ex(('localhost', port))
644
- if result == 0:
645
- print("🌐 Port Status: IN USE")
646
- if not http_server and not daemon_server:
647
- print(f" ⚠️ Port {port} is occupied by unknown process")
648
- if PSUTIL_AVAILABLE:
649
- print(" 🔍 Use 'lsof -i :{port}' or 'netstat -tulpn | grep {port}' to identify")
650
- else:
651
- print("🌐 Port Status: AVAILABLE")
652
- except:
653
- print("🌐 Port Status: UNKNOWN")
654
-
655
- print()
656
- print("📚 Management Style Comparison:")
657
- print(" HTTP-managed:")
658
- print(" • Pros: Full API, stats, multi-instance support")
659
- print(" • Cons: More complex, requires HTTP client")
660
- print(" Daemon-managed:")
661
- print(" • Pros: Simple, lightweight, traditional daemon")
662
- print(" • Cons: Single instance, basic management")
663
-
664
-
665
- def main():
666
- """Main CLI entry point."""
667
- parser = argparse.ArgumentParser(description="Socket.IO Server Manager")
668
- subparsers = parser.add_subparsers(dest='command', help='Available commands')
669
-
670
- # Start command
671
- start_parser = subparsers.add_parser('start', help='Start a Socket.IO server')
672
- start_parser.add_argument('--port', type=int, help='Port to bind to (auto-detect if not specified)')
673
- start_parser.add_argument('--host', default='localhost', help='Host to bind to')
674
- start_parser.add_argument('--server-id', help='Custom server ID')
675
-
676
- # Stop command
677
- stop_parser = subparsers.add_parser('stop', help='Stop a Socket.IO server')
678
- stop_parser.add_argument('--port', type=int, help='Port of server to stop')
679
- stop_parser.add_argument('--server-id', help='Server ID to stop')
680
-
681
- # Restart command
682
- restart_parser = subparsers.add_parser('restart', help='Restart a Socket.IO server')
683
- restart_parser.add_argument('--port', type=int, help='Port of server to restart')
684
- restart_parser.add_argument('--server-id', help='Server ID to restart')
685
-
686
- # Status command
687
- status_parser = subparsers.add_parser('status', help='Show server status')
688
- status_parser.add_argument('-v', '--verbose', action='store_true', help='Show detailed information')
689
-
690
- # Health check command
691
- health_parser = subparsers.add_parser('health', help='Perform health check')
692
- health_parser.add_argument('--port', type=int, help='Port to check (all servers if not specified)')
693
-
694
- # Install dependencies command
695
- subparsers.add_parser('install-deps', help='Install required dependencies')
696
-
697
- # List command
698
- subparsers.add_parser('list', help='List running servers')
699
-
700
- # Diagnose command
701
- diagnose_parser = subparsers.add_parser('diagnose', help='Diagnose server management conflicts')
702
- diagnose_parser.add_argument('--port', type=int, default=8765, help='Port to diagnose')
703
-
704
- args = parser.parse_args()
705
-
706
- if not args.command:
707
- parser.print_help()
708
- return
709
-
710
- manager = ServerManager()
711
-
712
- if args.command == 'start':
713
- success = manager.start_server(
714
- port=args.port,
715
- server_id=args.server_id,
716
- host=args.host
717
- )
718
- sys.exit(0 if success else 1)
719
-
720
- elif args.command == 'stop':
721
- success = manager.stop_server(
722
- port=args.port,
723
- server_id=args.server_id
724
- )
725
- sys.exit(0 if success else 1)
726
-
727
- elif args.command == 'restart':
728
- success = manager.restart_server(
729
- port=args.port,
730
- server_id=args.server_id
731
- )
732
- sys.exit(0 if success else 1)
733
-
734
- elif args.command == 'status':
735
- manager.status(verbose=args.verbose)
736
-
737
- elif args.command == 'health':
738
- healthy = manager.health_check(port=args.port)
739
- sys.exit(0 if healthy else 1)
740
-
741
- elif args.command == 'install-deps':
742
- success = manager.install_dependencies()
743
- sys.exit(0 if success else 1)
744
-
745
- elif args.command == 'list':
746
- manager.status(verbose=False)
747
-
748
- elif args.command == 'diagnose':
749
- manager.diagnose_conflicts(port=args.port)
750
-
751
-
752
- if __name__ == "__main__":
753
- main()