meshcode 2.10.14__tar.gz → 2.10.16__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.14 → meshcode-2.10.16}/PKG-INFO +1 -1
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/__init__.py +1 -1
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/meshcode_mcp/server.py +50 -27
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.10.14 → meshcode-2.10.16}/pyproject.toml +1 -1
- {meshcode-2.10.14 → meshcode-2.10.16}/README.md +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/cli.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/invites.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/launcher.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/preferences.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/secrets.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/self_update.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/setup.cfg +0 -0
- {meshcode-2.10.14 → meshcode-2.10.16}/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.16"
|
|
@@ -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 = [
|
|
@@ -67,16 +75,21 @@ def _agent_color(name: str) -> str:
|
|
|
67
75
|
|
|
68
76
|
|
|
69
77
|
def _mc_log(msg: str, level: str = "info") -> None:
|
|
70
|
-
"""Colored [meshcode-mcp] log line. Uses agent color if available.
|
|
78
|
+
"""Colored [meshcode-mcp] log line. Uses agent color if available.
|
|
79
|
+
|
|
80
|
+
CRITICAL: Must write to stderr, NEVER stdout. MCP protocol uses stdout
|
|
81
|
+
for JSON-RPC — any non-JSON output to stdout corrupts the stream and
|
|
82
|
+
causes Claude Code to kill the connection.
|
|
83
|
+
"""
|
|
71
84
|
agent = os.environ.get("MESHCODE_AGENT", "")
|
|
72
85
|
c = _agent_color(agent) if agent else "\033[36m"
|
|
73
86
|
prefix = f"{c}{_ANSI_BOLD}[meshcode-mcp]{_ANSI_RESET}"
|
|
74
87
|
if level == "error":
|
|
75
|
-
print(f"{prefix} \033[91mERROR:{_ANSI_RESET} {msg}",
|
|
88
|
+
print(f"{prefix} \033[91mERROR:{_ANSI_RESET} {msg}", file=sys.stderr)
|
|
76
89
|
elif level == "warn":
|
|
77
|
-
print(f"{prefix} \033[33mWARNING:{_ANSI_RESET} {msg}",
|
|
90
|
+
print(f"{prefix} \033[33mWARNING:{_ANSI_RESET} {msg}", file=sys.stderr)
|
|
78
91
|
else:
|
|
79
|
-
print(f"{prefix} {c}{msg}{_ANSI_RESET}",
|
|
92
|
+
print(f"{prefix} {c}{msg}{_ANSI_RESET}", file=sys.stderr)
|
|
80
93
|
|
|
81
94
|
|
|
82
95
|
# ============================================================
|
|
@@ -373,7 +386,7 @@ def _get_api_key() -> str:
|
|
|
373
386
|
_API_KEY_CACHE = kc_val
|
|
374
387
|
return kc_val
|
|
375
388
|
except Exception as e:
|
|
376
|
-
_mc_log(f" keychain lookup failed for profile '{profile}': {e}",
|
|
389
|
+
_mc_log(f" keychain lookup failed for profile '{profile}': {e}", file=sys.stderr)
|
|
377
390
|
_API_KEY_CACHE = ""
|
|
378
391
|
return ""
|
|
379
392
|
|
|
@@ -398,16 +411,16 @@ if not _PROJECT_ID:
|
|
|
398
411
|
_PROJECT_ID = _r["project_id"]
|
|
399
412
|
break
|
|
400
413
|
elif isinstance(_r, dict) and _r.get("error"):
|
|
401
|
-
_mc_log(f" mc_resolve_project: {_r['error']}",
|
|
414
|
+
_mc_log(f" mc_resolve_project: {_r['error']}", file=sys.stderr)
|
|
402
415
|
except Exception as _e:
|
|
403
|
-
_mc_log(f" mc_resolve_project failed: {_e}",
|
|
416
|
+
_mc_log(f" mc_resolve_project failed: {_e}", file=sys.stderr)
|
|
404
417
|
if not _PROJECT_ID:
|
|
405
418
|
_PROJECT_ID = be.get_project_id(PROJECT_NAME)
|
|
406
419
|
if _PROJECT_ID:
|
|
407
420
|
break
|
|
408
421
|
if _boot_attempt < _BOOT_MAX_RETRIES - 1:
|
|
409
422
|
_wait = _BOOT_BACKOFF[_boot_attempt]
|
|
410
|
-
_mc_log(f" project resolution failed (attempt {_boot_attempt+1}/{_BOOT_MAX_RETRIES}), retrying in {_wait}s...",
|
|
423
|
+
_mc_log(f" project resolution failed (attempt {_boot_attempt+1}/{_BOOT_MAX_RETRIES}), retrying in {_wait}s...", file=sys.stderr)
|
|
411
424
|
_time.sleep(_wait)
|
|
412
425
|
if not _PROJECT_ID:
|
|
413
426
|
_mc_log(f"project '{PROJECT_NAME}' not found after {_BOOT_MAX_RETRIES} attempts (check MESHCODE_KEYCHAIN_PROFILE / MESHCODE_API_KEY)", "error")
|
|
@@ -424,7 +437,7 @@ except Exception:
|
|
|
424
437
|
|
|
425
438
|
_register_result = be.register_agent(PROJECT_NAME, AGENT_NAME, AGENT_ROLE or "MCP-connected agent", api_key=_get_api_key())
|
|
426
439
|
if isinstance(_register_result, dict) and _register_result.get("error"):
|
|
427
|
-
_mc_log(f" register failed: {_register_result['error']}",
|
|
440
|
+
_mc_log(f" register failed: {_register_result['error']}", file=sys.stderr)
|
|
428
441
|
|
|
429
442
|
# ── Fetch profile color from dashboard (single source of truth) ──
|
|
430
443
|
try:
|
|
@@ -458,7 +471,7 @@ def _flip_status(status: str, task: str = "") -> bool:
|
|
|
458
471
|
return False
|
|
459
472
|
|
|
460
473
|
if not _flip_status("idle", ""):
|
|
461
|
-
_mc_log(f" could not flip status to idle",
|
|
474
|
+
_mc_log(f" could not flip status to idle", file=sys.stderr)
|
|
462
475
|
|
|
463
476
|
|
|
464
477
|
# ============================================================
|
|
@@ -623,7 +636,7 @@ def _acquire_lease() -> bool:
|
|
|
623
636
|
})
|
|
624
637
|
except Exception as e:
|
|
625
638
|
# Non-fatal: RPC might not exist on older servers.
|
|
626
|
-
_mc_log(f"stale-lease pre-clean skipped: {e}",
|
|
639
|
+
_mc_log(f"stale-lease pre-clean skipped: {e}", file=sys.stderr)
|
|
627
640
|
for attempt in range(3):
|
|
628
641
|
try:
|
|
629
642
|
r = be.sb_rpc("mc_acquire_agent_lease", {
|
|
@@ -675,14 +688,14 @@ def _acquire_lease() -> bool:
|
|
|
675
688
|
_mc_log(f"Could not start — agent '{AGENT_NAME}' is running in another window.", "error")
|
|
676
689
|
_mc_log("Close the other window first, or use a different agent name.", "error")
|
|
677
690
|
return False
|
|
678
|
-
_mc_log(f"lease attempt {attempt+1}: {r.get('error')}",
|
|
691
|
+
_mc_log(f"lease attempt {attempt+1}: {r.get('error')}", file=sys.stderr)
|
|
679
692
|
else:
|
|
680
693
|
return True
|
|
681
694
|
except Exception as e:
|
|
682
|
-
_mc_log(f"lease attempt {attempt+1} failed: {e}",
|
|
695
|
+
_mc_log(f"lease attempt {attempt+1} failed: {e}", file=sys.stderr)
|
|
683
696
|
if attempt < 2:
|
|
684
697
|
_time.sleep(2)
|
|
685
|
-
_mc_log(f" lease failed after 3 attempts — proceeding anyway",
|
|
698
|
+
_mc_log(f" lease failed after 3 attempts — proceeding anyway", file=sys.stderr)
|
|
686
699
|
return True
|
|
687
700
|
|
|
688
701
|
if not _acquire_lease():
|
|
@@ -700,7 +713,7 @@ def _boot_diagnostic() -> None:
|
|
|
700
713
|
be.sb_select("mc_projects", f"id=eq.{_PROJECT_ID}", limit=1)
|
|
701
714
|
checks_passed += 1
|
|
702
715
|
except Exception as e:
|
|
703
|
-
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.", file=sys.stderr)
|
|
704
717
|
|
|
705
718
|
# Check 2: Lease valid
|
|
706
719
|
try:
|
|
@@ -710,11 +723,11 @@ def _boot_diagnostic() -> None:
|
|
|
710
723
|
if agent.get("instance_id") == _INSTANCE_ID:
|
|
711
724
|
checks_passed += 1
|
|
712
725
|
else:
|
|
713
|
-
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.", file=sys.stderr)
|
|
714
727
|
else:
|
|
715
|
-
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.", file=sys.stderr)
|
|
716
729
|
except Exception as e:
|
|
717
|
-
print(f"[meshcode] BOOT CHECK FAILED: Could not verify lease ({e}).",
|
|
730
|
+
print(f"[meshcode] BOOT CHECK FAILED: Could not verify lease ({e}).", file=sys.stderr)
|
|
718
731
|
|
|
719
732
|
# Check 3: Heartbeat recent
|
|
720
733
|
try:
|
|
@@ -723,7 +736,7 @@ def _boot_diagnostic() -> None:
|
|
|
723
736
|
if hb:
|
|
724
737
|
checks_passed += 1
|
|
725
738
|
else:
|
|
726
|
-
print(f"[meshcode] BOOT CHECK WARNING: No heartbeat recorded yet.",
|
|
739
|
+
print(f"[meshcode] BOOT CHECK WARNING: No heartbeat recorded yet.", file=sys.stderr)
|
|
727
740
|
else:
|
|
728
741
|
checks_passed += 1 # skip if no agent data
|
|
729
742
|
except Exception:
|
|
@@ -740,9 +753,9 @@ def _boot_diagnostic() -> None:
|
|
|
740
753
|
checks_passed += 1 # non-critical
|
|
741
754
|
|
|
742
755
|
if checks_passed == checks_total:
|
|
743
|
-
print(f"[meshcode] All boot checks passed ({checks_passed}/{checks_total}).",
|
|
756
|
+
print(f"[meshcode] All boot checks passed ({checks_passed}/{checks_total}).", file=sys.stderr)
|
|
744
757
|
else:
|
|
745
|
-
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.", file=sys.stderr)
|
|
746
759
|
|
|
747
760
|
|
|
748
761
|
_boot_diagnostic()
|
|
@@ -789,7 +802,7 @@ def _log_crash_to_db(reason: str = "unknown", error_detail: str = "") -> None:
|
|
|
789
802
|
f"crashed: {reason[:100]}", api_key=_get_api_key())
|
|
790
803
|
except Exception:
|
|
791
804
|
pass
|
|
792
|
-
_mc_log(f" crash logged: {reason}",
|
|
805
|
+
_mc_log(f" crash logged: {reason}", file=sys.stderr)
|
|
793
806
|
|
|
794
807
|
|
|
795
808
|
def _on_exit() -> None:
|
|
@@ -1449,9 +1462,9 @@ try:
|
|
|
1449
1462
|
elif isinstance(_ls_val, str):
|
|
1450
1463
|
_LAST_SEEN_TS = _ls_val
|
|
1451
1464
|
if _LAST_SEEN_TS:
|
|
1452
|
-
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.", file=sys.stderr)
|
|
1453
1466
|
except Exception as _e:
|
|
1454
|
-
print(f"[meshcode] Could not restore last_seen: {_e}",
|
|
1467
|
+
print(f"[meshcode] Could not restore last_seen: {_e}", file=sys.stderr)
|
|
1455
1468
|
|
|
1456
1469
|
|
|
1457
1470
|
def _get_pending_tasks_summary() -> Optional[List[Dict[str, str]]]:
|
|
@@ -2611,7 +2624,7 @@ def _auto_update() -> None:
|
|
|
2611
2624
|
return
|
|
2612
2625
|
|
|
2613
2626
|
# 3. Install the new version (blocking, 60s timeout)
|
|
2614
|
-
print(f"[meshcode] Updating {current} → {latest}...",
|
|
2627
|
+
print(f"[meshcode] Updating {current} → {latest}...", file=sys.stderr)
|
|
2615
2628
|
try:
|
|
2616
2629
|
result = subprocess.run(
|
|
2617
2630
|
[sys.executable, "-m", "pip", "install", "--upgrade",
|
|
@@ -2628,8 +2641,14 @@ def _auto_update() -> None:
|
|
|
2628
2641
|
log.debug(f"[meshcode] Auto-update failed: {e}")
|
|
2629
2642
|
return
|
|
2630
2643
|
|
|
2631
|
-
# 4.
|
|
2632
|
-
|
|
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).", file=sys.stderr)
|
|
2648
|
+
return
|
|
2649
|
+
|
|
2650
|
+
# CLI mode: safe to re-exec
|
|
2651
|
+
print(f"[meshcode] Updated to {latest}, restarting...", file=sys.stderr)
|
|
2633
2652
|
os.environ["MESHCODE_UPDATED"] = "1"
|
|
2634
2653
|
try:
|
|
2635
2654
|
os.execv(sys.executable, [sys.executable] + sys.argv)
|
|
@@ -2650,6 +2669,10 @@ def run_server():
|
|
|
2650
2669
|
file=sys.stderr,
|
|
2651
2670
|
)
|
|
2652
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
|
|
2653
2676
|
mcp.run()
|
|
2654
2677
|
except KeyboardInterrupt:
|
|
2655
2678
|
_log_crash_to_db("keyboard_interrupt", "User stopped the agent")
|
|
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
|