claude-mpm 4.2.23__py3-none-any.whl → 4.2.24__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/services/monitor/daemon.py +16 -8
- claude_mpm/services/monitor/management/lifecycle.py +69 -16
- {claude_mpm-4.2.23.dist-info → claude_mpm-4.2.24.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.23.dist-info → claude_mpm-4.2.24.dist-info}/RECORD +9 -9
- {claude_mpm-4.2.23.dist-info → claude_mpm-4.2.24.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.23.dist-info → claude_mpm-4.2.24.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.23.dist-info → claude_mpm-4.2.24.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.23.dist-info → claude_mpm-4.2.24.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.2.
|
1
|
+
4.2.24
|
@@ -112,9 +112,11 @@ class UnifiedMonitorDaemon:
|
|
112
112
|
|
113
113
|
if force_restart:
|
114
114
|
# Check if it's our service
|
115
|
-
|
115
|
+
self.logger.debug(f"Checking if existing daemon (PID: {existing_pid}) is our service...")
|
116
|
+
is_ours, detected_pid = self.lifecycle.is_our_service(self.host)
|
117
|
+
|
116
118
|
if is_ours:
|
117
|
-
self.logger.info(f"Force restarting our existing daemon (PID: {
|
119
|
+
self.logger.info(f"Force restarting our existing claude-mpm monitor daemon (PID: {detected_pid or existing_pid})")
|
118
120
|
# Stop the existing daemon
|
119
121
|
if self.lifecycle.stop_daemon():
|
120
122
|
# Wait a moment for port to be released
|
@@ -123,7 +125,8 @@ class UnifiedMonitorDaemon:
|
|
123
125
|
self.logger.error("Failed to stop existing daemon for restart")
|
124
126
|
return False
|
125
127
|
else:
|
126
|
-
self.logger.warning(f"
|
128
|
+
self.logger.warning(f"Port {self.port} is in use by another service (PID: {existing_pid}). Cannot force restart.")
|
129
|
+
self.logger.info("To restart the claude-mpm monitor, first stop the other service or use a different port.")
|
127
130
|
return False
|
128
131
|
else:
|
129
132
|
self.logger.warning(f"Daemon already running with PID {existing_pid}")
|
@@ -131,9 +134,10 @@ class UnifiedMonitorDaemon:
|
|
131
134
|
|
132
135
|
# Check for orphaned processes (service running but no PID file)
|
133
136
|
elif force_restart:
|
137
|
+
self.logger.debug("No PID file found, checking for orphaned claude-mpm service...")
|
134
138
|
is_ours, pid = self.lifecycle.is_our_service(self.host)
|
135
139
|
if is_ours and pid:
|
136
|
-
self.logger.info(f"Found orphaned claude-mpm service (PID: {pid}), force restarting")
|
140
|
+
self.logger.info(f"Found orphaned claude-mpm monitor service (PID: {pid}), force restarting")
|
137
141
|
# Try to kill the orphaned process
|
138
142
|
try:
|
139
143
|
os.kill(pid, signal.SIGTERM)
|
@@ -194,9 +198,11 @@ class UnifiedMonitorDaemon:
|
|
194
198
|
|
195
199
|
if force_restart:
|
196
200
|
# Check if it's our service
|
197
|
-
|
201
|
+
self.logger.debug(f"Checking if existing daemon (PID: {existing_pid}) is our service...")
|
202
|
+
is_ours, detected_pid = self.lifecycle.is_our_service(self.host)
|
203
|
+
|
198
204
|
if is_ours:
|
199
|
-
self.logger.info(f"Force restarting our existing daemon (PID: {
|
205
|
+
self.logger.info(f"Force restarting our existing claude-mpm monitor daemon (PID: {detected_pid or existing_pid})")
|
200
206
|
# Stop the existing daemon
|
201
207
|
if self.lifecycle.stop_daemon():
|
202
208
|
# Wait a moment for port to be released
|
@@ -205,7 +211,8 @@ class UnifiedMonitorDaemon:
|
|
205
211
|
self.logger.error("Failed to stop existing daemon for restart")
|
206
212
|
return False
|
207
213
|
else:
|
208
|
-
self.logger.warning(f"
|
214
|
+
self.logger.warning(f"Port {self.port} is in use by another service (PID: {existing_pid}). Cannot force restart.")
|
215
|
+
self.logger.info("To restart the claude-mpm monitor, first stop the other service or use a different port.")
|
209
216
|
return False
|
210
217
|
else:
|
211
218
|
self.logger.warning(
|
@@ -215,9 +222,10 @@ class UnifiedMonitorDaemon:
|
|
215
222
|
|
216
223
|
# Check for orphaned processes (service running but no PID file)
|
217
224
|
elif force_restart:
|
225
|
+
self.logger.debug("No PID file found, checking for orphaned claude-mpm service...")
|
218
226
|
is_ours, pid = self.lifecycle.is_our_service(self.host)
|
219
227
|
if is_ours and pid:
|
220
|
-
self.logger.info(f"Found orphaned claude-mpm service (PID: {pid}), force restarting")
|
228
|
+
self.logger.info(f"Found orphaned claude-mpm monitor service (PID: {pid}), force restarting")
|
221
229
|
# Try to kill the orphaned process
|
222
230
|
try:
|
223
231
|
os.kill(pid, signal.SIGTERM)
|
@@ -513,60 +513,101 @@ class DaemonLifecycle:
|
|
513
513
|
Returns:
|
514
514
|
Tuple of (is_ours, pid_if_found)
|
515
515
|
"""
|
516
|
+
self.logger.debug(f"Checking if service on {host}:{self.port} is ours")
|
517
|
+
|
516
518
|
try:
|
517
519
|
# Method 1: Check health endpoint
|
518
520
|
import urllib.request
|
519
521
|
import urllib.error
|
520
522
|
|
521
523
|
health_url = f"http://{host}:{self.port}/health"
|
524
|
+
self.logger.debug(f"Checking health endpoint: {health_url}")
|
525
|
+
|
522
526
|
try:
|
523
|
-
|
527
|
+
req = urllib.request.Request(health_url)
|
528
|
+
req.add_header('User-Agent', 'claude-mpm-monitor')
|
529
|
+
|
530
|
+
with urllib.request.urlopen(req, timeout=3) as response:
|
524
531
|
if response.status == 200:
|
525
532
|
data = json.loads(response.read().decode())
|
533
|
+
self.logger.debug(f"Health endpoint response: {data}")
|
534
|
+
|
526
535
|
# Check for our service signature
|
527
|
-
|
536
|
+
service_name = data.get("service")
|
537
|
+
if service_name == "claude-mpm-monitor":
|
528
538
|
# Try to get PID from response
|
529
539
|
pid = data.get("pid")
|
530
540
|
if pid:
|
531
|
-
self.logger.
|
541
|
+
self.logger.info(f"Found our claude-mpm-monitor service via health endpoint, PID: {pid}")
|
532
542
|
return True, pid
|
533
543
|
else:
|
534
544
|
# Service is ours but no PID in response
|
535
545
|
# Try to get from PID file
|
536
546
|
file_pid = self.get_pid()
|
537
|
-
self.logger.
|
547
|
+
self.logger.info(f"Found our claude-mpm-monitor service via health endpoint, PID from file: {file_pid}")
|
538
548
|
return True, file_pid
|
539
|
-
|
540
|
-
|
541
|
-
|
549
|
+
else:
|
550
|
+
self.logger.debug(f"Service name '{service_name}' does not match 'claude-mpm-monitor'")
|
551
|
+
|
552
|
+
except urllib.error.URLError as e:
|
553
|
+
self.logger.debug(f"Health endpoint not accessible: {e}")
|
554
|
+
except urllib.error.HTTPError as e:
|
555
|
+
self.logger.debug(f"Health endpoint HTTP error: {e}")
|
556
|
+
except json.JSONDecodeError as e:
|
557
|
+
self.logger.debug(f"Health endpoint invalid JSON: {e}")
|
558
|
+
except Exception as e:
|
559
|
+
self.logger.debug(f"Health endpoint check failed: {e}")
|
542
560
|
|
543
561
|
# Method 2: Check if PID file exists and process matches
|
544
562
|
pid = self.get_pid()
|
545
563
|
if pid:
|
564
|
+
self.logger.debug(f"Checking PID from file: {pid}")
|
546
565
|
try:
|
547
566
|
# Check if process exists
|
548
567
|
os.kill(pid, 0)
|
568
|
+
self.logger.debug(f"Process {pid} exists")
|
549
569
|
|
550
570
|
# Process exists, check if it's using our port
|
551
571
|
# This requires psutil for accurate port checking
|
552
572
|
try:
|
553
573
|
import psutil
|
554
574
|
process = psutil.Process(pid)
|
575
|
+
|
576
|
+
# Check process command line for our service
|
577
|
+
cmdline = ' '.join(process.cmdline())
|
578
|
+
if 'claude_mpm' in cmdline or 'claude-mpm' in cmdline:
|
579
|
+
if 'monitor' in cmdline:
|
580
|
+
self.logger.info(f"Found our claude-mpm monitor process via PID file, PID: {pid}")
|
581
|
+
return True, pid
|
582
|
+
|
583
|
+
# Also check if it's listening on our port
|
555
584
|
connections = process.connections()
|
556
585
|
for conn in connections:
|
557
|
-
if conn.laddr.port == self.port:
|
558
|
-
self.logger.
|
559
|
-
|
586
|
+
if conn.laddr.port == self.port and conn.status == 'LISTEN':
|
587
|
+
self.logger.info(f"Found process {pid} listening on our port {self.port}")
|
588
|
+
# Double-check it's a Python process (likely ours)
|
589
|
+
if 'python' in process.name().lower():
|
590
|
+
self.logger.info(f"Confirmed as Python process, assuming it's our service")
|
591
|
+
return True, pid
|
592
|
+
|
560
593
|
except ImportError:
|
561
|
-
# psutil not available,
|
562
|
-
|
594
|
+
# psutil not available, but we have a PID file and process exists
|
595
|
+
# Assume it's ours since we manage the PID file
|
596
|
+
self.logger.info(f"Found process with our PID file: {pid}, assuming it's ours (psutil not available)")
|
563
597
|
return True, pid
|
564
|
-
except
|
565
|
-
|
598
|
+
except psutil.NoSuchProcess:
|
599
|
+
self.logger.debug(f"Process {pid} no longer exists")
|
600
|
+
except psutil.AccessDenied:
|
601
|
+
# Can't access process info, but it exists - likely ours
|
602
|
+
self.logger.info(f"Process {pid} exists but access denied, assuming it's ours")
|
603
|
+
return True, pid
|
604
|
+
except Exception as e:
|
605
|
+
self.logger.debug(f"Error checking process {pid}: {e}")
|
566
606
|
|
567
607
|
except (OSError, ProcessLookupError):
|
568
608
|
# Process doesn't exist
|
569
|
-
|
609
|
+
self.logger.debug(f"Process {pid} does not exist")
|
610
|
+
self._cleanup_stale_pid_file()
|
570
611
|
|
571
612
|
# Method 3: Try Socket.IO connection to check namespace
|
572
613
|
try:
|
@@ -620,9 +661,21 @@ class DaemonLifecycle:
|
|
620
661
|
except Exception as e:
|
621
662
|
self.logger.debug(f"Error checking Socket.IO connection: {e}")
|
622
663
|
|
664
|
+
# Method 4: Final fallback - if we have a PID file and can't definitively say it's NOT ours
|
665
|
+
# This handles edge cases where the health endpoint might be temporarily unavailable
|
666
|
+
if pid and self.pid_file.exists():
|
667
|
+
try:
|
668
|
+
# One more check - see if process exists
|
669
|
+
os.kill(pid, 0)
|
670
|
+
self.logger.info(f"PID file exists with valid process {pid}, assuming it's our stale service")
|
671
|
+
return True, pid
|
672
|
+
except (OSError, ProcessLookupError):
|
673
|
+
pass
|
674
|
+
|
623
675
|
# No service detected or not ours
|
676
|
+
self.logger.debug("Service not detected as ours")
|
624
677
|
return False, None
|
625
678
|
|
626
679
|
except Exception as e:
|
627
|
-
self.logger.error(f"Error checking if service is ours: {e}")
|
680
|
+
self.logger.error(f"Error checking if service is ours: {e}", exc_info=True)
|
628
681
|
return False, None
|
@@ -1,5 +1,5 @@
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
|
2
|
-
claude_mpm/VERSION,sha256=
|
2
|
+
claude_mpm/VERSION,sha256=U4vIe-sZ-5Iumq-KW3abswP9PknmnEd-RswDftmED1g,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
|
@@ -548,7 +548,7 @@ claude_mpm/services/memory/cache/__init__.py,sha256=6M6-P8ParyxX8vOgp_IxHgLMvacr
|
|
548
548
|
claude_mpm/services/memory/cache/shared_prompt_cache.py,sha256=crnYPUT8zcS7TvoE1vW7pyaf4T77N5rJ1wUf_YQ2vvo,28704
|
549
549
|
claude_mpm/services/memory/cache/simple_cache.py,sha256=qsTjbcsPxj-kNfaod9VN_uE5NioIwpfkUin_mMVUJCg,10218
|
550
550
|
claude_mpm/services/monitor/__init__.py,sha256=X7gxSLUm9Fg_zEsX6LtCHP2ipF0qj6Emkun20h2So7g,745
|
551
|
-
claude_mpm/services/monitor/daemon.py,sha256=
|
551
|
+
claude_mpm/services/monitor/daemon.py,sha256=9Cllm-jtVA85_qX1Z3TF-OwB5QZDzSfIvAcKhVQ65G8,21829
|
552
552
|
claude_mpm/services/monitor/event_emitter.py,sha256=JzRLNg8PUJ5s3ulNnq_D4yqCPItvidJzu8DmFxriieQ,12224
|
553
553
|
claude_mpm/services/monitor/server.py,sha256=2-xLo14qFBZf5MDYwBxBhTrFCNnbu3tOS3eEu8vyddc,28476
|
554
554
|
claude_mpm/services/monitor/handlers/__init__.py,sha256=jgPIf4IJVERm_tAeD9834tfx9IcxtlHj5r9rhEWpkfM,701
|
@@ -558,7 +558,7 @@ claude_mpm/services/monitor/handlers/file.py,sha256=p3C4wffl0GIcN00b-KkrmZ8F-Amd
|
|
558
558
|
claude_mpm/services/monitor/handlers/hooks.py,sha256=dlrmyFu8WChlvn6-sND9DLjSbm5nrMfNZrAgoWN-2No,17582
|
559
559
|
claude_mpm/services/monitor/management/__init__.py,sha256=mxaEFRgvvgV85gUpXu_DsnHtywihdP14EisvISAVZuQ,525
|
560
560
|
claude_mpm/services/monitor/management/health.py,sha256=Wm92Cli_4cWD6B89KX_CdpAvvevuEaGB8Ah59ILhFww,3772
|
561
|
-
claude_mpm/services/monitor/management/lifecycle.py,sha256=
|
561
|
+
claude_mpm/services/monitor/management/lifecycle.py,sha256=EClf8CK-kCLVb2iRo9uiGI2CAHFrii1Qe1i8xuWnhG4,27037
|
562
562
|
claude_mpm/services/project/__init__.py,sha256=IUclN1L7ChHCNya7PJiVxu4nttxsrj3WRIpwyA1A_hw,512
|
563
563
|
claude_mpm/services/project/analyzer.py,sha256=VHlLrP8-S5gr12w4Yzs7-6d7LWdJKISHPCFSG7SDiQU,38434
|
564
564
|
claude_mpm/services/project/analyzer_refactored.py,sha256=USYEdPAhSoGPqZCpaT89Dw6ElFW_L1yXSURheQjAhLA,18243
|
@@ -639,9 +639,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=zgiwLqh_17WxHpySvUPH65pb4bzIeUGOAYUJ
|
|
639
639
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
640
640
|
claude_mpm/validation/agent_validator.py,sha256=3Lo6LK-Mw9IdnL_bd3zl_R6FkgSVDYKUUM7EeVVD3jc,20865
|
641
641
|
claude_mpm/validation/frontmatter_validator.py,sha256=u8g4Eyd_9O6ugj7Un47oSGh3kqv4wMkuks2i_CtWRvM,7028
|
642
|
-
claude_mpm-4.2.
|
643
|
-
claude_mpm-4.2.
|
644
|
-
claude_mpm-4.2.
|
645
|
-
claude_mpm-4.2.
|
646
|
-
claude_mpm-4.2.
|
647
|
-
claude_mpm-4.2.
|
642
|
+
claude_mpm-4.2.24.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
643
|
+
claude_mpm-4.2.24.dist-info/METADATA,sha256=gBePVzfdnH07tGtxu8dnaQwimTu90EqPI0hcpdIZnjM,14451
|
644
|
+
claude_mpm-4.2.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
645
|
+
claude_mpm-4.2.24.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
|
646
|
+
claude_mpm-4.2.24.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
647
|
+
claude_mpm-4.2.24.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|