meshcode 2.10.15__tar.gz → 2.10.17__tar.gz
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.
- {meshcode-2.10.15 → meshcode-2.10.17}/PKG-INFO +1 -1
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/__init__.py +1 -1
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/meshcode_mcp/server.py +47 -29
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.10.15 → meshcode-2.10.17}/pyproject.toml +1 -1
- {meshcode-2.10.15 → meshcode-2.10.17}/README.md +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/cli.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/invites.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/launcher.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/preferences.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/secrets.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/self_update.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/setup.cfg +0 -0
- {meshcode-2.10.15 → meshcode-2.10.17}/tests/test_status_enum_coverage.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""MeshCode — Real-time communication between AI agents."""
|
|
2
|
-
__version__ = "2.10.
|
|
2
|
+
__version__ = "2.10.17"
|
|
@@ -22,6 +22,14 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
22
22
|
|
|
23
23
|
from meshcode import __version__ as _SDK_VERSION
|
|
24
24
|
|
|
25
|
+
# ── CRITICAL: Protect stdout for MCP JSON-RPC protocol ──────────
|
|
26
|
+
# MCP uses stdout exclusively for JSON-RPC. ANY stray print() to stdout
|
|
27
|
+
# corrupts the stream and causes Claude Code to kill the connection.
|
|
28
|
+
# Save the real stdout for FastMCP, then redirect sys.stdout to stderr
|
|
29
|
+
# so all print() calls (ours + third-party libs) go to stderr safely.
|
|
30
|
+
_REAL_STDOUT = sys.stdout # FastMCP will use this via mcp.run()
|
|
31
|
+
sys.stdout = sys.stderr # All print() now goes to stderr
|
|
32
|
+
|
|
25
33
|
|
|
26
34
|
# ── Agent color for terminal logs (pure ANSI, zero tokens) ──────
|
|
27
35
|
_ANSI_COLORS = [
|
|
@@ -77,11 +85,11 @@ def _mc_log(msg: str, level: str = "info") -> None:
|
|
|
77
85
|
c = _agent_color(agent) if agent else "\033[36m"
|
|
78
86
|
prefix = f"{c}{_ANSI_BOLD}[meshcode-mcp]{_ANSI_RESET}"
|
|
79
87
|
if level == "error":
|
|
80
|
-
print(f"{prefix} \033[91mERROR:{_ANSI_RESET} {msg}",
|
|
88
|
+
print(f"{prefix} \033[91mERROR:{_ANSI_RESET} {msg}", "warn")
|
|
81
89
|
elif level == "warn":
|
|
82
|
-
print(f"{prefix} \033[33mWARNING:{_ANSI_RESET} {msg}",
|
|
90
|
+
print(f"{prefix} \033[33mWARNING:{_ANSI_RESET} {msg}", "warn")
|
|
83
91
|
else:
|
|
84
|
-
print(f"{prefix} {c}{msg}{_ANSI_RESET}",
|
|
92
|
+
print(f"{prefix} {c}{msg}{_ANSI_RESET}", "warn")
|
|
85
93
|
|
|
86
94
|
|
|
87
95
|
# ============================================================
|
|
@@ -378,7 +386,7 @@ def _get_api_key() -> str:
|
|
|
378
386
|
_API_KEY_CACHE = kc_val
|
|
379
387
|
return kc_val
|
|
380
388
|
except Exception as e:
|
|
381
|
-
_mc_log(f" keychain lookup failed for profile '{profile}': {e}",
|
|
389
|
+
_mc_log(f" keychain lookup failed for profile '{profile}': {e}", "warn")
|
|
382
390
|
_API_KEY_CACHE = ""
|
|
383
391
|
return ""
|
|
384
392
|
|
|
@@ -403,17 +411,17 @@ if not _PROJECT_ID:
|
|
|
403
411
|
_PROJECT_ID = _r["project_id"]
|
|
404
412
|
break
|
|
405
413
|
elif isinstance(_r, dict) and _r.get("error"):
|
|
406
|
-
_mc_log(f" mc_resolve_project: {_r['error']}",
|
|
414
|
+
_mc_log(f" mc_resolve_project: {_r['error']}", "warn")
|
|
407
415
|
except Exception as _e:
|
|
408
|
-
_mc_log(f" mc_resolve_project failed: {_e}",
|
|
416
|
+
_mc_log(f" mc_resolve_project failed: {_e}", "warn")
|
|
409
417
|
if not _PROJECT_ID:
|
|
410
418
|
_PROJECT_ID = be.get_project_id(PROJECT_NAME)
|
|
411
419
|
if _PROJECT_ID:
|
|
412
420
|
break
|
|
413
421
|
if _boot_attempt < _BOOT_MAX_RETRIES - 1:
|
|
414
422
|
_wait = _BOOT_BACKOFF[_boot_attempt]
|
|
415
|
-
_mc_log(f" project resolution failed (attempt {_boot_attempt+1}/{_BOOT_MAX_RETRIES}), retrying in {_wait}s...",
|
|
416
|
-
|
|
423
|
+
_mc_log(f" project resolution failed (attempt {_boot_attempt+1}/{_BOOT_MAX_RETRIES}), retrying in {_wait}s...", "warn")
|
|
424
|
+
import time; time.sleep(_wait)
|
|
417
425
|
if not _PROJECT_ID:
|
|
418
426
|
_mc_log(f"project '{PROJECT_NAME}' not found after {_BOOT_MAX_RETRIES} attempts (check MESHCODE_KEYCHAIN_PROFILE / MESHCODE_API_KEY)", "error")
|
|
419
427
|
sys.exit(2)
|
|
@@ -429,7 +437,7 @@ except Exception:
|
|
|
429
437
|
|
|
430
438
|
_register_result = be.register_agent(PROJECT_NAME, AGENT_NAME, AGENT_ROLE or "MCP-connected agent", api_key=_get_api_key())
|
|
431
439
|
if isinstance(_register_result, dict) and _register_result.get("error"):
|
|
432
|
-
_mc_log(f" register failed: {_register_result['error']}",
|
|
440
|
+
_mc_log(f" register failed: {_register_result['error']}", "warn")
|
|
433
441
|
|
|
434
442
|
# ── Fetch profile color from dashboard (single source of truth) ──
|
|
435
443
|
try:
|
|
@@ -463,7 +471,7 @@ def _flip_status(status: str, task: str = "") -> bool:
|
|
|
463
471
|
return False
|
|
464
472
|
|
|
465
473
|
if not _flip_status("idle", ""):
|
|
466
|
-
_mc_log(f" could not flip status to idle",
|
|
474
|
+
_mc_log(f" could not flip status to idle", "warn")
|
|
467
475
|
|
|
468
476
|
|
|
469
477
|
# ============================================================
|
|
@@ -628,7 +636,7 @@ def _acquire_lease() -> bool:
|
|
|
628
636
|
})
|
|
629
637
|
except Exception as e:
|
|
630
638
|
# Non-fatal: RPC might not exist on older servers.
|
|
631
|
-
_mc_log(f"stale-lease pre-clean skipped: {e}",
|
|
639
|
+
_mc_log(f"stale-lease pre-clean skipped: {e}", "warn")
|
|
632
640
|
for attempt in range(3):
|
|
633
641
|
try:
|
|
634
642
|
r = be.sb_rpc("mc_acquire_agent_lease", {
|
|
@@ -680,14 +688,14 @@ def _acquire_lease() -> bool:
|
|
|
680
688
|
_mc_log(f"Could not start — agent '{AGENT_NAME}' is running in another window.", "error")
|
|
681
689
|
_mc_log("Close the other window first, or use a different agent name.", "error")
|
|
682
690
|
return False
|
|
683
|
-
_mc_log(f"lease attempt {attempt+1}: {r.get('error')}",
|
|
691
|
+
_mc_log(f"lease attempt {attempt+1}: {r.get('error')}", "warn")
|
|
684
692
|
else:
|
|
685
693
|
return True
|
|
686
694
|
except Exception as e:
|
|
687
|
-
_mc_log(f"lease attempt {attempt+1} failed: {e}",
|
|
695
|
+
_mc_log(f"lease attempt {attempt+1} failed: {e}", "warn")
|
|
688
696
|
if attempt < 2:
|
|
689
697
|
_time.sleep(2)
|
|
690
|
-
_mc_log(f" lease failed after 3 attempts — proceeding anyway",
|
|
698
|
+
_mc_log(f" lease failed after 3 attempts — proceeding anyway", "warn")
|
|
691
699
|
return True
|
|
692
700
|
|
|
693
701
|
if not _acquire_lease():
|
|
@@ -705,7 +713,7 @@ def _boot_diagnostic() -> None:
|
|
|
705
713
|
be.sb_select("mc_projects", f"id=eq.{_PROJECT_ID}", limit=1)
|
|
706
714
|
checks_passed += 1
|
|
707
715
|
except Exception as e:
|
|
708
|
-
print(f"[meshcode] BOOT CHECK FAILED: Supabase API unreachable ({e}). Fix: check network/VPN.",
|
|
716
|
+
print(f"[meshcode] BOOT CHECK FAILED: Supabase API unreachable ({e}). Fix: check network/VPN.", "warn")
|
|
709
717
|
|
|
710
718
|
# Check 2: Lease valid
|
|
711
719
|
try:
|
|
@@ -715,11 +723,11 @@ def _boot_diagnostic() -> None:
|
|
|
715
723
|
if agent.get("instance_id") == _INSTANCE_ID:
|
|
716
724
|
checks_passed += 1
|
|
717
725
|
else:
|
|
718
|
-
print(f"[meshcode] BOOT CHECK FAILED: Lease mismatch — expected {_INSTANCE_ID}, got {agent.get('instance_id')}. Fix: restart agent.",
|
|
726
|
+
print(f"[meshcode] BOOT CHECK FAILED: Lease mismatch — expected {_INSTANCE_ID}, got {agent.get('instance_id')}. Fix: restart agent.", "warn")
|
|
719
727
|
else:
|
|
720
|
-
print(f"[meshcode] BOOT CHECK FAILED: Agent '{AGENT_NAME}' not found in project. Fix: register agent first.",
|
|
728
|
+
print(f"[meshcode] BOOT CHECK FAILED: Agent '{AGENT_NAME}' not found in project. Fix: register agent first.", "warn")
|
|
721
729
|
except Exception as e:
|
|
722
|
-
print(f"[meshcode] BOOT CHECK FAILED: Could not verify lease ({e}).",
|
|
730
|
+
print(f"[meshcode] BOOT CHECK FAILED: Could not verify lease ({e}).", "warn")
|
|
723
731
|
|
|
724
732
|
# Check 3: Heartbeat recent
|
|
725
733
|
try:
|
|
@@ -728,7 +736,7 @@ def _boot_diagnostic() -> None:
|
|
|
728
736
|
if hb:
|
|
729
737
|
checks_passed += 1
|
|
730
738
|
else:
|
|
731
|
-
print(f"[meshcode] BOOT CHECK WARNING: No heartbeat recorded yet.",
|
|
739
|
+
print(f"[meshcode] BOOT CHECK WARNING: No heartbeat recorded yet.", "warn")
|
|
732
740
|
else:
|
|
733
741
|
checks_passed += 1 # skip if no agent data
|
|
734
742
|
except Exception:
|
|
@@ -745,9 +753,9 @@ def _boot_diagnostic() -> None:
|
|
|
745
753
|
checks_passed += 1 # non-critical
|
|
746
754
|
|
|
747
755
|
if checks_passed == checks_total:
|
|
748
|
-
print(f"[meshcode] All boot checks passed ({checks_passed}/{checks_total}).",
|
|
756
|
+
print(f"[meshcode] All boot checks passed ({checks_passed}/{checks_total}).", "warn")
|
|
749
757
|
else:
|
|
750
|
-
print(f"[meshcode] Boot checks: {checks_passed}/{checks_total} passed. Agent starting anyway.",
|
|
758
|
+
print(f"[meshcode] Boot checks: {checks_passed}/{checks_total} passed. Agent starting anyway.", "warn")
|
|
751
759
|
|
|
752
760
|
|
|
753
761
|
_boot_diagnostic()
|
|
@@ -794,7 +802,7 @@ def _log_crash_to_db(reason: str = "unknown", error_detail: str = "") -> None:
|
|
|
794
802
|
f"crashed: {reason[:100]}", api_key=_get_api_key())
|
|
795
803
|
except Exception:
|
|
796
804
|
pass
|
|
797
|
-
_mc_log(f" crash logged: {reason}",
|
|
805
|
+
_mc_log(f" crash logged: {reason}", "warn")
|
|
798
806
|
|
|
799
807
|
|
|
800
808
|
def _on_exit() -> None:
|
|
@@ -1454,9 +1462,9 @@ try:
|
|
|
1454
1462
|
elif isinstance(_ls_val, str):
|
|
1455
1463
|
_LAST_SEEN_TS = _ls_val
|
|
1456
1464
|
if _LAST_SEEN_TS:
|
|
1457
|
-
print(f"[meshcode] Restored last_seen={_LAST_SEEN_TS} from mesh memory.",
|
|
1465
|
+
print(f"[meshcode] Restored last_seen={_LAST_SEEN_TS} from mesh memory.", "warn")
|
|
1458
1466
|
except Exception as _e:
|
|
1459
|
-
print(f"[meshcode] Could not restore last_seen: {_e}",
|
|
1467
|
+
print(f"[meshcode] Could not restore last_seen: {_e}", "warn")
|
|
1460
1468
|
|
|
1461
1469
|
|
|
1462
1470
|
def _get_pending_tasks_summary() -> Optional[List[Dict[str, str]]]:
|
|
@@ -2616,7 +2624,7 @@ def _auto_update() -> None:
|
|
|
2616
2624
|
return
|
|
2617
2625
|
|
|
2618
2626
|
# 3. Install the new version (blocking, 60s timeout)
|
|
2619
|
-
print(f"[meshcode] Updating {current} → {latest}...",
|
|
2627
|
+
print(f"[meshcode] Updating {current} → {latest}...", "warn")
|
|
2620
2628
|
try:
|
|
2621
2629
|
result = subprocess.run(
|
|
2622
2630
|
[sys.executable, "-m", "pip", "install", "--upgrade",
|
|
@@ -2633,8 +2641,14 @@ def _auto_update() -> None:
|
|
|
2633
2641
|
log.debug(f"[meshcode] Auto-update failed: {e}")
|
|
2634
2642
|
return
|
|
2635
2643
|
|
|
2636
|
-
# 4.
|
|
2637
|
-
|
|
2644
|
+
# 4. In MCP mode, NEVER re-exec — it kills the stdio pipe to Claude Code.
|
|
2645
|
+
# The new version will load on the next clean boot.
|
|
2646
|
+
if os.environ.get("MESHCODE_MCP_SERVE") == "1":
|
|
2647
|
+
print(f"[meshcode] Updated {current} → {latest}. Will load on next boot (MCP mode — cannot restart).", "warn")
|
|
2648
|
+
return
|
|
2649
|
+
|
|
2650
|
+
# CLI mode: safe to re-exec
|
|
2651
|
+
print(f"[meshcode] Updated to {latest}, restarting...", "warn")
|
|
2638
2652
|
os.environ["MESHCODE_UPDATED"] = "1"
|
|
2639
2653
|
try:
|
|
2640
2654
|
os.execv(sys.executable, [sys.executable] + sys.argv)
|
|
@@ -2655,6 +2669,10 @@ def run_server():
|
|
|
2655
2669
|
file=sys.stderr,
|
|
2656
2670
|
)
|
|
2657
2671
|
try:
|
|
2672
|
+
# Restore real stdout for FastMCP's JSON-RPC transport.
|
|
2673
|
+
# sys.stdout was redirected to stderr at module load to prevent
|
|
2674
|
+
# accidental stdout writes from corrupting the MCP protocol.
|
|
2675
|
+
sys.stdout = _REAL_STDOUT
|
|
2658
2676
|
mcp.run()
|
|
2659
2677
|
except KeyboardInterrupt:
|
|
2660
2678
|
_log_crash_to_db("keyboard_interrupt", "User stopped the agent")
|
|
@@ -2665,6 +2683,6 @@ def run_server():
|
|
|
2665
2683
|
import traceback as _tb
|
|
2666
2684
|
tb_str = _tb.format_exc()
|
|
2667
2685
|
_log_crash_to_db("unhandled_exception", f"{type(e).__name__}: {e}\n{tb_str}")
|
|
2668
|
-
print(f"[meshcode-mcp] FATAL: {e}",
|
|
2669
|
-
print(f"[meshcode-mcp] Stack trace logged to mc_agent_crash_logs",
|
|
2686
|
+
print(f"[meshcode-mcp] FATAL: {e}", "warn")
|
|
2687
|
+
print(f"[meshcode-mcp] Stack trace logged to mc_agent_crash_logs", "warn")
|
|
2670
2688
|
sys.exit(1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|