meshcode 2.11.67__tar.gz → 2.11.68__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 (90) hide show
  1. {meshcode-2.11.67 → meshcode-2.11.68}/PKG-INFO +1 -1
  2. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/comms_v4.py +54 -0
  4. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/hostd.py +43 -7
  5. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-2.11.67 → meshcode-2.11.68}/pyproject.toml +1 -1
  7. {meshcode-2.11.67 → meshcode-2.11.68}/README.md +0 -0
  8. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/__main__.py +0 -0
  9. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/_stop_hook_template.py +0 -0
  10. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/ascii_art.py +0 -0
  11. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/atomic_push.py +0 -0
  12. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/claude_update.py +0 -0
  13. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/cli.py +0 -0
  14. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/compat.py +0 -0
  15. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/daemon.py +0 -0
  16. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/date_parse.py +0 -0
  17. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/doctor.py +0 -0
  18. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/error_hints.py +0 -0
  19. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/exceptions.py +0 -0
  20. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/invites.py +0 -0
  21. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/launcher.py +0 -0
  22. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/launcher_install.py +0 -0
  23. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/__init__.py +0 -0
  24. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/__main__.py +0 -0
  25. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/backend.py +0 -0
  26. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/realtime.py +0 -0
  27. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/server.py +0 -0
  28. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
  29. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/test_backend.py +0 -0
  30. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  31. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  32. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  33. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  34. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  35. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/preferences.py +0 -0
  36. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/protocol_handler.py +0 -0
  37. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/protocol_v2.py +0 -0
  38. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/quickstart.py +0 -0
  39. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/rpc_allowlist.py +0 -0
  40. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/run_agent.py +0 -0
  41. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/scripts/check_secrets.py +0 -0
  42. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/scripts/race_rate_harness.py +0 -0
  43. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/secrets.py +0 -0
  44. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/self_update.py +0 -0
  45. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/setup_clients.py +0 -0
  46. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/supervisor.py +0 -0
  47. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/up.py +0 -0
  48. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode/upload.py +0 -0
  49. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode.egg-info/SOURCES.txt +0 -0
  50. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode.egg-info/dependency_links.txt +0 -0
  51. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode.egg-info/entry_points.txt +0 -0
  52. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode.egg-info/requires.txt +0 -0
  53. {meshcode-2.11.67 → meshcode-2.11.68}/meshcode.egg-info/top_level.txt +0 -0
  54. {meshcode-2.11.67 → meshcode-2.11.68}/setup.cfg +0 -0
  55. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_auto_update_hardening.py +0 -0
  56. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_autonomous_closegap_1.py +0 -0
  57. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_autonomous_closegap_2.py +0 -0
  58. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_autonomous_closegap_3.py +0 -0
  59. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_autonomous_prompt_inject.py +0 -0
  60. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_boot_bug_regression.py +0 -0
  61. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_color_truecolor.py +0 -0
  62. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_core.py +0 -0
  63. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_cross_agent_messaging.py +0 -0
  64. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_date_parse.py +0 -0
  65. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_doctor.py +0 -0
  66. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_epistemic_v1_python_sdk.py +0 -0
  67. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  68. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_esc_deaf_state.py +0 -0
  69. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_exceptions.py +0 -0
  70. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_file_upload.py +0 -0
  71. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_init_device_code.py +0 -0
  72. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_install_guard.py +0 -0
  73. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_lease_sigterm_release.py +0 -0
  74. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_mark_read_batch.py +0 -0
  75. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_marketplace_ratings.py +0 -0
  76. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_migration_integrity.py +0 -0
  77. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_realtime_event_freshness.py +0 -0
  78. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_rls_cross_tenant.py +0 -0
  79. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_rpc_grants.py +0 -0
  80. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_rpc_migrations.py +0 -0
  81. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_run_agent_dry_run.py +0 -0
  82. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_run_agent_no_server_import.py +0 -0
  83. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_security_regressions.py +0 -0
  84. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_self_update_user_site.py +0 -0
  85. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_sentinel.py +0 -0
  86. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_setup_path.py +0 -0
  87. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_sleep_signals.py +0 -0
  88. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_status_enum_coverage.py +0 -0
  89. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_stay_on_loop_hook.py +0 -0
  90. {meshcode-2.11.67 → meshcode-2.11.68}/tests/test_wait_open_tasks_contradiction.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.67
3
+ Version: 2.11.68
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,5 +1,5 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "2.11.67"
2
+ __version__ = "2.11.68"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -266,6 +266,29 @@ def _load_api_key_for_cli() -> str:
266
266
  return ""
267
267
 
268
268
 
269
+ def _ensure_protocol_registered():
270
+ """Best-effort, run-once registration of the meshcode:// URL scheme.
271
+
272
+ The dashboard 'Launch' button opens a meshcode:// deep-link; without a
273
+ registered OS handler the click silently no-ops (the bug Samuel hit).
274
+ cmd_install_protocol() is idempotent + platform-dispatched, but nothing
275
+ used to call it. Invoke once on first-run paths (init / go / setup / up),
276
+ guarded by a marker so we never re-run lsregister on every invocation.
277
+ Never raises — registration failure must not block the actual command.
278
+ """
279
+ try:
280
+ marker = COMMS_DIR / ".protocol_installed"
281
+ if marker.exists():
282
+ return
283
+ import importlib
284
+ _ph = importlib.import_module("meshcode.protocol_handler")
285
+ _ph.cmd_install_protocol()
286
+ COMMS_DIR.mkdir(parents=True, exist_ok=True)
287
+ marker.write_text("1", encoding="utf-8")
288
+ except Exception:
289
+ pass
290
+
291
+
269
292
  def get_project_id(project_name):
270
293
  """Resolve a project's UUID for the authenticated CLI user.
271
294
 
@@ -2833,6 +2856,9 @@ if __name__ == "__main__":
2833
2856
  # Guided onboarding: try zero-config pairing first, then manual fallback
2834
2857
  import webbrowser
2835
2858
  import time as _t
2859
+ # Register meshcode:// early in onboarding (run-once) so the dashboard
2860
+ # Launch deep-link works for this user.
2861
+ _ensure_protocol_registered()
2836
2862
  print()
2837
2863
  print(" Welcome to MeshCode!")
2838
2864
  print(" " + "=" * 40)
@@ -3003,6 +3029,12 @@ if __name__ == "__main__":
3003
3029
  print("[meshcode] Get your API key at: https://meshcode.io/settings")
3004
3030
  sys.exit(1)
3005
3031
 
3032
+ # First-run paths where the user is about to launch/manage agents: ensure
3033
+ # the meshcode:// scheme is registered (run-once) so the dashboard Launch
3034
+ # deep-link dispatches to this machine.
3035
+ if cmd in ("go", "run", "setup", "add-agent", "add_agent", "up", "quickstart"):
3036
+ _ensure_protocol_registered()
3037
+
3006
3038
  if cmd == "register":
3007
3039
  # Backwards-compat alias for `connect`. Mesh-only model: bind THIS
3008
3040
  # terminal to (project, agent) — never spawns claude.
@@ -3852,6 +3884,27 @@ if __name__ == "__main__":
3852
3884
  _up = importlib.import_module("meshcode.up")
3853
3885
  sys.exit(_up.cmd_up(pos))
3854
3886
 
3887
+ elif cmd == "install-protocol":
3888
+ # Register the meshcode:// URL scheme so the dashboard 'Launch' deep-link
3889
+ # spawns agents. LAUNCH-1 RC: protocol_handler defined this but it was
3890
+ # never wired to the dispatcher -> `meshcode install-protocol` = Unknown command.
3891
+ import importlib
3892
+ _ph = importlib.import_module("meshcode.protocol_handler")
3893
+ sys.exit(_ph.cmd_install_protocol())
3894
+
3895
+ elif cmd == "launch-url":
3896
+ # meshcode launch-url "meshcode://launch?agents=..&token=..&user=.." —
3897
+ # invoked by the OS scheme handler (.app/.desktop) the line above registers.
3898
+ import importlib
3899
+ _ph = importlib.import_module("meshcode.protocol_handler")
3900
+ sys.exit(_ph.cmd_launch_url(pos[0] if pos else ""))
3901
+
3902
+ elif cmd == "launch-batch":
3903
+ # meshcode launch-batch <agent> [<agent>...] — open a visible terminal per agent.
3904
+ import importlib
3905
+ _ph = importlib.import_module("meshcode.protocol_handler")
3906
+ sys.exit(_ph.cmd_launch_batch(pos))
3907
+
3855
3908
  else:
3856
3909
  known_cmds = [
3857
3910
  "register", "send", "broadcast", "read", "check", "watch", "inbox", "next",
@@ -3861,6 +3914,7 @@ if __name__ == "__main__":
3861
3914
  "revoke-invite", "revoke-member", "login", "prefs", "launcher",
3862
3915
  "help", "init", "doctor", "compat", "upgrade", "profile", "validate-sessions", "wake-headless",
3863
3916
  "supervisor", "hostd", "up", "upload", "quickstart", "patch-hooks", "wake-all",
3917
+ "install-protocol", "launch-url", "launch-batch",
3864
3918
  ]
3865
3919
  # Simple fuzzy: prefix match + Levenshtein-like best match
3866
3920
  suggestions = [c for c in known_cmds if c.startswith(cmd)]
@@ -87,25 +87,61 @@ def get_host_id() -> str:
87
87
  # ------------------------------------------------------------------
88
88
 
89
89
  def _supabase_cfg() -> tuple:
90
+ # env first (explicit override), then fall back to the SAME resolution the
91
+ # rest of the CLI uses (comms_v4: env -> ~/.meshcode/env -> baked publishable
92
+ # default). hostd runs under launchd with NO env, so the old env-only lookup
93
+ # left it unable to reach the cloud at all ("SUPABASE_URL/KEY not set").
90
94
  url = os.environ.get("SUPABASE_URL", "").rstrip("/")
91
95
  key = os.environ.get("SUPABASE_KEY") or os.environ.get("MESHCODE_SUPABASE_ANON_KEY") or ""
92
- return url, key
96
+ if url and key:
97
+ return url, key
98
+ try:
99
+ from meshcode import comms_v4 as _c # type: ignore
100
+ return (url or _c.SUPABASE_URL).rstrip("/"), (key or _c.SUPABASE_KEY)
101
+ except Exception:
102
+ return url, key
93
103
 
94
104
 
95
105
  def _api_key() -> Optional[str]:
96
- """The meshcode agent/user api key, used as p_api_key in mc_ RPCs."""
106
+ """The meshcode agent/user api key, used as p_api_key in mc_ RPCs.
107
+
108
+ Resolution mirrors comms_v4._load_api_key_for_cli so hostd authenticates
109
+ the same way `meshcode run` does. CRITICAL: launchd starts hostd with NO
110
+ environment, so the key MUST be resolvable from the keychain (where
111
+ `meshcode login` stores it). The old code only checked env + a
112
+ wrong-shaped profiles.json, so launchd-started hostd always FATAL'd
113
+ ("no api key") and never registered -> dashboard launch had no host.
114
+ """
115
+ # 1) explicit env override
97
116
  k = os.environ.get("MESHCODE_API_KEY")
98
117
  if k:
99
118
  return k
119
+ # 2) keychain — the actual store `meshcode login` writes to. Same source
120
+ # comms_v4._load_api_key_for_cli() uses. This is the launchd fix.
121
+ try:
122
+ from meshcode import secrets as _secrets # type: ignore
123
+ profile = os.environ.get("MESHCODE_KEYCHAIN_PROFILE") or _secrets.DEFAULT_PROFILE
124
+ key = _secrets.get_api_key(profile=profile) or ""
125
+ if key:
126
+ return key
127
+ except Exception:
128
+ pass
129
+ # 3) legacy per-profile state file fallback. Handles BOTH the flat
130
+ # {name: {api_key}} and nested {"profiles": {name: {api_key}}} shapes
131
+ # (the flat-only scan silently missed the nested writer).
100
132
  try:
101
- # same source meshcode run uses (per-profile state)
102
133
  prof = STATE_DIR / "profiles.json"
103
134
  if prof.exists():
104
135
  data = json.loads(prof.read_text(encoding="utf-8"))
105
- # best-effort: first profile with an api_key
106
- for v in (data.values() if isinstance(data, dict) else []):
107
- if isinstance(v, dict) and v.get("api_key"):
108
- return v["api_key"]
136
+ roots = []
137
+ if isinstance(data, dict):
138
+ roots.append(data)
139
+ if isinstance(data.get("profiles"), dict):
140
+ roots.append(data["profiles"])
141
+ for root in roots:
142
+ for v in root.values():
143
+ if isinstance(v, dict) and v.get("api_key"):
144
+ return v["api_key"]
109
145
  except Exception:
110
146
  pass
111
147
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.67
3
+ Version: 2.11.68
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.11.67"
7
+ version = "2.11.68"
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