claude-mpm 4.2.35__py3-none-any.whl → 4.2.37__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 +4 -2
- claude_mpm/services/monitor/daemon_manager.py +63 -15
- {claude_mpm-4.2.35.dist-info → claude_mpm-4.2.37.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.35.dist-info → claude_mpm-4.2.37.dist-info}/RECORD +9 -9
- {claude_mpm-4.2.35.dist-info → claude_mpm-4.2.37.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.35.dist-info → claude_mpm-4.2.37.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.35.dist-info → claude_mpm-4.2.37.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.35.dist-info → claude_mpm-4.2.37.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.2.
|
1
|
+
4.2.37
|
@@ -161,14 +161,16 @@ class UnifiedMonitorDaemon:
|
|
161
161
|
self._wait_for_prewarm_completion()
|
162
162
|
|
163
163
|
# Use daemon manager's daemonize which includes cleanup
|
164
|
-
|
164
|
+
# DO NOT reset startup_status_file - it's needed for parent-child communication!
|
165
|
+
# self.daemon_manager.startup_status_file = None # BUG: This breaks communication
|
165
166
|
success = self.daemon_manager.daemonize()
|
166
167
|
if not success:
|
167
168
|
return False
|
168
169
|
|
169
170
|
# We're now in the daemon process
|
170
|
-
# Update our PID references
|
171
|
+
# Update our PID references and status file
|
171
172
|
self.lifecycle.pid_file = self.daemon_manager.pid_file
|
173
|
+
self.lifecycle.startup_status_file = self.daemon_manager.startup_status_file
|
172
174
|
|
173
175
|
# Start the server in daemon mode
|
174
176
|
try:
|
@@ -223,9 +223,32 @@ class DaemonManager:
|
|
223
223
|
text=True, check=False,
|
224
224
|
)
|
225
225
|
|
226
|
+
# Get full command to check if it's our monitor process
|
227
|
+
cmd_info = subprocess.run(
|
228
|
+
["ps", "-p", str(pid), "-o", "command="],
|
229
|
+
capture_output=True,
|
230
|
+
text=True, check=False,
|
231
|
+
)
|
232
|
+
|
233
|
+
if cmd_info.returncode != 0:
|
234
|
+
continue
|
235
|
+
|
236
|
+
full_command = cmd_info.stdout.strip().lower()
|
226
237
|
process_name = process_info.stdout.strip().lower()
|
227
|
-
|
228
|
-
|
238
|
+
|
239
|
+
# Check if this is our monitor/socketio process specifically
|
240
|
+
# Look for monitor, socketio, dashboard, or our specific port
|
241
|
+
is_monitor = any([
|
242
|
+
"monitor" in full_command,
|
243
|
+
"socketio" in full_command,
|
244
|
+
"dashboard" in full_command,
|
245
|
+
f"port={self.port}" in full_command,
|
246
|
+
f":{self.port}" in full_command,
|
247
|
+
"unified_monitor" in full_command,
|
248
|
+
])
|
249
|
+
|
250
|
+
if is_monitor and "python" in process_name:
|
251
|
+
self.logger.info(f"Killing monitor process {pid}: {full_command[:100]}")
|
229
252
|
os.kill(pid, signal.SIGTERM)
|
230
253
|
|
231
254
|
# Wait briefly for graceful shutdown
|
@@ -242,10 +265,12 @@ class DaemonManager:
|
|
242
265
|
except ProcessLookupError:
|
243
266
|
pass # Process already dead
|
244
267
|
else:
|
245
|
-
|
246
|
-
|
268
|
+
# Not a monitor process - log but don't fail
|
269
|
+
self.logger.info(
|
270
|
+
f"Skipping non-monitor process {pid} ({process_name})"
|
247
271
|
)
|
248
|
-
return False
|
272
|
+
# Continue to next PID - don't return False
|
273
|
+
continue
|
249
274
|
|
250
275
|
except (ValueError, ProcessLookupError) as e:
|
251
276
|
self.logger.debug(f"Error handling PID {pid_str}: {e}")
|
@@ -303,13 +328,16 @@ class DaemonManager:
|
|
303
328
|
return False
|
304
329
|
|
305
330
|
def _kill_claude_mpm_processes(self) -> bool:
|
306
|
-
"""Kill any claude-mpm monitor processes.
|
331
|
+
"""Kill any claude-mpm monitor processes specifically.
|
332
|
+
|
333
|
+
This targets monitor/dashboard/socketio processes only,
|
334
|
+
NOT general Claude instances.
|
307
335
|
|
308
336
|
Returns:
|
309
337
|
True if successful, False on error
|
310
338
|
"""
|
311
339
|
try:
|
312
|
-
# Look for
|
340
|
+
# Look for monitor-specific processes
|
313
341
|
result = subprocess.run(["ps", "aux"], capture_output=True, text=True, check=False)
|
314
342
|
|
315
343
|
if result.returncode != 0:
|
@@ -319,7 +347,12 @@ class DaemonManager:
|
|
319
347
|
killed_any = False
|
320
348
|
|
321
349
|
for line in lines:
|
322
|
-
|
350
|
+
line_lower = line.lower()
|
351
|
+
# Only target monitor/dashboard/socketio processes
|
352
|
+
if any(["monitor" in line_lower and "claude" in line_lower,
|
353
|
+
"dashboard" in line_lower and "claude" in line_lower,
|
354
|
+
"socketio" in line_lower,
|
355
|
+
f":{self.port}" in line_lower and "python" in line_lower]):
|
323
356
|
parts = line.split()
|
324
357
|
if len(parts) > 1:
|
325
358
|
try:
|
@@ -513,11 +546,12 @@ class DaemonManager:
|
|
513
546
|
|
514
547
|
self.logger.info(f"Daemon process started with PID {os.getpid()}")
|
515
548
|
|
516
|
-
#
|
517
|
-
|
549
|
+
# DO NOT report success here - let the caller report after starting the service
|
550
|
+
# This prevents race conditions where we report success before the server starts
|
551
|
+
# self._report_startup_success() # REMOVED - caller must report
|
518
552
|
|
519
553
|
# Note: Daemon process continues running
|
520
|
-
# Caller is responsible for running the actual service
|
554
|
+
# Caller is responsible for running the actual service AND reporting status
|
521
555
|
return True
|
522
556
|
|
523
557
|
def stop_daemon(self, timeout: int = 10) -> bool:
|
@@ -737,18 +771,32 @@ class DaemonManager:
|
|
737
771
|
|
738
772
|
def _report_startup_success(self):
|
739
773
|
"""Report successful startup to parent process."""
|
740
|
-
if self.startup_status_file
|
774
|
+
if self.startup_status_file:
|
741
775
|
try:
|
776
|
+
# Don't check if file exists - we need to write to it regardless
|
777
|
+
# The parent created it and is waiting for us to update it
|
742
778
|
with open(self.startup_status_file, "w") as f:
|
743
779
|
f.write("success")
|
780
|
+
f.flush() # Ensure it's written immediately
|
781
|
+
os.fsync(f.fileno()) # Force write to disk
|
744
782
|
except Exception as e:
|
745
|
-
|
783
|
+
# Logging might not work in daemon process after fork
|
784
|
+
pass
|
746
785
|
|
747
786
|
def _report_startup_error(self, error: str):
|
748
787
|
"""Report startup error to parent process."""
|
749
|
-
if self.startup_status_file
|
788
|
+
if self.startup_status_file:
|
750
789
|
try:
|
790
|
+
# Don't check if file exists - we need to write to it regardless
|
751
791
|
with open(self.startup_status_file, "w") as f:
|
752
792
|
f.write(f"error:{error}")
|
793
|
+
f.flush() # Ensure it's written immediately
|
794
|
+
os.fsync(f.fileno()) # Force write to disk
|
753
795
|
except Exception as e:
|
754
|
-
|
796
|
+
# Try to write error to a debug file since logging might not work
|
797
|
+
try:
|
798
|
+
with open("/tmp/daemon_debug_error.txt", "a") as debug:
|
799
|
+
debug.write(f"Error reporting error: {e}\n")
|
800
|
+
debug.write(f"Status file: {self.startup_status_file}\n")
|
801
|
+
except:
|
802
|
+
pass
|
@@ -1,5 +1,5 @@
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
|
2
|
-
claude_mpm/VERSION,sha256=
|
2
|
+
claude_mpm/VERSION,sha256=8MTto0T2Pjn3EmbsPiVKwl4bfctwhNwfnZha2doafiU,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
|
@@ -552,8 +552,8 @@ claude_mpm/services/memory/cache/__init__.py,sha256=6M6-P8ParyxX8vOgp_IxHgLMvacr
|
|
552
552
|
claude_mpm/services/memory/cache/shared_prompt_cache.py,sha256=crnYPUT8zcS7TvoE1vW7pyaf4T77N5rJ1wUf_YQ2vvo,28704
|
553
553
|
claude_mpm/services/memory/cache/simple_cache.py,sha256=qsTjbcsPxj-kNfaod9VN_uE5NioIwpfkUin_mMVUJCg,10218
|
554
554
|
claude_mpm/services/monitor/__init__.py,sha256=X7gxSLUm9Fg_zEsX6LtCHP2ipF0qj6Emkun20h2So7g,745
|
555
|
-
claude_mpm/services/monitor/daemon.py,sha256=
|
556
|
-
claude_mpm/services/monitor/daemon_manager.py,sha256=
|
555
|
+
claude_mpm/services/monitor/daemon.py,sha256=94Wdv9gRxbPUSnGU4aQrvYQzykqqoUdiciJgh10EerE,23166
|
556
|
+
claude_mpm/services/monitor/daemon_manager.py,sha256=ITtD3-lccRETX6RibWr50tdbg2YiAHDVtv29MS2jgHw,27885
|
557
557
|
claude_mpm/services/monitor/event_emitter.py,sha256=JzRLNg8PUJ5s3ulNnq_D4yqCPItvidJzu8DmFxriieQ,12224
|
558
558
|
claude_mpm/services/monitor/server.py,sha256=aKweXs3saNuPDaPwuoJT9g6kYYHefSiLcGmLdHD6FYM,28579
|
559
559
|
claude_mpm/services/monitor/handlers/__init__.py,sha256=jgPIf4IJVERm_tAeD9834tfx9IcxtlHj5r9rhEWpkfM,701
|
@@ -644,9 +644,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=zgiwLqh_17WxHpySvUPH65pb4bzIeUGOAYUJ
|
|
644
644
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
645
645
|
claude_mpm/validation/agent_validator.py,sha256=3Lo6LK-Mw9IdnL_bd3zl_R6FkgSVDYKUUM7EeVVD3jc,20865
|
646
646
|
claude_mpm/validation/frontmatter_validator.py,sha256=u8g4Eyd_9O6ugj7Un47oSGh3kqv4wMkuks2i_CtWRvM,7028
|
647
|
-
claude_mpm-4.2.
|
648
|
-
claude_mpm-4.2.
|
649
|
-
claude_mpm-4.2.
|
650
|
-
claude_mpm-4.2.
|
651
|
-
claude_mpm-4.2.
|
652
|
-
claude_mpm-4.2.
|
647
|
+
claude_mpm-4.2.37.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
648
|
+
claude_mpm-4.2.37.dist-info/METADATA,sha256=bENryvC9QBtyjDK0QZDgPuYMd8KP7DJGwk4P9gkMgOc,14451
|
649
|
+
claude_mpm-4.2.37.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
650
|
+
claude_mpm-4.2.37.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
|
651
|
+
claude_mpm-4.2.37.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
652
|
+
claude_mpm-4.2.37.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|