meshcode 2.10.64__tar.gz → 2.10.66__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.64 → meshcode-2.10.66}/PKG-INFO +1 -1
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/__init__.py +1 -1
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/comms_v4.py +109 -11
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.10.64 → meshcode-2.10.66}/pyproject.toml +1 -1
- {meshcode-2.10.64 → meshcode-2.10.66}/README.md +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/cli.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/compat.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/error_hints.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/exceptions.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/invites.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/launcher.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/preferences.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/quickstart.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/secrets.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/self_update.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/supervisor.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode/upload.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/setup.cfg +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_core.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_exceptions.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_sentinel.py +0 -0
- {meshcode-2.10.64 → meshcode-2.10.66}/tests/test_status_enum_coverage.py +0 -0
|
@@ -1171,7 +1171,8 @@ def send_msg(project, from_agent, to_agent, content, msg_type="msg", compact=Fal
|
|
|
1171
1171
|
if agents and agents[0].get("status") not in ("offline", "killed"):
|
|
1172
1172
|
nudge_agent(project, to_agent, from_agent)
|
|
1173
1173
|
else:
|
|
1174
|
-
print(f"[
|
|
1174
|
+
print(f"[meshcode] ERROR: Could not send message to {project}")
|
|
1175
|
+
print(f"[meshcode] Fix: Check project name with: meshcode projects")
|
|
1175
1176
|
|
|
1176
1177
|
|
|
1177
1178
|
def broadcast(project, from_agent, content, msg_type="broadcast"):
|
|
@@ -1200,7 +1201,7 @@ def read_messages(project, name, silent=False, send_acks=True):
|
|
|
1200
1201
|
project_id = get_project_id(project)
|
|
1201
1202
|
if not project_id:
|
|
1202
1203
|
if not silent:
|
|
1203
|
-
print("
|
|
1204
|
+
print("No new messages.")
|
|
1204
1205
|
return []
|
|
1205
1206
|
|
|
1206
1207
|
# Get unread messages
|
|
@@ -1210,7 +1211,7 @@ def read_messages(project, name, silent=False, send_acks=True):
|
|
|
1210
1211
|
|
|
1211
1212
|
if not messages:
|
|
1212
1213
|
if not silent:
|
|
1213
|
-
print("
|
|
1214
|
+
print("No new messages.")
|
|
1214
1215
|
return []
|
|
1215
1216
|
|
|
1216
1217
|
# Mark all as read
|
|
@@ -1463,12 +1464,60 @@ def show_board(project):
|
|
|
1463
1464
|
print(f"[{project}] No agents found")
|
|
1464
1465
|
return
|
|
1465
1466
|
|
|
1467
|
+
def _human_time(ts_str):
|
|
1468
|
+
"""Convert ISO timestamp to human-readable age."""
|
|
1469
|
+
if not ts_str:
|
|
1470
|
+
return ""
|
|
1471
|
+
try:
|
|
1472
|
+
from datetime import datetime, timezone
|
|
1473
|
+
dt = datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
|
|
1474
|
+
age = (datetime.now(timezone.utc) - dt).total_seconds()
|
|
1475
|
+
if age < 60: return f"{int(age)}s ago"
|
|
1476
|
+
if age < 3600: return f"{int(age/60)}m ago"
|
|
1477
|
+
if age < 86400: return f"{int(age/3600)}h ago"
|
|
1478
|
+
return f"{int(age/86400)}d ago"
|
|
1479
|
+
except Exception:
|
|
1480
|
+
return ""
|
|
1481
|
+
|
|
1466
1482
|
print(f"\n{'='*60}")
|
|
1467
1483
|
print(f" {project.upper()} — Board")
|
|
1468
1484
|
print(f"{'='*60}")
|
|
1469
1485
|
for a in agents:
|
|
1470
|
-
|
|
1471
|
-
|
|
1486
|
+
status = a.get("status", "?")
|
|
1487
|
+
icon = {"online": "●", "working": "▶", "blocked": "■", "standby": "◆",
|
|
1488
|
+
"idle": "○", "done": "✓", "sleeping": "☾", "offline": "✗",
|
|
1489
|
+
"waiting": "◎"}.get(status, "?")
|
|
1490
|
+
task = a.get("task", "")
|
|
1491
|
+
# Clean up noisy task text
|
|
1492
|
+
if task and "idle" in task.lower() and "s —" in task:
|
|
1493
|
+
task = "" # hide "idle 29720s — still listening"
|
|
1494
|
+
hb_age = _human_time(a.get("last_heartbeat"))
|
|
1495
|
+
hb_info = f" ({hb_age})" if hb_age and status in ("offline", "sleeping") else ""
|
|
1496
|
+
print(f" {icon} {a['name']:<14} {status:<10} {task[:40]}{hb_info}")
|
|
1497
|
+
|
|
1498
|
+
# Enhanced: show health + tasks + activity from dashboard summary
|
|
1499
|
+
if _ak:
|
|
1500
|
+
try:
|
|
1501
|
+
summary = sb_rpc("mc_get_dashboard_summary", {"p_api_key": _ak})
|
|
1502
|
+
if isinstance(summary, dict) and summary.get("ok"):
|
|
1503
|
+
h = summary.get("health", {})
|
|
1504
|
+
print(f"\n{'-'*60}")
|
|
1505
|
+
print(f" Health: {h.get('active_agents',0)}/{h.get('total_agents',0)} agents active"
|
|
1506
|
+
f" | {h.get('open_tasks',0)} open tasks"
|
|
1507
|
+
f" | {h.get('messages_today',0)} messages today")
|
|
1508
|
+
tasks = summary.get("tasks", [])
|
|
1509
|
+
if tasks:
|
|
1510
|
+
print(f"\n Open tasks:")
|
|
1511
|
+
for t in tasks[:5]:
|
|
1512
|
+
prio_icon = {"urgent": "!!", "high": "!", "normal": " ", "low": "."}.get(t.get("priority", ""), " ")
|
|
1513
|
+
print(f" [{prio_icon}] {t.get('title','')[:50]} ({t.get('assignee','?')})")
|
|
1514
|
+
activity = summary.get("activity", [])
|
|
1515
|
+
if activity:
|
|
1516
|
+
print(f"\n Recent activity:")
|
|
1517
|
+
for e in activity[:5]:
|
|
1518
|
+
print(f" {e.get('agent','?')}: {e.get('summary','')[:50]}")
|
|
1519
|
+
except Exception:
|
|
1520
|
+
pass
|
|
1472
1521
|
print()
|
|
1473
1522
|
|
|
1474
1523
|
|
|
@@ -2040,11 +2089,11 @@ def cmd_doctor(flags, pos):
|
|
|
2040
2089
|
else:
|
|
2041
2090
|
fail("API key", "not found. Run: meshcode login <api_key>")
|
|
2042
2091
|
|
|
2043
|
-
# 4c. MCP server import test
|
|
2092
|
+
# 4c. MCP server import test (lightweight — only test backend, not full server)
|
|
2044
2093
|
try:
|
|
2045
2094
|
import importlib
|
|
2046
|
-
_mod = importlib.import_module("meshcode.meshcode_mcp.
|
|
2047
|
-
ok("MCP server", "module imports cleanly")
|
|
2095
|
+
_mod = importlib.import_module("meshcode.meshcode_mcp.backend")
|
|
2096
|
+
ok("MCP server", "core module imports cleanly")
|
|
2048
2097
|
except Exception as e:
|
|
2049
2098
|
fail("MCP server", f"import error: {e}. Run: pip install --upgrade meshcode")
|
|
2050
2099
|
|
|
@@ -2576,6 +2625,17 @@ if __name__ == "__main__":
|
|
|
2576
2625
|
pass
|
|
2577
2626
|
|
|
2578
2627
|
if len(sys.argv) < 2:
|
|
2628
|
+
# First-run detection: no credentials = new user
|
|
2629
|
+
_config = Path.home() / ".meshcode"
|
|
2630
|
+
_has_creds = (_config / "profile_meta.json").exists() if _config.exists() else False
|
|
2631
|
+
if not _has_creds:
|
|
2632
|
+
print()
|
|
2633
|
+
print(" Welcome to MeshCode!")
|
|
2634
|
+
print(" First time? Run: meshcode quickstart")
|
|
2635
|
+
print(" Already have an API key? Run: meshcode login <api_key>")
|
|
2636
|
+
print(" Need help? Run: meshcode doctor")
|
|
2637
|
+
print()
|
|
2638
|
+
sys.exit(0)
|
|
2579
2639
|
show_help()
|
|
2580
2640
|
sys.exit(0)
|
|
2581
2641
|
|
|
@@ -2959,9 +3019,47 @@ if __name__ == "__main__":
|
|
|
2959
3019
|
# name exists in multiple meshworks
|
|
2960
3020
|
# meshcode run <agent> (backwards compat, same as go)
|
|
2961
3021
|
if len(sys.argv) < 3:
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
3022
|
+
# Interactive agent selection
|
|
3023
|
+
_ak = _load_api_key_for_cli()
|
|
3024
|
+
if _ak:
|
|
3025
|
+
try:
|
|
3026
|
+
_projs = sb_rpc("mc_list_user_projects", {"p_api_key": _ak})
|
|
3027
|
+
_agents_list = []
|
|
3028
|
+
if isinstance(_projs, dict) and _projs.get("projects"):
|
|
3029
|
+
for _p in _projs["projects"]:
|
|
3030
|
+
for _a in (_p.get("agents") or []):
|
|
3031
|
+
_agents_list.append((_p["name"], _a.get("name", "?")))
|
|
3032
|
+
if _agents_list:
|
|
3033
|
+
print(f"\n Your agents:")
|
|
3034
|
+
for i, (pn, an) in enumerate(_agents_list, 1):
|
|
3035
|
+
print(f" {i}. {pn}/{an}")
|
|
3036
|
+
print()
|
|
3037
|
+
choice = input(" Which agent? (number or name): ").strip()
|
|
3038
|
+
if choice.isdigit() and 1 <= int(choice) <= len(_agents_list):
|
|
3039
|
+
pn, an = _agents_list[int(choice) - 1]
|
|
3040
|
+
sys.argv = [sys.argv[0], cmd, f"{pn}/{an}"] + sys.argv[2:]
|
|
3041
|
+
elif "/" in choice:
|
|
3042
|
+
sys.argv = [sys.argv[0], cmd, choice] + sys.argv[2:]
|
|
3043
|
+
else:
|
|
3044
|
+
# Try to find by name
|
|
3045
|
+
matches = [(pn, an) for pn, an in _agents_list if an == choice]
|
|
3046
|
+
if matches:
|
|
3047
|
+
pn, an = matches[0]
|
|
3048
|
+
sys.argv = [sys.argv[0], cmd, f"{pn}/{an}"] + sys.argv[2:]
|
|
3049
|
+
else:
|
|
3050
|
+
print(f" Agent '{choice}' not found.")
|
|
3051
|
+
sys.exit(1)
|
|
3052
|
+
else:
|
|
3053
|
+
print(f" No agents found. Run: meshcode quickstart")
|
|
3054
|
+
sys.exit(1)
|
|
3055
|
+
except Exception:
|
|
3056
|
+
print(f"Usage: meshcode {cmd} <agent> [--project <name>]")
|
|
3057
|
+
sys.exit(1)
|
|
3058
|
+
else:
|
|
3059
|
+
print(f"Usage: meshcode {cmd} <agent> [--project <name>] [--editor claude|cursor|code]")
|
|
3060
|
+
print(f" or: meshcode {cmd} <project>/<agent> (disambiguation)")
|
|
3061
|
+
print(f"\n Not logged in? Run: meshcode quickstart")
|
|
3062
|
+
sys.exit(1)
|
|
2965
3063
|
agent_arg = sys.argv[2]
|
|
2966
3064
|
# Parse project/agent syntax
|
|
2967
3065
|
if "/" in agent_arg and not agent_arg.startswith("/"):
|
|
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
|
|
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
|