claude-mpm 4.2.26__py3-none-any.whl → 4.2.28__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 CHANGED
@@ -1 +1 @@
1
- 4.2.26
1
+ 4.2.28
@@ -115,18 +115,45 @@ class UnifiedDashboardManager(IUnifiedDashboardManager):
115
115
  self.logger.info(
116
116
  f"Force restarting our dashboard on port {port} (PID: {pid})"
117
117
  )
118
+ # Clean up the existing service before restart
119
+ self._cleanup_port_conflicts(port)
118
120
  elif self.is_dashboard_running(port) and not force_restart:
119
- # Different service is using the port
120
- self.logger.warning(f"Port {port} is in use by a different service")
121
- return False, False
121
+ # Different service is using the port - try to clean it up
122
+ self.logger.warning(f"Port {port} is in use by a different service, attempting cleanup")
123
+ self._cleanup_port_conflicts(port)
124
+ # Brief pause to ensure cleanup is complete
125
+ time.sleep(1)
122
126
 
123
127
  self.logger.info(
124
128
  f"Starting unified dashboard on port {port} (background: {background}, force_restart: {force_restart})"
125
129
  )
126
130
 
127
131
  if background:
128
- # Start daemon in background mode with force restart if needed
129
- success = daemon.start(force_restart=force_restart)
132
+ # Try to start daemon with retry on port conflicts
133
+ max_retries = 3
134
+ retry_count = 0
135
+ success = False
136
+
137
+ while retry_count < max_retries and not success:
138
+ if retry_count > 0:
139
+ self.logger.info(f"Retry {retry_count}/{max_retries}: Cleaning up port {port}")
140
+ self._cleanup_port_conflicts(port)
141
+ time.sleep(2) # Wait for cleanup to complete
142
+
143
+ # Start daemon in background mode with force restart if needed
144
+ success = daemon.start(force_restart=force_restart or retry_count > 0)
145
+
146
+ if not success and retry_count < max_retries - 1:
147
+ # Check if it's a port conflict
148
+ if not self.port_manager.is_port_available(port):
149
+ self.logger.warning(f"Port {port} still in use, will retry cleanup")
150
+ retry_count += 1
151
+ else:
152
+ # Different kind of failure, don't retry
153
+ break
154
+ else:
155
+ break
156
+
130
157
  if success:
131
158
  with self._lock:
132
159
  self._background_daemons[port] = daemon
@@ -311,6 +338,84 @@ class UnifiedDashboardManager(IUnifiedDashboardManager):
311
338
  """
312
339
  return self.port_manager.find_available_port(preferred_port)
313
340
 
341
+ def _cleanup_port_conflicts(self, port: int) -> bool:
342
+ """
343
+ Try to clean up any processes using our port.
344
+
345
+ Args:
346
+ port: Port to clean up
347
+
348
+ Returns:
349
+ True if cleanup was successful or not needed
350
+ """
351
+ try:
352
+ import subprocess
353
+ import signal
354
+ import time
355
+
356
+ # Find processes using the port
357
+ result = subprocess.run(
358
+ ["lsof", "-ti", f":{port}"],
359
+ capture_output=True,
360
+ text=True
361
+ )
362
+
363
+ if result.returncode == 0 and result.stdout.strip():
364
+ pids = result.stdout.strip().split('\n')
365
+ self.logger.info(f"Found processes using port {port}: {pids}")
366
+
367
+ for pid_str in pids:
368
+ try:
369
+ pid = int(pid_str.strip())
370
+ # Try graceful termination first
371
+ import os
372
+ os.kill(pid, signal.SIGTERM)
373
+ self.logger.info(f"Sent SIGTERM to process {pid}")
374
+ except (ValueError, ProcessLookupError) as e:
375
+ self.logger.debug(f"Could not terminate process {pid_str}: {e}")
376
+ continue
377
+
378
+ # Give processes time to shut down gracefully
379
+ time.sleep(3)
380
+
381
+ # Check if port is still in use and force kill if needed
382
+ result = subprocess.run(
383
+ ["lsof", "-ti", f":{port}"],
384
+ capture_output=True,
385
+ text=True
386
+ )
387
+
388
+ if result.returncode == 0 and result.stdout.strip():
389
+ remaining_pids = result.stdout.strip().split('\n')
390
+ self.logger.warning(f"Processes still using port {port}: {remaining_pids}, force killing")
391
+
392
+ for pid_str in remaining_pids:
393
+ try:
394
+ pid = int(pid_str.strip())
395
+ os.kill(pid, signal.SIGKILL)
396
+ self.logger.info(f"Force killed process {pid}")
397
+ except (ValueError, ProcessLookupError) as e:
398
+ self.logger.debug(f"Could not force kill process {pid_str}: {e}")
399
+ continue
400
+
401
+ # Brief pause after force kill to ensure port is released
402
+ time.sleep(2)
403
+
404
+ self.logger.info(f"Successfully cleaned up processes on port {port}")
405
+ return True
406
+ else:
407
+ self.logger.debug(f"No processes found using port {port}")
408
+ return True
409
+
410
+ except FileNotFoundError:
411
+ # lsof not available, try alternative approach
412
+ self.logger.debug("lsof not available, skipping port cleanup")
413
+ return True
414
+ except Exception as e:
415
+ self.logger.warning(f"Error during port cleanup: {e}")
416
+ # Continue anyway - the port check will catch actual conflicts
417
+ return True
418
+
314
419
  def start_server(
315
420
  self, port: Optional[int] = None, timeout: int = 30, force_restart: bool = True
316
421
  ) -> Tuple[bool, DashboardInfo]:
@@ -263,8 +263,8 @@ class UnifiedMonitorDaemon:
263
263
  cleaned = self._cleanup_port_conflicts()
264
264
 
265
265
  if cleaned:
266
- # Wait a moment for port to be released
267
- time.sleep(2)
266
+ # Wait longer for port to be released to avoid race conditions
267
+ time.sleep(3)
268
268
  # Check again
269
269
  port_available, error_msg = self.lifecycle.verify_port_available(self.host)
270
270
 
@@ -302,6 +302,13 @@ class UnifiedMonitorDaemon:
302
302
  force_restart: If True, restart existing service if it's ours
303
303
  """
304
304
  self.logger.info(f"Starting unified monitor daemon on {self.host}:{self.port}")
305
+
306
+ # Clean up any processes on the port before checking service status
307
+ # This helps with race conditions where old processes haven't fully released the port
308
+ if force_restart:
309
+ self.logger.info("Force restart requested, cleaning up port conflicts...")
310
+ self._cleanup_port_conflicts()
311
+ time.sleep(1) # Brief pause to ensure port is released
305
312
 
306
313
  # Check if already running (check PID file even in foreground mode)
307
314
  if self.lifecycle.is_running():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.2.26
3
+ Version: 4.2.28
4
4
  Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
5
5
  Author-email: Bob Matsuoka <bob@matsuoka.com>
6
6
  Maintainer: Claude MPM Team
@@ -1,5 +1,5 @@
1
1
  claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
2
- claude_mpm/VERSION,sha256=SQgB6WkCfQ-PDwm6SAsbMLZRb40PTP081FhoVTi1sfg,7
2
+ claude_mpm/VERSION,sha256=kUdYgJvCcbklXcKUUTbI6s9esdHkXpgMy6bKPHBks3M,7
3
3
  claude_mpm/__init__.py,sha256=lyTZAYGH4DTaFGLRNWJKk5Q5oTjzN5I6AXmfVX-Jff0,1512
4
4
  claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
5
5
  claude_mpm/constants.py,sha256=I946iCQzIIPRZVVJ8aO7lA4euiyDnNw2IX7EelAOkIE,5915
@@ -434,7 +434,7 @@ claude_mpm/services/cli/memory_crud_service.py,sha256=ciN9Pl_12iDAqF9zPBWOzu-iXi
434
434
  claude_mpm/services/cli/memory_output_formatter.py,sha256=nbf7VsjGvH4e9fLv9c7PzjuO9COZhbK5P2fNZ79055w,24783
435
435
  claude_mpm/services/cli/session_manager.py,sha256=rla_Stbcvt93wa9G9MCMu9UqB3FLGqlPt_eN5lQb3Gg,16599
436
436
  claude_mpm/services/cli/startup_checker.py,sha256=efhuvu8ns5G16jcQ0nQZKVddmD2AktUEdlvjNcXjAuk,12232
437
- claude_mpm/services/cli/unified_dashboard_manager.py,sha256=4p8ubQhAXMezCVjd2sj_Q7D_AsDXtJJDQFD9BuvW838,12682
437
+ claude_mpm/services/cli/unified_dashboard_manager.py,sha256=X1dsf4-H1bFZGmhZtPFIJjS3Ps56uhUfyU1jLZskf7A,17346
438
438
  claude_mpm/services/communication/__init__.py,sha256=b4qc7_Rqy4DE9q7BAUlfUZjoYG4uimAyUnE0irPcXyU,560
439
439
  claude_mpm/services/core/__init__.py,sha256=evEayLlBqJvxMZhrhuK6aagXmNrKGSj8Jm9OOxKzqvU,2195
440
440
  claude_mpm/services/core/base.py,sha256=iA-F7DgGp-FJIMvQTiHQ68RkG_k-AtUWlArJPMw6ZPk,7297
@@ -550,7 +550,7 @@ claude_mpm/services/memory/cache/__init__.py,sha256=6M6-P8ParyxX8vOgp_IxHgLMvacr
550
550
  claude_mpm/services/memory/cache/shared_prompt_cache.py,sha256=crnYPUT8zcS7TvoE1vW7pyaf4T77N5rJ1wUf_YQ2vvo,28704
551
551
  claude_mpm/services/memory/cache/simple_cache.py,sha256=qsTjbcsPxj-kNfaod9VN_uE5NioIwpfkUin_mMVUJCg,10218
552
552
  claude_mpm/services/monitor/__init__.py,sha256=X7gxSLUm9Fg_zEsX6LtCHP2ipF0qj6Emkun20h2So7g,745
553
- claude_mpm/services/monitor/daemon.py,sha256=pZqn_-1PBMf9gfIq8Zo55gXoXInB0gKNyOZpWGkdrxw,27731
553
+ claude_mpm/services/monitor/daemon.py,sha256=LGKn9LG2RKL4Of6c3DWZSxRFWqJJRUZFnxT4ochTPjc,28157
554
554
  claude_mpm/services/monitor/event_emitter.py,sha256=JzRLNg8PUJ5s3ulNnq_D4yqCPItvidJzu8DmFxriieQ,12224
555
555
  claude_mpm/services/monitor/server.py,sha256=m98Eyv9caxRywJ4JtAdOuv5EB__z7vd2hYRZPwcqFLg,28498
556
556
  claude_mpm/services/monitor/handlers/__init__.py,sha256=jgPIf4IJVERm_tAeD9834tfx9IcxtlHj5r9rhEWpkfM,701
@@ -641,9 +641,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=zgiwLqh_17WxHpySvUPH65pb4bzIeUGOAYUJ
641
641
  claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
642
642
  claude_mpm/validation/agent_validator.py,sha256=3Lo6LK-Mw9IdnL_bd3zl_R6FkgSVDYKUUM7EeVVD3jc,20865
643
643
  claude_mpm/validation/frontmatter_validator.py,sha256=u8g4Eyd_9O6ugj7Un47oSGh3kqv4wMkuks2i_CtWRvM,7028
644
- claude_mpm-4.2.26.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
645
- claude_mpm-4.2.26.dist-info/METADATA,sha256=_oxZGDtNFYz4VX0vwQhEKz5cQFyw1bl3gcpiYXohypI,14451
646
- claude_mpm-4.2.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
647
- claude_mpm-4.2.26.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
648
- claude_mpm-4.2.26.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
649
- claude_mpm-4.2.26.dist-info/RECORD,,
644
+ claude_mpm-4.2.28.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
645
+ claude_mpm-4.2.28.dist-info/METADATA,sha256=s_GExoyYOTQqWkW7cDJhws2rmiOJwk7yKlKvjT1TClI,14451
646
+ claude_mpm-4.2.28.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
647
+ claude_mpm-4.2.28.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
648
+ claude_mpm-4.2.28.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
649
+ claude_mpm-4.2.28.dist-info/RECORD,,