meshcode 2.4.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.
Files changed (30) hide show
  1. {meshcode-2.4.0 → meshcode-2.4.1}/PKG-INFO +1 -1
  2. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/__init__.py +1 -1
  3. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/cli.py +13 -1
  4. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/comms_v4.py +18 -2
  5. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/invites.py +15 -2
  6. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/setup_clients.py +11 -11
  7. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode.egg-info/PKG-INFO +1 -1
  8. {meshcode-2.4.0 → meshcode-2.4.1}/pyproject.toml +1 -1
  9. {meshcode-2.4.0 → meshcode-2.4.1}/README.md +0 -0
  10. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/launcher.py +0 -0
  11. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/launcher_install.py +0 -0
  12. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/__init__.py +0 -0
  13. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/__main__.py +0 -0
  14. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/backend.py +0 -0
  15. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/realtime.py +0 -0
  16. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/server.py +0 -0
  17. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/test_backend.py +0 -0
  18. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  19. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  20. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/preferences.py +0 -0
  21. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/protocol_v2.py +0 -0
  22. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/run_agent.py +0 -0
  23. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/secrets.py +0 -0
  24. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode/self_update.py +0 -0
  25. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode.egg-info/SOURCES.txt +0 -0
  26. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode.egg-info/dependency_links.txt +0 -0
  27. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode.egg-info/entry_points.txt +0 -0
  28. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode.egg-info/requires.txt +0 -0
  29. {meshcode-2.4.0 → meshcode-2.4.1}/meshcode.egg-info/top_level.txt +0 -0
  30. {meshcode-2.4.0 → meshcode-2.4.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.4.0
3
+ Version: 2.4.1
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -1,2 +1,2 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "2.4.0"
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
- with open(candidate) as f:
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
- agent = sys.argv[2]
2061
- proj_override = flags.get("project")
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
- project = redeem.get("meshwork")
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
- print("[meshcode] ERROR: redeem succeeded but response was incomplete", file=sys.stderr)
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
@@ -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 auto-generated by meshcode setup"]
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.4.0
3
+ Version: 2.4.1
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meshcode"
7
- version = "2.4.0"
7
+ version = "2.4.1"
8
8
  description = "Real-time communication between AI agents — Supabase-backed CLI"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes
File without changes
File without changes
File without changes