meshcode 2.3.0__tar.gz → 2.4.1__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.3.0 → meshcode-2.4.1}/PKG-INFO +1 -1
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/__init__.py +1 -1
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/cli.py +13 -1
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/comms_v4.py +18 -2
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/invites.py +15 -2
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/server.py +8 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/setup_clients.py +11 -11
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.3.0 → meshcode-2.4.1}/pyproject.toml +1 -1
- {meshcode-2.3.0 → meshcode-2.4.1}/README.md +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/launcher.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/launcher_install.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/preferences.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/run_agent.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/secrets.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode/self_update.py +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.3.0 → meshcode-2.4.1}/setup.cfg +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""MeshCode — Real-time communication between AI agents."""
|
|
2
|
-
__version__ = "2.
|
|
2
|
+
__version__ = "2.4.1"
|
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
import sys
|
|
4
4
|
import os
|
|
5
5
|
|
|
6
|
+
# Force UTF-8 on stdout/stderr for Windows terminals (cp1252 default crashes
|
|
7
|
+
# on emojis, em-dashes, and any non-ASCII output the CLI prints).
|
|
8
|
+
# Python 3.7+ supports reconfigure(); older versions get a no-op.
|
|
9
|
+
try:
|
|
10
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
11
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
12
|
+
if hasattr(sys.stderr, "reconfigure"):
|
|
13
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
14
|
+
except Exception:
|
|
15
|
+
pass
|
|
16
|
+
|
|
6
17
|
|
|
7
18
|
def main():
|
|
8
19
|
"""Entry point that delegates to comms_v4.py within the package."""
|
|
@@ -18,7 +29,8 @@ def main():
|
|
|
18
29
|
sys.argv[0] = candidate
|
|
19
30
|
# Set __name__ and __file__ for the script
|
|
20
31
|
globs = {"__name__": "__main__", "__file__": candidate}
|
|
21
|
-
|
|
32
|
+
# Force UTF-8 on Windows (cp1252 default crashes on non-ASCII)
|
|
33
|
+
with open(candidate, encoding="utf-8") as f:
|
|
22
34
|
exec(compile(f.read(), candidate, "exec"), globs)
|
|
23
35
|
return
|
|
24
36
|
|
|
@@ -2053,12 +2053,28 @@ if __name__ == "__main__":
|
|
|
2053
2053
|
|
|
2054
2054
|
elif cmd in ("run", "go"):
|
|
2055
2055
|
# meshcode go <agent> [--project <name>] [--editor claude|cursor|code]
|
|
2056
|
+
# meshcode go <project>/<agent> — disambiguation syntax when same agent
|
|
2057
|
+
# name exists in multiple meshworks
|
|
2056
2058
|
# meshcode run <agent> (backwards compat, same as go)
|
|
2057
2059
|
if len(sys.argv) < 3:
|
|
2058
2060
|
print(f"Usage: meshcode {cmd} <agent> [--project <name>] [--editor claude|cursor|code]")
|
|
2061
|
+
print(f" or: meshcode {cmd} <project>/<agent> (disambiguation)")
|
|
2059
2062
|
sys.exit(1)
|
|
2060
|
-
|
|
2061
|
-
|
|
2063
|
+
agent_arg = sys.argv[2]
|
|
2064
|
+
# Parse project/agent syntax
|
|
2065
|
+
if "/" in agent_arg and not agent_arg.startswith("/"):
|
|
2066
|
+
parts = agent_arg.split("/", 1)
|
|
2067
|
+
if len(parts) == 2 and parts[0] and parts[1]:
|
|
2068
|
+
proj_override = parts[0]
|
|
2069
|
+
agent = parts[1]
|
|
2070
|
+
else:
|
|
2071
|
+
agent = agent_arg
|
|
2072
|
+
proj_override = flags.get("project")
|
|
2073
|
+
else:
|
|
2074
|
+
agent = agent_arg
|
|
2075
|
+
proj_override = flags.get("project")
|
|
2076
|
+
if flags.get("project"):
|
|
2077
|
+
proj_override = flags.get("project")
|
|
2062
2078
|
editor_override = flags.get("editor")
|
|
2063
2079
|
perm_override = flags.get("permission") # bypass | safe | ask
|
|
2064
2080
|
if "--bypass" in sys.argv:
|
|
@@ -165,15 +165,28 @@ def cmd_join(token: str, display_name: Optional[str] = None) -> int:
|
|
|
165
165
|
if not isinstance(redeem, dict):
|
|
166
166
|
print("[meshcode] ERROR: could not redeem invite", file=sys.stderr)
|
|
167
167
|
return 1
|
|
168
|
+
# Handle both application errors ({"error": "..."}) and Postgres errors
|
|
169
|
+
# ({"code": "42703", "message": "...", "details": ..., "hint": ...})
|
|
168
170
|
if redeem.get("error"):
|
|
169
171
|
print(f"[meshcode] ERROR: {redeem['error']}", file=sys.stderr)
|
|
170
172
|
return 1
|
|
173
|
+
if redeem.get("code") and redeem.get("message"):
|
|
174
|
+
print(f"[meshcode] ERROR: database error {redeem['code']}: {redeem['message']}", file=sys.stderr)
|
|
175
|
+
if redeem.get("hint"):
|
|
176
|
+
print(f"[meshcode] hint: {redeem['hint']}", file=sys.stderr)
|
|
177
|
+
return 1
|
|
171
178
|
|
|
172
179
|
scoped_key = redeem.get("api_key")
|
|
173
|
-
|
|
180
|
+
# Backend returns both "project_name" (canonical) and "meshwork" (legacy)
|
|
181
|
+
project = redeem.get("project_name") or redeem.get("meshwork")
|
|
174
182
|
agent = redeem.get("agent_name")
|
|
175
183
|
if not scoped_key or not project or not agent:
|
|
176
|
-
|
|
184
|
+
missing = []
|
|
185
|
+
if not scoped_key: missing.append("api_key")
|
|
186
|
+
if not project: missing.append("project_name/meshwork")
|
|
187
|
+
if not agent: missing.append("agent_name")
|
|
188
|
+
print(f"[meshcode] ERROR: redeem succeeded but response missing fields: {', '.join(missing)}", file=sys.stderr)
|
|
189
|
+
print(f"[meshcode] got keys: {sorted(redeem.keys())}", file=sys.stderr)
|
|
177
190
|
return 1
|
|
178
191
|
|
|
179
192
|
# 3. Store scoped key in OS keychain under a per-mesh profile
|
|
@@ -647,6 +647,7 @@ try:
|
|
|
647
647
|
_memories = be.sb_rpc("mc_memory_list", {
|
|
648
648
|
"p_api_key": _get_api_key(),
|
|
649
649
|
"p_agent_name": AGENT_NAME,
|
|
650
|
+
"p_project_name": PROJECT_NAME,
|
|
650
651
|
})
|
|
651
652
|
if isinstance(_memories, dict) and _memories.get("ok") and _memories.get("memories"):
|
|
652
653
|
_mem_lines = []
|
|
@@ -1272,6 +1273,7 @@ def meshcode_accept_link(link_id: str) -> Dict[str, Any]:
|
|
|
1272
1273
|
return be.sb_rpc("mc_accept_mesh_link", {
|
|
1273
1274
|
"p_api_key": api_key,
|
|
1274
1275
|
"p_link_id": link_id,
|
|
1276
|
+
"p_project_name": PROJECT_NAME,
|
|
1275
1277
|
})
|
|
1276
1278
|
|
|
1277
1279
|
|
|
@@ -1297,6 +1299,7 @@ def meshcode_expand_link(link_id: str, agents: str) -> Dict[str, Any]:
|
|
|
1297
1299
|
"p_link_id": link_id,
|
|
1298
1300
|
"p_src": agent_list,
|
|
1299
1301
|
"p_tgt": agent_list,
|
|
1302
|
+
"p_project_name": PROJECT_NAME,
|
|
1300
1303
|
})
|
|
1301
1304
|
|
|
1302
1305
|
|
|
@@ -1367,6 +1370,7 @@ def meshcode_edit_memory(agent_name: str, key: str, value: Any) -> Dict[str, Any
|
|
|
1367
1370
|
"p_agent_name": agent_name,
|
|
1368
1371
|
"p_key": key,
|
|
1369
1372
|
"p_value": json_value,
|
|
1373
|
+
"p_project_name": PROJECT_NAME,
|
|
1370
1374
|
}) or {"error": "failed to edit memory"}
|
|
1371
1375
|
|
|
1372
1376
|
|
|
@@ -1485,6 +1489,7 @@ def meshcode_remember(key: str, value: Any) -> Dict[str, Any]:
|
|
|
1485
1489
|
"p_agent_name": AGENT_NAME,
|
|
1486
1490
|
"p_key": key,
|
|
1487
1491
|
"p_value": json_value,
|
|
1492
|
+
"p_project_name": PROJECT_NAME,
|
|
1488
1493
|
})
|
|
1489
1494
|
# Best-effort sync to Obsidian vault (if configured)
|
|
1490
1495
|
if isinstance(result, dict) and result.get("ok"):
|
|
@@ -1509,11 +1514,13 @@ def meshcode_recall(key: Optional[str] = None) -> Dict[str, Any]:
|
|
|
1509
1514
|
"p_api_key": api_key,
|
|
1510
1515
|
"p_agent_name": AGENT_NAME,
|
|
1511
1516
|
"p_key": key,
|
|
1517
|
+
"p_project_name": PROJECT_NAME,
|
|
1512
1518
|
})
|
|
1513
1519
|
else:
|
|
1514
1520
|
return be.sb_rpc("mc_memory_list", {
|
|
1515
1521
|
"p_api_key": api_key,
|
|
1516
1522
|
"p_agent_name": AGENT_NAME,
|
|
1523
|
+
"p_project_name": PROJECT_NAME,
|
|
1517
1524
|
})
|
|
1518
1525
|
|
|
1519
1526
|
|
|
@@ -1530,6 +1537,7 @@ def meshcode_forget(key: str) -> Dict[str, Any]:
|
|
|
1530
1537
|
"p_api_key": api_key,
|
|
1531
1538
|
"p_agent_name": AGENT_NAME,
|
|
1532
1539
|
"p_key": key,
|
|
1540
|
+
"p_project_name": PROJECT_NAME,
|
|
1533
1541
|
})
|
|
1534
1542
|
|
|
1535
1543
|
|
|
@@ -53,7 +53,7 @@ def _load_credentials(profile: str = "default") -> Dict[str, str]:
|
|
|
53
53
|
meta: Dict[str, Any] = {"api_key": api_key}
|
|
54
54
|
if meta_path.exists():
|
|
55
55
|
try:
|
|
56
|
-
extra = json.loads(meta_path.read_text())
|
|
56
|
+
extra = json.loads(meta_path.read_text(encoding="utf-8"))
|
|
57
57
|
if isinstance(extra, dict):
|
|
58
58
|
meta.update({k: v for k, v in extra.items() if v is not None})
|
|
59
59
|
except Exception:
|
|
@@ -64,7 +64,7 @@ def _load_credentials(profile: str = "default") -> Dict[str, str]:
|
|
|
64
64
|
legacy_path = Path.home() / ".meshcode" / "credentials.json"
|
|
65
65
|
if legacy_path.exists() and not meta_path.exists():
|
|
66
66
|
try:
|
|
67
|
-
legacy = json.loads(legacy_path.read_text())
|
|
67
|
+
legacy = json.loads(legacy_path.read_text(encoding="utf-8"))
|
|
68
68
|
if isinstance(legacy, dict):
|
|
69
69
|
for k in ("user_id", "email", "display_name"):
|
|
70
70
|
if legacy.get(k) and not meta.get(k):
|
|
@@ -81,7 +81,7 @@ def _load_supabase_env() -> Dict[str, str]:
|
|
|
81
81
|
if not url or not key:
|
|
82
82
|
env_file = Path.home() / ".meshcode" / "env"
|
|
83
83
|
if env_file.exists():
|
|
84
|
-
for line in env_file.read_text().splitlines():
|
|
84
|
+
for line in env_file.read_text(encoding="utf-8").splitlines():
|
|
85
85
|
line = line.strip()
|
|
86
86
|
if line.startswith("export "):
|
|
87
87
|
line = line[7:]
|
|
@@ -204,7 +204,7 @@ def _build_server_block(project: str, project_id: str, agent: str, role: str,
|
|
|
204
204
|
def _atomic_write_json(path: Path, data: dict) -> None:
|
|
205
205
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
206
206
|
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
207
|
-
tmp.write_text(json.dumps(data, indent=2))
|
|
207
|
+
tmp.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
208
208
|
tmp.replace(path)
|
|
209
209
|
|
|
210
210
|
|
|
@@ -258,7 +258,7 @@ def _atomic_write_toml_codex(path: Path, server_id: str, server_block: Dict[str,
|
|
|
258
258
|
Atomic via tempfile + replace.
|
|
259
259
|
"""
|
|
260
260
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
261
|
-
text = path.read_text() if path.exists() else ""
|
|
261
|
+
text = path.read_text(encoding="utf-8") if path.exists() else ""
|
|
262
262
|
|
|
263
263
|
quoted = f'"{_toml_escape(server_id)}"'
|
|
264
264
|
header = f"[mcp_servers.{quoted}]"
|
|
@@ -291,7 +291,7 @@ def _atomic_write_toml_codex(path: Path, server_id: str, server_block: Dict[str,
|
|
|
291
291
|
new_text += _render_codex_block(server_id, server_block)
|
|
292
292
|
|
|
293
293
|
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
294
|
-
tmp.write_text(new_text)
|
|
294
|
+
tmp.write_text(new_text, encoding="utf-8")
|
|
295
295
|
tmp.replace(path)
|
|
296
296
|
|
|
297
297
|
|
|
@@ -306,7 +306,7 @@ def _write_continue_config(ws: Path, server_id: str, server_block: Dict[str, Any
|
|
|
306
306
|
args = server_block.get("args", [])
|
|
307
307
|
|
|
308
308
|
# Build YAML manually (no yaml dependency)
|
|
309
|
-
lines = ["# MeshCode MCP server
|
|
309
|
+
lines = ["# MeshCode MCP server - auto-generated by meshcode setup"]
|
|
310
310
|
lines.append("models: []")
|
|
311
311
|
lines.append("mcpServers:")
|
|
312
312
|
lines.append(f" - name: {server_id}")
|
|
@@ -323,7 +323,7 @@ def _write_continue_config(ws: Path, server_id: str, server_block: Dict[str, Any
|
|
|
323
323
|
config_dir.mkdir(parents=True, exist_ok=True)
|
|
324
324
|
config_path = config_dir / "config.yaml"
|
|
325
325
|
try:
|
|
326
|
-
config_path.write_text("\n".join(lines) + "\n")
|
|
326
|
+
config_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
327
327
|
except (IOError, OSError) as e:
|
|
328
328
|
print(f"[meshcode] WARNING: could not write Continue config: {e}", file=sys.stderr)
|
|
329
329
|
|
|
@@ -423,7 +423,7 @@ def setup_workspace(project: str, agent: str, role: str = "",
|
|
|
423
423
|
registry: Dict[str, Any] = {}
|
|
424
424
|
if registry_path.exists():
|
|
425
425
|
try:
|
|
426
|
-
registry = json.loads(registry_path.read_text())
|
|
426
|
+
registry = json.loads(registry_path.read_text(encoding="utf-8"))
|
|
427
427
|
except Exception:
|
|
428
428
|
registry = {}
|
|
429
429
|
registry.setdefault("agents", {})[agent] = {
|
|
@@ -463,7 +463,7 @@ This is your workspace dir — feel free to scaffold any files your task needs h
|
|
|
463
463
|
work in a different repo by `cd`ing elsewhere after launch.
|
|
464
464
|
"""
|
|
465
465
|
try:
|
|
466
|
-
(ws / "README.md").write_text(readme_body)
|
|
466
|
+
(ws / "README.md").write_text(readme_body, encoding="utf-8")
|
|
467
467
|
except Exception as _e:
|
|
468
468
|
print(f"[meshcode] WARNING: could not write README.md: {_e}", file=sys.stderr)
|
|
469
469
|
|
|
@@ -567,7 +567,7 @@ def setup_global(client: str, project: str, agent: str, role: str = "") -> int:
|
|
|
567
567
|
existing: Dict[str, Any] = {}
|
|
568
568
|
if config_path.exists():
|
|
569
569
|
try:
|
|
570
|
-
existing = json.loads(config_path.read_text())
|
|
570
|
+
existing = json.loads(config_path.read_text(encoding="utf-8"))
|
|
571
571
|
except json.JSONDecodeError:
|
|
572
572
|
print(f"[meshcode] WARNING: {config_path} exists but is not valid JSON.", file=sys.stderr)
|
|
573
573
|
return 2
|
|
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
|