meshcode 2.11.67__tar.gz → 2.11.69__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.11.67 → meshcode-2.11.69}/PKG-INFO +1 -1
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/__init__.py +1 -1
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/comms_v4.py +54 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/hostd.py +53 -8
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.11.67 → meshcode-2.11.69}/pyproject.toml +1 -1
- {meshcode-2.11.67 → meshcode-2.11.69}/README.md +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/__main__.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/_stop_hook_template.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/atomic_push.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/claude_update.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/cli.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/compat.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/daemon.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/date_parse.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/doctor.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/error_hints.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/exceptions.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/invites.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/launcher.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/preferences.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/protocol_handler.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/quickstart.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/rpc_allowlist.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/run_agent.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/scripts/check_secrets.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/scripts/race_rate_harness.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/secrets.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/self_update.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/supervisor.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/up.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode/upload.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/setup.cfg +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_auto_update_hardening.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_autonomous_closegap_1.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_autonomous_closegap_2.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_autonomous_closegap_3.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_autonomous_prompt_inject.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_boot_bug_regression.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_color_truecolor.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_core.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_date_parse.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_doctor.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_epistemic_v1_python_sdk.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_epistemic_v1_stop_conditions.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_exceptions.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_file_upload.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_init_device_code.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_install_guard.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_lease_sigterm_release.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_marketplace_ratings.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_rpc_grants.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_run_agent_dry_run.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_run_agent_no_server_import.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_self_update_user_site.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_sentinel.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_setup_path.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_sleep_signals.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_stay_on_loop_hook.py +0 -0
- {meshcode-2.11.67 → meshcode-2.11.69}/tests/test_wait_open_tasks_contradiction.py +0 -0
|
@@ -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
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
@@ -377,8 +413,17 @@ def cmd_hostd(args: list) -> int:
|
|
|
377
413
|
|
|
378
414
|
if sub == "run":
|
|
379
415
|
if not api_key:
|
|
380
|
-
_log("FATAL: no api key —
|
|
416
|
+
_log("FATAL: no api key — run `meshcode login` (key is read from the keychain)")
|
|
381
417
|
return 1
|
|
418
|
+
# Register this host in mc_host_config so the dashboard can list it as a
|
|
419
|
+
# launch target (Path2 canonical: dashboard -> mc_host_set_agents ->
|
|
420
|
+
# mc_agents.desired_state='running' -> this daemon polls + spawns).
|
|
421
|
+
# mc_host_config_set upserts (host_id, owner_user_id) idempotently.
|
|
422
|
+
_reg = _rpc("mc_host_config_set", {"p_api_key": api_key, "p_host_id": host_id})
|
|
423
|
+
if _reg and _reg.get("ok"):
|
|
424
|
+
_log(f"registered host {host_id} in mc_host_config")
|
|
425
|
+
else:
|
|
426
|
+
_log(f"WARN: host registration failed (dashboard may not list this host): {_reg}")
|
|
382
427
|
_log(f"hostd starting — host_id={host_id} interval={POLL_INTERVAL_SEC}s stale={STALE_SECONDS}s")
|
|
383
428
|
while True:
|
|
384
429
|
try:
|
|
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
|
|
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
|