claude-mpm 4.2.34__py3-none-any.whl → 4.2.36__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_manager.py +62 -13
- claude_mpm/services/monitor/server.py +2 -1
- {claude_mpm-4.2.34.dist-info → claude_mpm-4.2.36.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.34.dist-info → claude_mpm-4.2.36.dist-info}/RECORD +9 -9
- {claude_mpm-4.2.34.dist-info → claude_mpm-4.2.36.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.34.dist-info → claude_mpm-4.2.36.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.34.dist-info → claude_mpm-4.2.36.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.34.dist-info → claude_mpm-4.2.36.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.2.
|
1
|
+
4.2.36
|
@@ -143,14 +143,29 @@ class DaemonManager:
|
|
143
143
|
Returns:
|
144
144
|
True if port is available, False otherwise
|
145
145
|
"""
|
146
|
+
# Try to bind to the port using the same method as the actual server
|
147
|
+
# We only need to check if we can bind to at least one address family
|
146
148
|
try:
|
149
|
+
# Try IPv4 first (most common)
|
147
150
|
test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
148
151
|
test_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
149
|
-
|
152
|
+
|
153
|
+
# Use 127.0.0.1 for localhost to match what the server does
|
154
|
+
bind_host = "127.0.0.1" if self.host == "localhost" else self.host
|
155
|
+
test_sock.bind((bind_host, self.port))
|
150
156
|
test_sock.close()
|
151
157
|
return True
|
152
158
|
except OSError:
|
153
|
-
|
159
|
+
# IPv4 failed, try IPv6
|
160
|
+
try:
|
161
|
+
test_sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
162
|
+
test_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
163
|
+
test_sock.bind(("::1", self.port))
|
164
|
+
test_sock.close()
|
165
|
+
return True
|
166
|
+
except:
|
167
|
+
# Both IPv4 and IPv6 failed - port is in use
|
168
|
+
return False
|
154
169
|
|
155
170
|
def _kill_processes_on_port(self) -> bool:
|
156
171
|
"""Kill processes using the daemon port.
|
@@ -208,9 +223,32 @@ class DaemonManager:
|
|
208
223
|
text=True, check=False,
|
209
224
|
)
|
210
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()
|
211
237
|
process_name = process_info.stdout.strip().lower()
|
212
|
-
|
213
|
-
|
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]}")
|
214
252
|
os.kill(pid, signal.SIGTERM)
|
215
253
|
|
216
254
|
# Wait briefly for graceful shutdown
|
@@ -227,10 +265,12 @@ class DaemonManager:
|
|
227
265
|
except ProcessLookupError:
|
228
266
|
pass # Process already dead
|
229
267
|
else:
|
230
|
-
|
231
|
-
|
268
|
+
# Not a monitor process - log but don't fail
|
269
|
+
self.logger.info(
|
270
|
+
f"Skipping non-monitor process {pid} ({process_name})"
|
232
271
|
)
|
233
|
-
return False
|
272
|
+
# Continue to next PID - don't return False
|
273
|
+
continue
|
234
274
|
|
235
275
|
except (ValueError, ProcessLookupError) as e:
|
236
276
|
self.logger.debug(f"Error handling PID {pid_str}: {e}")
|
@@ -288,13 +328,16 @@ class DaemonManager:
|
|
288
328
|
return False
|
289
329
|
|
290
330
|
def _kill_claude_mpm_processes(self) -> bool:
|
291
|
-
"""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.
|
292
335
|
|
293
336
|
Returns:
|
294
337
|
True if successful, False on error
|
295
338
|
"""
|
296
339
|
try:
|
297
|
-
# Look for
|
340
|
+
# Look for monitor-specific processes
|
298
341
|
result = subprocess.run(["ps", "aux"], capture_output=True, text=True, check=False)
|
299
342
|
|
300
343
|
if result.returncode != 0:
|
@@ -304,7 +347,12 @@ class DaemonManager:
|
|
304
347
|
killed_any = False
|
305
348
|
|
306
349
|
for line in lines:
|
307
|
-
|
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]):
|
308
356
|
parts = line.split()
|
309
357
|
if len(parts) > 1:
|
310
358
|
try:
|
@@ -498,11 +546,12 @@ class DaemonManager:
|
|
498
546
|
|
499
547
|
self.logger.info(f"Daemon process started with PID {os.getpid()}")
|
500
548
|
|
501
|
-
#
|
502
|
-
|
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
|
503
552
|
|
504
553
|
# Note: Daemon process continues running
|
505
|
-
# Caller is responsible for running the actual service
|
554
|
+
# Caller is responsible for running the actual service AND reporting status
|
506
555
|
return True
|
507
556
|
|
508
557
|
def stop_daemon(self, timeout: int = 10) -> bool:
|
@@ -238,7 +238,8 @@ class UnifiedMonitorServer:
|
|
238
238
|
self.logger.info(f"Server running on http://{self.host}:{self.port}")
|
239
239
|
except OSError as e:
|
240
240
|
# Port binding error - make sure it's reported clearly
|
241
|
-
|
241
|
+
# Check for common port binding errors
|
242
|
+
if "Address already in use" in str(e) or "[Errno 48]" in str(e) or "[Errno 98]" in str(e):
|
242
243
|
error_msg = f"Port {self.port} is already in use. Another process may be using this port."
|
243
244
|
self.logger.error(error_msg)
|
244
245
|
self.startup_error = error_msg
|
@@ -1,5 +1,5 @@
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
|
2
|
-
claude_mpm/VERSION,sha256=
|
2
|
+
claude_mpm/VERSION,sha256=_8H8oebxvokQynjfzzqADPwrmT0PrJLlaKY4vdgLZIM,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
|
@@ -553,9 +553,9 @@ claude_mpm/services/memory/cache/shared_prompt_cache.py,sha256=crnYPUT8zcS7TvoE1
|
|
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
555
|
claude_mpm/services/monitor/daemon.py,sha256=nkB_xslT4yxIiSVf2u6nGm56rYpkit0WDj4YPWr-osM,22961
|
556
|
-
claude_mpm/services/monitor/daemon_manager.py,sha256=
|
556
|
+
claude_mpm/services/monitor/daemon_manager.py,sha256=EiM0-Qfn6MD0GyME2lBNRj8Au33mUF6E2wOdJg4xTDk,27156
|
557
557
|
claude_mpm/services/monitor/event_emitter.py,sha256=JzRLNg8PUJ5s3ulNnq_D4yqCPItvidJzu8DmFxriieQ,12224
|
558
|
-
claude_mpm/services/monitor/server.py,sha256=
|
558
|
+
claude_mpm/services/monitor/server.py,sha256=aKweXs3saNuPDaPwuoJT9g6kYYHefSiLcGmLdHD6FYM,28579
|
559
559
|
claude_mpm/services/monitor/handlers/__init__.py,sha256=jgPIf4IJVERm_tAeD9834tfx9IcxtlHj5r9rhEWpkfM,701
|
560
560
|
claude_mpm/services/monitor/handlers/code_analysis.py,sha256=mHyI27Wp6WVmUBc0m0i991ogyFZBTvkrfR7Kf3EAk5U,11474
|
561
561
|
claude_mpm/services/monitor/handlers/dashboard.py,sha256=uGBhb-6RG6u4WLipUXgdx7RCW-vb_qek5dIfHIwAC7o,9805
|
@@ -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.36.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
648
|
+
claude_mpm-4.2.36.dist-info/METADATA,sha256=bOjvBE3f3Nq7mhrnOeq8ih3gD6bUdetPTOcvVVOYuOU,14451
|
649
|
+
claude_mpm-4.2.36.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
650
|
+
claude_mpm-4.2.36.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
|
651
|
+
claude_mpm-4.2.36.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
652
|
+
claude_mpm-4.2.36.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|