meshcode 2.11.88__tar.gz → 2.11.91__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.88 → meshcode-2.11.91}/PKG-INFO +1 -1
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/__init__.py +1 -1
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/hostd.py +101 -10
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.11.88 → meshcode-2.11.91}/pyproject.toml +1 -1
- {meshcode-2.11.88 → meshcode-2.11.91}/README.md +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/__main__.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/_session_handoff_template.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/_stop_hook_template.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/atomic_push.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/claude_update.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/cli.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/compat.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/daemon.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/date_parse.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/doctor.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/error_hints.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/exceptions.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/invites.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/launcher.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/preferences.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/protocol_handler.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/quickstart.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/rpc_allowlist.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/run_agent.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/scripts/check_secrets.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/scripts/race_rate_harness.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/secrets.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/self_update.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/supervisor.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/up.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode/upload.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/setup.cfg +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_auto_update_hardening.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_autonomous_closegap_1.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_autonomous_closegap_2.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_autonomous_closegap_3.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_autonomous_prompt_inject.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_boot_bug_regression.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_color_truecolor.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_core.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_date_parse.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_doctor.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_epistemic_v1_python_sdk.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_epistemic_v1_stop_conditions.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_exceptions.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_file_upload.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_init_device_code.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_install_guard.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_lease_sigterm_release.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_marketplace_ratings.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_rpc_grants.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_run_agent_dry_run.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_run_agent_no_server_import.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_self_update_user_site.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_sentinel.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_setup_path.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_sleep_signals.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_stay_on_loop_hook.py +0 -0
- {meshcode-2.11.88 → meshcode-2.11.91}/tests/test_wait_open_tasks_contradiction.py +0 -0
|
@@ -55,6 +55,27 @@ try:
|
|
|
55
55
|
except Exception:
|
|
56
56
|
_RUNNING_VERSION = "0.0.0"
|
|
57
57
|
_LAST_UPDATE_KICK_MONO = 0.0
|
|
58
|
+
_REEXEC_GUARD_LOGGED = False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _has_supervisor() -> bool:
|
|
62
|
+
"""True if an OS supervisor (launchd/systemd/Task Scheduler) manages this hostd and will
|
|
63
|
+
relaunch it on clean exit. Field data (Samuel Mac): os.execv is BLOCKED in the sandboxed
|
|
64
|
+
runtime, so where a supervisor exists we PREFER exit-and-relaunch over execv."""
|
|
65
|
+
import platform
|
|
66
|
+
try:
|
|
67
|
+
s = platform.system()
|
|
68
|
+
if s == "Darwin":
|
|
69
|
+
return _hostd_plist_path().exists()
|
|
70
|
+
if s == "Windows":
|
|
71
|
+
return subprocess.run(["schtasks", "/Query", "/TN", _HOSTD_TASK_NAME],
|
|
72
|
+
capture_output=True, text=True).returncode == 0
|
|
73
|
+
if shutil.which("systemctl"):
|
|
74
|
+
return subprocess.run(["systemctl", "--user", "is-enabled", _HOSTD_SYSTEMD_UNIT],
|
|
75
|
+
capture_output=True, text=True).returncode == 0
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
78
|
+
return False
|
|
58
79
|
|
|
59
80
|
|
|
60
81
|
def _maybe_self_restart_on_version_drift() -> None:
|
|
@@ -96,13 +117,42 @@ def _maybe_self_restart_on_version_drift() -> None:
|
|
|
96
117
|
newer = False
|
|
97
118
|
if not newer:
|
|
98
119
|
return
|
|
99
|
-
|
|
100
|
-
|
|
120
|
+
# Loop-guard (backend2 finding): in a source/dev run, importlib.metadata can report a
|
|
121
|
+
# pip-installed wheel NEWER than the __init__.py actually executing, so the drift would
|
|
122
|
+
# PERSIST across restart -> storm. Persist the attempt; if we already tried to reach this
|
|
123
|
+
# exact on-disk target recently and didn't advance, skip until the guard window passes.
|
|
124
|
+
global _REEXEC_GUARD_LOGGED
|
|
125
|
+
try:
|
|
126
|
+
_st = _load_state()
|
|
127
|
+
except Exception:
|
|
128
|
+
_st = {}
|
|
129
|
+
_att = _st.get("reexec_attempt") or {}
|
|
130
|
+
if _att.get("target") == ondisk and (time.time() - float(_att.get("at", 0) or 0)) < 600:
|
|
131
|
+
if not _REEXEC_GUARD_LOGGED:
|
|
132
|
+
_REEXEC_GUARD_LOGGED = True
|
|
133
|
+
_log(f"version drift {_RUNNING_VERSION}->{ondisk} but a recent restart didn't advance the "
|
|
134
|
+
f"running version (source/dev run?). Skipping to avoid a restart storm; retry after 600s.")
|
|
135
|
+
return
|
|
136
|
+
_st["reexec_attempt"] = {"target": ondisk, "at": time.time()}
|
|
137
|
+
try:
|
|
138
|
+
_save_state(_st)
|
|
139
|
+
except Exception:
|
|
140
|
+
pass
|
|
141
|
+
_log(f"VERSION DRIFT: running {_RUNNING_VERSION}, on-disk {ondisk} -> restart to load new code "
|
|
142
|
+
f"(Stop kill sweep + daemon fixes). headless_pids persisted.")
|
|
143
|
+
# Prefer supervisor-restart where one manages hostd: field data (Samuel Mac) showed os.execv
|
|
144
|
+
# is BLOCKED in the sandboxed runtime and the os._exit->KeepAlive fallback is what actually
|
|
145
|
+
# restarts. So when a supervisor exists, exit cleanly and let it relaunch on the new wheel.
|
|
146
|
+
if _has_supervisor():
|
|
147
|
+
_log("supervisor present -> clean exit; launchd/systemd/schtasks relaunches on new code")
|
|
148
|
+
os._exit(0)
|
|
149
|
+
# No supervisor (e.g. dev foreground run): execv is the only in-place restart. If it's blocked,
|
|
150
|
+
# do NOT self-destruct (nothing would relaunch us) — stay on old code, retry after the guard window.
|
|
101
151
|
try:
|
|
102
152
|
os.execv(sys.executable, [sys.executable, "-m", "meshcode"] + sys.argv[1:])
|
|
103
153
|
except Exception as e:
|
|
104
|
-
_log(f"WARN:
|
|
105
|
-
|
|
154
|
+
_log(f"WARN: no supervisor + execv failed ({e}); staying on {_RUNNING_VERSION}, retry after guard window")
|
|
155
|
+
return
|
|
106
156
|
|
|
107
157
|
STATE_DIR = Path.home() / ".meshcode"
|
|
108
158
|
HOST_ID_PATH = STATE_DIR / "host_id"
|
|
@@ -881,28 +931,58 @@ def _hostd_bootstrap_if_needed(ttl_sec: int = 3600, verbose: bool = False) -> bo
|
|
|
881
931
|
return ok
|
|
882
932
|
|
|
883
933
|
|
|
934
|
+
def _running_daemon_is_stale() -> bool:
|
|
935
|
+
"""ca947d7f: True ONLY if hostd_state.json records a running_version that is STRICTLY older
|
|
936
|
+
than the on-disk installed version. Absent / unknown / equal / newer -> False (FAIL-SAFE:
|
|
937
|
+
never bounce a healthy or ambiguous daemon on the login-critical path — commander's safety
|
|
938
|
+
condition). NOTE: a daemon predating the running_version stamp (<2.11.90) wrote no marker, so
|
|
939
|
+
this returns False for it — that one-time transition still needs a manual restart; this guards
|
|
940
|
+
FUTURE transitions as defense-in-depth (the daemon's own self-reexec is the primary path)."""
|
|
941
|
+
try:
|
|
942
|
+
rv = (_load_state() or {}).get("running_version")
|
|
943
|
+
if not rv:
|
|
944
|
+
return False
|
|
945
|
+
import importlib.metadata as _ilmd
|
|
946
|
+
ondisk = _ilmd.version("meshcode")
|
|
947
|
+
from meshcode import self_update as _su
|
|
948
|
+
return bool(_su._is_newer(ondisk, rv))
|
|
949
|
+
except Exception:
|
|
950
|
+
return False
|
|
951
|
+
|
|
952
|
+
|
|
884
953
|
def _hostd_bootstrap(verbose: bool = False) -> bool:
|
|
885
954
|
"""Idempotent auto-install + start of hostd (task f12b9d36) — called from `meshcode login`
|
|
886
955
|
so onboarding is just `pip install meshcode` + `meshcode login` (no manual `hostd install`).
|
|
887
|
-
If the OS job already exists, just ensure it's running (don't bounce a live daemon)
|
|
888
|
-
|
|
956
|
+
If the OS job already exists, just ensure it's running (don't bounce a live daemon) UNLESS the
|
|
957
|
+
running daemon is provably STALE (ca947d7f), in which case force-restart it so Stop's kill
|
|
958
|
+
sweep + launch fixes land. Best-effort: NEVER raises — login must not fail on a hiccup."""
|
|
889
959
|
import platform
|
|
890
960
|
try:
|
|
961
|
+
force = _running_daemon_is_stale() # bounce ONLY a provably-stale daemon
|
|
962
|
+
if force:
|
|
963
|
+
_log(f"hostd bootstrap: running daemon is STALE vs on-disk -> force-restart")
|
|
891
964
|
sysname = platform.system()
|
|
892
965
|
if sysname == "Darwin":
|
|
893
966
|
if not _hostd_plist_path().exists():
|
|
894
967
|
_hostd_install() # writes plist + launchctl load
|
|
895
968
|
try:
|
|
896
969
|
uid = str(os.getuid())
|
|
897
|
-
|
|
898
|
-
|
|
970
|
+
# `kickstart -k` restarts a running job; plain `kickstart` only starts if down.
|
|
971
|
+
args = ["launchctl", "kickstart", f"gui/{uid}/{_HOSTD_PLIST_LABEL}"]
|
|
972
|
+
if force:
|
|
973
|
+
args.insert(2, "-k")
|
|
974
|
+
subprocess.run(args, capture_output=True, text=True)
|
|
899
975
|
except Exception:
|
|
900
976
|
pass
|
|
901
977
|
elif sysname == "Windows":
|
|
902
978
|
q = subprocess.run(["schtasks", "/Query", "/TN", _HOSTD_TASK_NAME],
|
|
903
979
|
capture_output=True, text=True)
|
|
904
980
|
if q.returncode == 0:
|
|
905
|
-
|
|
981
|
+
if force:
|
|
982
|
+
# stale -> stop the running instance, then start fresh on the new wheel.
|
|
983
|
+
subprocess.run(["schtasks", "/End", "/TN", _HOSTD_TASK_NAME],
|
|
984
|
+
capture_output=True, text=True)
|
|
985
|
+
# task exists -> ensure running (IgnoreNew => harmless if already up; no double-spawn)
|
|
906
986
|
subprocess.run(["schtasks", "/Run", "/TN", _HOSTD_TASK_NAME],
|
|
907
987
|
capture_output=True, text=True)
|
|
908
988
|
else:
|
|
@@ -910,6 +990,10 @@ def _hostd_bootstrap(verbose: bool = False) -> bool:
|
|
|
910
990
|
_hostd_install_windows()
|
|
911
991
|
else: # Linux + others
|
|
912
992
|
if shutil.which("systemctl"):
|
|
993
|
+
if force:
|
|
994
|
+
subprocess.run(["systemctl", "--user", "restart", _HOSTD_SYSTEMD_UNIT],
|
|
995
|
+
capture_output=True, text=True)
|
|
996
|
+
return True
|
|
913
997
|
act = subprocess.run(["systemctl", "--user", "is-active", _HOSTD_SYSTEMD_UNIT],
|
|
914
998
|
capture_output=True, text=True)
|
|
915
999
|
if act.stdout.strip() == "active":
|
|
@@ -1024,7 +1108,14 @@ def cmd_hostd(args: list) -> int:
|
|
|
1024
1108
|
_log(f"registered host {host_id} in mc_host_config")
|
|
1025
1109
|
else:
|
|
1026
1110
|
_log(f"WARN: host registration failed (dashboard may not list this host): {_reg}")
|
|
1027
|
-
_log(f"hostd starting — host_id={host_id} interval={POLL_INTERVAL_SEC}s stale={STALE_SECONDS}s")
|
|
1111
|
+
_log(f"hostd starting — host_id={host_id} interval={POLL_INTERVAL_SEC}s stale={STALE_SECONDS}s v{_RUNNING_VERSION}")
|
|
1112
|
+
# ca947d7f: stamp the version THIS daemon is running into hostd_state.json so
|
|
1113
|
+
# `meshcode login` (_hostd_bootstrap) can detect a STALE running daemon and force-
|
|
1114
|
+
# restart it (the one-time transition onto a self-reexec-capable build). Best-effort.
|
|
1115
|
+
try:
|
|
1116
|
+
_st = _load_state(); _st["running_version"] = _RUNNING_VERSION; _save_state(_st)
|
|
1117
|
+
except Exception:
|
|
1118
|
+
pass
|
|
1028
1119
|
# uptime-since-spawn (core's suggestion): if the daemon dies <2min, the last alive log +
|
|
1029
1120
|
# the uptime stamped on the FATAL line reveal the <2min pattern for RC.
|
|
1030
1121
|
_spawn_mono = time.monotonic()
|
|
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
|
|
File without changes
|
|
File without changes
|