meshcode 2.10.42__tar.gz → 2.10.43__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 (32) hide show
  1. {meshcode-2.10.42 → meshcode-2.10.43}/PKG-INFO +1 -1
  2. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/__init__.py +1 -1
  3. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/comms_v4.py +45 -1
  4. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/meshcode_mcp/server.py +29 -8
  5. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/run_agent.py +30 -6
  6. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/secrets.py +29 -8
  7. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/setup_clients.py +20 -0
  8. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode.egg-info/PKG-INFO +1 -1
  9. {meshcode-2.10.42 → meshcode-2.10.43}/pyproject.toml +1 -1
  10. {meshcode-2.10.42 → meshcode-2.10.43}/README.md +0 -0
  11. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/ascii_art.py +0 -0
  12. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/cli.py +0 -0
  13. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/invites.py +0 -0
  14. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/launcher.py +0 -0
  15. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/launcher_install.py +0 -0
  16. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/meshcode_mcp/__init__.py +0 -0
  17. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/meshcode_mcp/__main__.py +0 -0
  18. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/meshcode_mcp/backend.py +0 -0
  19. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/meshcode_mcp/realtime.py +0 -0
  20. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/meshcode_mcp/test_backend.py +0 -0
  21. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  22. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  23. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/preferences.py +0 -0
  24. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/protocol_v2.py +0 -0
  25. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode/self_update.py +0 -0
  26. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode.egg-info/SOURCES.txt +0 -0
  27. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode.egg-info/dependency_links.txt +0 -0
  28. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode.egg-info/entry_points.txt +0 -0
  29. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode.egg-info/requires.txt +0 -0
  30. {meshcode-2.10.42 → meshcode-2.10.43}/meshcode.egg-info/top_level.txt +0 -0
  31. {meshcode-2.10.42 → meshcode-2.10.43}/setup.cfg +0 -0
  32. {meshcode-2.10.42 → meshcode-2.10.43}/tests/test_status_enum_coverage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.10.42
3
+ Version: 2.10.43
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.10.42"
2
+ __version__ = "2.10.43"
@@ -352,6 +352,48 @@ def send_notification(project, name, from_agent, pending=1):
352
352
  pass
353
353
 
354
354
 
355
+ def _nudge_windows_terminal(name, pending, tty, pid):
356
+ """Activate the agent's terminal window on Windows.
357
+
358
+ Uses PowerShell + kernel32 to find the console window by process ID and
359
+ bring it to the foreground. Does NOT send keystrokes — that would risk
360
+ confirming prompts or interrupting bypass mode. The window activation
361
+ combined with the desktop notification is enough to alert the user.
362
+ """
363
+ if not pid:
364
+ return
365
+ try:
366
+ pid = int(pid)
367
+ except (ValueError, TypeError):
368
+ return
369
+ try:
370
+ ps_script = f'''
371
+ $proc = Get-Process -Id {pid} -ErrorAction SilentlyContinue
372
+ if ($proc -and $proc.MainWindowHandle -ne 0) {{
373
+ Add-Type @"
374
+ using System;
375
+ using System.Runtime.InteropServices;
376
+ public class WinApi {{
377
+ [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
378
+ [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
379
+ }}
380
+ "@
381
+ [WinApi]::ShowWindow($proc.MainWindowHandle, 9)
382
+ [WinApi]::SetForegroundWindow($proc.MainWindowHandle)
383
+ }}
384
+ '''
385
+ subprocess.run(
386
+ ["powershell", "-NoProfile", "-Command", ps_script],
387
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
388
+ timeout=10,
389
+ )
390
+ log_msg(f"[nudge] Windows: activated terminal for {name} (pid={pid})")
391
+ except subprocess.TimeoutExpired:
392
+ log_msg(f"[nudge] Windows: PowerShell timed out for {name}")
393
+ except Exception as e:
394
+ log_msg(f"[nudge] Windows: failed for {name}: {e}")
395
+
396
+
355
397
  def _is_pid_alive(pid):
356
398
  """Return True iff the given pid corresponds to a live process."""
357
399
  if not pid:
@@ -796,8 +838,10 @@ def nudge_agent(project, name, from_agent=""):
796
838
  message = f"Tienes {pending} mensaje(s). Revisa: meshcode read {project} {name}"
797
839
  escaped_message = message.replace('\\', '\\\\').replace('"', '\\"')
798
840
 
799
- # Only attempt AppleScript nudge on macOS
841
+ # Non-macOS: use platform-specific terminal nudge
800
842
  if sys.platform != "darwin":
843
+ if sys.platform == "win32":
844
+ _nudge_windows_terminal(name, pending, tty, cached_pid)
801
845
  send_notification(project, name, from_agent, pending)
802
846
  mark_nudged(project, name)
803
847
  return True
@@ -108,14 +108,14 @@ _SEEN_TTL = 300.0 # 5 minutes
108
108
  # ============================================================
109
109
  # Auto-wake: when agent is NOT in meshcode_wait and a message
110
110
  # arrives, inject text into the terminal to wake the agent.
111
- # DEFAULT: ON. Disable with MESHCODE_AUTO_WAKE=0 if you don't want it.
112
111
  # ============================================================
113
112
  _IN_WAIT = False # True while meshcode_wait is blocking
114
- # Default OFF as of 2.10.27 the AppleScript/PowerShell keystroke injection
115
- # wrote nudge text directly into the user's terminal, which corrupts stdin on
116
- # terminals that don't interpret ANSI, interrupts the user mid-typing, and
117
- # duplicates what the dashboard already surfaces. Opt-in with
118
- # MESHCODE_AUTO_WAKE=1 for users who want the legacy behavior.
113
+ # Default OFF keystroke injection can corrupt stdin on some terminals.
114
+ # The primary nudge path is now in comms_v4.nudge_agent() which uses
115
+ # platform-specific window activation (SetForegroundWindow on Windows,
116
+ # AppleScript on macOS) + desktop notification.
117
+ # Set MESHCODE_AUTO_WAKE=1 to also inject text into the terminal from
118
+ # the MCP server side (useful if comms_v4 nudge doesn't reach the agent).
119
119
  _AUTO_WAKE = os.environ.get("MESHCODE_AUTO_WAKE", "0").lower() in ("1", "true", "yes")
120
120
 
121
121
 
@@ -197,8 +197,10 @@ def _try_auto_wake(from_agent: str, preview: str) -> None:
197
197
  Add-Type -AssemblyName System.Windows.Forms
198
198
  [System.Windows.Forms.SendKeys]::SendWait("{sk_safe}{{ENTER}}")
199
199
  '''
200
- subprocess.Popen(["powershell", "-Command", ps_script],
201
- stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
200
+ # Use subprocess.run with timeout to prevent orphaned PowerShell processes
201
+ subprocess.run(["powershell", "-NoProfile", "-Command", ps_script],
202
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
203
+ timeout=10)
202
204
  log.info("auto-wake: injected nudge via PowerShell SendKeys")
203
205
  else:
204
206
  # Linux — xdotool takes args directly, not shell-interpolated (safe)
@@ -3026,6 +3028,25 @@ def meshcode_recall_search(query: str) -> Dict[str, Any]:
3026
3028
  })
3027
3029
 
3028
3030
 
3031
+ # ----------------- BUG REPORTING -----------------
3032
+
3033
+ @mcp.tool()
3034
+ @with_working_status
3035
+ def meshcode_report_bug(description: str) -> Dict[str, Any]:
3036
+ """Report a bug from within the mesh. Visible in admin panel.
3037
+
3038
+ Args:
3039
+ description: What went wrong — include steps to reproduce if possible.
3040
+ """
3041
+ api_key = _get_api_key()
3042
+ return be.sb_rpc("mc_report_bug", {
3043
+ "p_api_key": api_key,
3044
+ "p_description": description,
3045
+ "p_meshwork_name": PROJECT_NAME,
3046
+ "p_agent_name": AGENT_NAME,
3047
+ })
3048
+
3049
+
3029
3050
  # ----------------- HEALTH CHECK -----------------
3030
3051
 
3031
3052
  @mcp.tool()
@@ -363,13 +363,30 @@ def _claude_supports_bypass(editor_cmd: str) -> bool:
363
363
  """
364
364
  try:
365
365
  use_shell = sys.platform == "win32" and editor_cmd.lower().endswith((".cmd", ".bat"))
366
+ print(f"[meshcode] bypass-detect: checking '{editor_cmd}' (shell={use_shell})", file=sys.stderr)
366
367
  r = subprocess.run(
367
368
  [editor_cmd, "--help"],
368
369
  capture_output=True, text=True, timeout=10,
369
370
  shell=use_shell,
370
371
  )
371
- return "--dangerously-skip-permissions" in r.stdout
372
- except Exception:
372
+ found = "--dangerously-skip-permissions" in r.stdout
373
+ if found:
374
+ print(f"[meshcode] bypass-detect: ✓ flag found — bypass mode available", file=sys.stderr)
375
+ else:
376
+ stdout_len = len(r.stdout)
377
+ stderr_preview = (r.stderr or "")[:200]
378
+ print(f"[meshcode] bypass-detect: ✗ flag NOT found in --help output ({stdout_len} chars, exit={r.returncode})", file=sys.stderr)
379
+ if stderr_preview:
380
+ print(f"[meshcode] bypass-detect: stderr: {stderr_preview}", file=sys.stderr)
381
+ return found
382
+ except subprocess.TimeoutExpired:
383
+ print(f"[meshcode] bypass-detect: ✗ '{editor_cmd} --help' timed out after 10s", file=sys.stderr)
384
+ return False
385
+ except FileNotFoundError:
386
+ print(f"[meshcode] bypass-detect: ✗ '{editor_cmd}' not found in PATH", file=sys.stderr)
387
+ return False
388
+ except Exception as e:
389
+ print(f"[meshcode] bypass-detect: ✗ unexpected error: {e}", file=sys.stderr)
373
390
  return False
374
391
 
375
392
 
@@ -377,17 +394,22 @@ def _detect_editor() -> Optional[str]:
377
394
  """Pick the user's preferred MCP-aware editor."""
378
395
  override = os.environ.get("MESHCODE_EDITOR", "").strip().lower()
379
396
  if override:
380
- if shutil.which(override):
381
- return override
397
+ resolved = shutil.which(override)
398
+ if resolved:
399
+ # On Windows, return resolved path to preserve .cmd/.bat extension
400
+ return resolved if sys.platform == "win32" else override
382
401
  print(f"[meshcode] WARNING: MESHCODE_EDITOR='{override}' not found in PATH", file=sys.stderr)
383
402
 
384
403
  # Detection order: most likely to be installed + best per-workspace MCP
385
404
  # support first. codex is last because its MCP config is GLOBAL only —
386
405
  # launching it doesn't take a workspace-scoped .mcp.json, so it should
387
406
  # only be picked if nothing else is available.
407
+ # On Windows, return the fully-resolved path so .cmd/.bat extension is
408
+ # preserved — needed for shell=True detection in bypass and launch paths.
388
409
  for cmd in ("claude", "cursor", "code", "windsurf", "codex"):
389
- if shutil.which(cmd):
390
- return cmd
410
+ resolved = shutil.which(cmd)
411
+ if resolved:
412
+ return resolved if sys.platform == "win32" else cmd
391
413
 
392
414
  # Windows fallback: check common install locations not in PATH
393
415
  if sys.platform == "win32":
@@ -655,6 +677,8 @@ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str
655
677
  # MCP" bucket and survive ESC. Verified with a minimal FastMCP
656
678
  # repro side-by-side: Built-in dies, Local survives, same SDK code.
657
679
  mode = resolve_permission_mode(permission_override)
680
+ print(f"[meshcode] Editor resolved: {editor}", file=sys.stderr)
681
+ print(f"[meshcode] Permission mode resolved: {mode}", file=sys.stderr)
658
682
  cmd = [editor]
659
683
  if mode == "bypass":
660
684
  if _claude_supports_bypass(editor):
@@ -47,6 +47,33 @@ DEFAULT_PROFILE = "default"
47
47
  LEGACY_CREDS_FILE = Path.home() / ".meshcode" / "credentials.json"
48
48
 
49
49
 
50
+ # ============================================================
51
+ # File permission helpers
52
+ # ============================================================
53
+
54
+
55
+ def _restrict_perms(path: "Path") -> None:
56
+ """Restrict file to owner-only access on all platforms.
57
+
58
+ On Windows, os.chmod(0o600) is a no-op — must use icacls to remove
59
+ inherited ACLs and grant only the current user Full Control.
60
+ """
61
+ try:
62
+ if sys.platform == "win32":
63
+ import subprocess
64
+ username = os.environ.get("USERNAME", os.environ.get("USER", ""))
65
+ if username:
66
+ subprocess.run(
67
+ ["icacls", str(path), "/inheritance:r",
68
+ "/grant:r", f"{username}:(F)"],
69
+ capture_output=True, timeout=5,
70
+ )
71
+ else:
72
+ os.chmod(path, 0o600)
73
+ except Exception:
74
+ pass
75
+
76
+
50
77
  # ============================================================
51
78
  # Keyring backend probe
52
79
  # ============================================================
@@ -129,10 +156,7 @@ def _save_index(idx: Dict[str, Any]) -> None:
129
156
  PROFILE_INDEX.parent.mkdir(parents=True, exist_ok=True)
130
157
  tmp = PROFILE_INDEX.with_suffix(".tmp")
131
158
  tmp.write_text(json.dumps(idx, indent=2), encoding="utf-8")
132
- try:
133
- os.chmod(tmp, 0o600)
134
- except Exception:
135
- pass
159
+ _restrict_perms(tmp)
136
160
  tmp.replace(PROFILE_INDEX)
137
161
 
138
162
 
@@ -182,10 +206,7 @@ def set_api_key(api_key: str, profile: str = DEFAULT_PROFILE,
182
206
  fallback_path = Path.home() / ".meshcode" / f"credentials.{profile}.json"
183
207
  fallback_path.parent.mkdir(parents=True, exist_ok=True)
184
208
  fallback_path.write_text(json.dumps({"api_key": api_key, **(meta or {})}, indent=2), encoding="utf-8")
185
- try:
186
- os.chmod(fallback_path, 0o600)
187
- except Exception:
188
- pass
209
+ _restrict_perms(fallback_path)
189
210
  _index_set(profile, {"backend": "file-fallback", **(meta or {})})
190
211
  return True
191
212
 
@@ -204,11 +204,31 @@ def _build_server_block(project: str, project_id: str, agent: str, role: str,
204
204
  }
205
205
 
206
206
 
207
+ def _restrict_perms(path: Path) -> None:
208
+ """Restrict file to owner-only access (Windows-aware)."""
209
+ try:
210
+ if sys.platform == "win32":
211
+ import subprocess
212
+ username = os.environ.get("USERNAME", os.environ.get("USER", ""))
213
+ if username:
214
+ subprocess.run(
215
+ ["icacls", str(path), "/inheritance:r",
216
+ "/grant:r", f"{username}:(F)"],
217
+ capture_output=True, timeout=5,
218
+ )
219
+ else:
220
+ os.chmod(path, 0o600)
221
+ except Exception:
222
+ pass
223
+
224
+
207
225
  def _atomic_write_json(path: Path, data: dict) -> None:
208
226
  path.parent.mkdir(parents=True, exist_ok=True)
209
227
  tmp = path.with_suffix(path.suffix + ".tmp")
210
228
  tmp.write_text(json.dumps(data, indent=2), encoding="utf-8")
229
+ _restrict_perms(tmp)
211
230
  tmp.replace(path)
231
+ _restrict_perms(path)
212
232
 
213
233
 
214
234
  def _toml_escape(s: str) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.10.42
3
+ Version: 2.10.43
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.10.42"
7
+ version = "2.10.43"
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