meshcode 2.11.158__tar.gz → 2.11.160__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.158 → meshcode-2.11.160}/PKG-INFO +1 -1
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/__init__.py +1 -1
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/__main__.py +1 -1
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/_session_handoff_template.py +16 -70
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/comms_v4.py +39 -23
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/hostd.py +275 -418
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/backend.py +2 -2
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/realtime.py +0 -11
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/server.py +27 -316
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/sleep_signals.py +1 -45
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/protocol_handler.py +7 -41
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/run_agent.py +11 -29
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/self_update.py +38 -3
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/setup_clients.py +4 -69
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/up.py +4 -31
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode.egg-info/SOURCES.txt +0 -5
- {meshcode-2.11.158 → meshcode-2.11.160}/pyproject.toml +1 -1
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_fleet_reaper.py +1 -1
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_hostd_zombie_sessions.py +2 -2
- meshcode-2.11.158/meshcode/helper_visuals.py +0 -142
- meshcode-2.11.158/meshcode/meshcode_mcp/swarm.py +0 -472
- meshcode-2.11.158/meshcode/meshcode_mcp/test_swarm.py +0 -469
- meshcode-2.11.158/tests/test_helper_visuals.py +0 -214
- meshcode-2.11.158/tests/test_swarm_events.py +0 -115
- {meshcode-2.11.158 → meshcode-2.11.160}/README.md +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/_launch_smoke.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/_stop_hook_template.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/_update_guard.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/atomic_push.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/claude_update.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/cli.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/compat.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/daemon.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/date_parse.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/doctor.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/error_hints.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/exceptions.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/hooks/__init__.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/hooks/push_guard.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/hooks/repo_path_lock.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/invites.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/launcher.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/preferences.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/quickstart.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/rpc_allowlist.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/scripts/check_secrets.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/scripts/race_rate_harness.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/secrets.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/supervisor.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode/upload.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/setup.cfg +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_auto_update_hardening.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_autonomous_closegap_1.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_autonomous_closegap_2.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_autonomous_closegap_3.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_autonomous_prompt_inject.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_boot_bug_regression.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_color_truecolor.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_core.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_date_parse.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_doctor.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_ensure_boot_env_urgent_wake.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_epistemic_v1_python_sdk.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_epistemic_v1_stop_conditions.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_exceptions.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_file_upload.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_hostd_launch_pinned_env.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_hostd_serve_discovery_split.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_init_device_code.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_install_guard.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_launch_smoke.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_lease_sigterm_release.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_live_mesh_guard.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_marketplace_ratings.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_no_appleevents_on_sweep.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_preflight_hb_gate.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_pretrust_claude.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_push_guard.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_replica_base_workspace_fallback.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_replica_boot_protocol_unconditional.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_rm_guard.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_rpc_grants.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_run_agent_dry_run.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_run_agent_no_server_import.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_self_update_user_site.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_sentinel.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_session_replay_gate.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_setup_path.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_sleep_signals.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_stay_on_loop_hook.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_stop_ghost_terminal.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_task_progress.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_terminal_lifecycle.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_up_launch_cmd.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_update_guard.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_urgent_wake_tmux.py +0 -0
- {meshcode-2.11.158 → meshcode-2.11.160}/tests/test_wait_open_tasks_contradiction.py +0 -0
|
@@ -13,7 +13,7 @@ def main():
|
|
|
13
13
|
# (comms_v4), not just setup-path. hostd spawns agents via `python -m meshcode run <target>`
|
|
14
14
|
# (so a bg pip can replace the .exe) — that MUST route to the `run` dispatch, NOT fall through
|
|
15
15
|
# to the PATH-help error. Previously only setup-path dispatched, so every `python -m meshcode
|
|
16
|
-
# run` died with the misleading "meshcode not on PATH" message (qa/
|
|
16
|
+
# run` died with the misleading "meshcode not on PATH" message (qa/respawn boots).
|
|
17
17
|
if len(sys.argv) > 1:
|
|
18
18
|
# runpy imports + executes comms_v4 as __main__; do NOT pre-import it here (a prior
|
|
19
19
|
# `import meshcode.comms_v4` triggers a runpy "found in sys.modules" double-exec warning).
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""Session-handoff hook templates — single source of truth for the
|
|
2
2
|
PreCompact (write) + SessionStart (read) hooks.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
SESSION HANDOFF (task bcd157a9, backend2): when a session ends or compacts at
|
|
5
|
+
high context — either by Claude Code's in-place autocompact or by a manual
|
|
6
|
+
relaunch — the IN-FLIGHT working context (which task I was mid-executing, what
|
|
7
|
+
I just edited/decided this session) is lost. meshcode_boot restores mesh state
|
|
8
|
+
(tasks, inbox, memory) authoritatively, but NOT the conversational thread.
|
|
9
|
+
These two hooks bridge that gap:
|
|
10
10
|
|
|
11
11
|
* PreCompact -> session_handoff_write.py: dump a compact snapshot of the
|
|
12
12
|
recent transcript tail to .claude/handoff.json BEFORE the context is
|
|
@@ -18,11 +18,8 @@ conversational thread. These two hooks bridge that gap:
|
|
|
18
18
|
|
|
19
19
|
Stored as string constants (same pattern as _stop_hook_template.py) so
|
|
20
20
|
`meshcode patch-hooks` can backfill existing workspaces without a full
|
|
21
|
-
re-scaffold.
|
|
22
|
-
|
|
23
|
-
and is intentionally NOT in these scripts — handoff preservation is decoupled
|
|
24
|
-
from whatever causes the restart, so it works for autocompact, clean recycle,
|
|
25
|
-
crash-respawn, or a manual relaunch alike.
|
|
21
|
+
re-scaffold. Handoff preservation is decoupled from whatever causes the
|
|
22
|
+
restart, so it works for autocompact, crash-respawn, or a manual relaunch alike.
|
|
26
23
|
"""
|
|
27
24
|
|
|
28
25
|
# Shared constants kept in sync between the two bodies.
|
|
@@ -33,9 +30,9 @@ _HANDOFF_MAX_AGE_S = 24 * 3600 # ignore stale handoffs on read
|
|
|
33
30
|
|
|
34
31
|
HANDOFF_WRITE_BODY = '''#!/usr/bin/env python3
|
|
35
32
|
"""PreCompact hook: snapshot the recent transcript tail to .claude/handoff.json
|
|
36
|
-
so a fresh post-
|
|
33
|
+
so a fresh post-restart session can resume the in-flight thread.
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
SESSION HANDOFF (task bcd157a9). Pure local file I/O — never touches the
|
|
39
36
|
network, never imports meshcode, never blocks. Any failure is swallowed and the
|
|
40
37
|
hook exits 0 so it can NEVER stall or break a Claude Code compaction/exit.
|
|
41
38
|
|
|
@@ -113,61 +110,13 @@ def _extract_tail(transcript_path: str):
|
|
|
113
110
|
return rows[-MAX_TURNS:]
|
|
114
111
|
|
|
115
112
|
|
|
116
|
-
def _request_recycle_if_marked(project_dir) -> None:
|
|
117
|
-
"""CTX-CLOSE-RELAUNCH (task 400fc536): commander-tier sessions ask the
|
|
118
|
-
server to recycle (close+relaunch fresh) at the next task-edge, right after
|
|
119
|
-
the handoff is snapshotted. Gated by the scaffold-baked
|
|
120
|
-
.claude/meshcode_hook_ctx.json recycle_on_compact flag (commander-only v1).
|
|
121
|
-
|
|
122
|
-
Best-effort in every dimension: missing marker/flag, missing creds, import
|
|
123
|
-
failure, network error -> silently skip. handoff.json is already written, so
|
|
124
|
-
a context-recycle still relaunches WITH context; and the actual exit is the
|
|
125
|
-
server's call (mc_consume_recycle at a task boundary), never this hook.
|
|
126
|
-
"""
|
|
127
|
-
try:
|
|
128
|
-
ctx_path = project_dir / ".claude" / "meshcode_hook_ctx.json"
|
|
129
|
-
if not ctx_path.exists():
|
|
130
|
-
return
|
|
131
|
-
if not json.loads(ctx_path.read_text(encoding="utf-8")).get("recycle_on_compact"):
|
|
132
|
-
return # non-commander -> in-place autocompact, no recycle
|
|
133
|
-
mcp = json.loads((project_dir / ".mcp.json").read_text(encoding="utf-8"))
|
|
134
|
-
env = (next(iter((mcp.get("mcpServers") or {}).values()), {}) or {}).get("env", {}) or {}
|
|
135
|
-
url = env.get("SUPABASE_URL"); key = env.get("SUPABASE_KEY")
|
|
136
|
-
pid = env.get("MESHCODE_PROJECT_ID"); agent = env.get("MESHCODE_AGENT")
|
|
137
|
-
if not (url and key and pid and agent):
|
|
138
|
-
return
|
|
139
|
-
api_key = os.environ.get("MESHCODE_API_KEY")
|
|
140
|
-
if not api_key:
|
|
141
|
-
try:
|
|
142
|
-
import importlib
|
|
143
|
-
api_key = importlib.import_module("meshcode.secrets").get_api_key(
|
|
144
|
-
profile=env.get("MESHCODE_KEYCHAIN_PROFILE") or "default")
|
|
145
|
-
except Exception:
|
|
146
|
-
api_key = None
|
|
147
|
-
if not api_key:
|
|
148
|
-
return
|
|
149
|
-
import urllib.request as _u
|
|
150
|
-
body = json.dumps({
|
|
151
|
-
"p_api_key": api_key, "p_project_id": pid,
|
|
152
|
-
"p_agent_name": agent, "p_allow_busy": True, # flag-now; exit deferred to wait-loop
|
|
153
|
-
}).encode("utf-8")
|
|
154
|
-
req = _u.Request(
|
|
155
|
-
url.rstrip("/") + "/rest/v1/rpc/mc_request_recycle",
|
|
156
|
-
data=body, method="POST",
|
|
157
|
-
headers={"apikey": key, "Authorization": "Bearer " + key,
|
|
158
|
-
"Content-Type": "application/json"})
|
|
159
|
-
_u.urlopen(req, timeout=5).read() # best-effort; ignore result per backend contract
|
|
160
|
-
except Exception as e: # noqa: BLE001 — never block compaction
|
|
161
|
-
sys.stderr.write(f"[session_handoff_write] recycle-request skipped: {e}\\n")
|
|
162
|
-
|
|
163
|
-
|
|
164
113
|
def _persist_handoff_to_memory(project_dir, handoff) -> None:
|
|
165
114
|
"""L6 M6.1 (task 84c426d4, 2.11.114): mirror the handoff into
|
|
166
115
|
mc_agent_memory key='session_handoff' so the server-side boot
|
|
167
116
|
continuity_capsule (mig 456) can surface it on the NEXT session even
|
|
168
117
|
when the local handoff.json is gone (new host, wiped workspace).
|
|
169
|
-
|
|
170
|
-
|
|
118
|
+
Best-effort creds pattern — any failure silently skips; handoff.json
|
|
119
|
+
already covers the local path.
|
|
171
120
|
"""
|
|
172
121
|
try:
|
|
173
122
|
mcp = json.loads((project_dir / ".mcp.json").read_text(encoding="utf-8"))
|
|
@@ -305,9 +254,6 @@ def main() -> int:
|
|
|
305
254
|
_persist_handoff_to_memory(_project_dir(), handoff)
|
|
306
255
|
# py-half 4aee4739: dedicated handoff capsule -> mc_boot continuity_capsule.last_handoff.
|
|
307
256
|
_send_session_handoff_capsule(_project_dir(), handoff)
|
|
308
|
-
# CTX-CLOSE-RELAUNCH (task 400fc536): now that the thread is snapshotted,
|
|
309
|
-
# commander-tier sessions ask the server to recycle at the next task-edge.
|
|
310
|
-
_request_recycle_if_marked(_project_dir())
|
|
311
257
|
return 0
|
|
312
258
|
|
|
313
259
|
|
|
@@ -321,10 +267,10 @@ if __name__ == "__main__":
|
|
|
321
267
|
|
|
322
268
|
HANDOFF_READ_BODY = '''#!/usr/bin/env python3
|
|
323
269
|
"""SessionStart hook: if a handoff snapshot exists, inject it as
|
|
324
|
-
additionalContext so a freshly-
|
|
270
|
+
additionalContext so a freshly-relaunched session resumes the in-flight thread,
|
|
325
271
|
then archive it so it is injected exactly once.
|
|
326
272
|
|
|
327
|
-
|
|
273
|
+
SESSION HANDOFF (task bcd157a9). Pure local file I/O — no network, no
|
|
328
274
|
meshcode import, never blocks. meshcode_boot remains authoritative for mesh
|
|
329
275
|
state; this only restores the conversational thread.
|
|
330
276
|
|
|
@@ -360,9 +306,9 @@ def _render(handoff: dict) -> str:
|
|
|
360
306
|
if not turns:
|
|
361
307
|
return ""
|
|
362
308
|
lines = [
|
|
363
|
-
"## Recovered session handoff
|
|
309
|
+
"## Recovered session handoff",
|
|
364
310
|
"",
|
|
365
|
-
"Your previous session
|
|
311
|
+
"Your previous session ended or compacted at high context. meshcode_boot has "
|
|
366
312
|
"the authoritative mesh state (tasks/inbox/memory); the thread tail "
|
|
367
313
|
"below is the in-flight context that boot does not restore. Use it to "
|
|
368
314
|
"resume what you were mid-doing, then run the normal boot loop.",
|
|
@@ -2509,7 +2509,7 @@ AGENT CONTROL:
|
|
|
2509
2509
|
disconnect <proj> <name> Graceful disconnect
|
|
2510
2510
|
whoami Show logged-in identity
|
|
2511
2511
|
|
|
2512
|
-
|
|
2512
|
+
REPLICAS (agent clones):
|
|
2513
2513
|
replicate <agent> --count N [--no-launch] Clone agent into <agent>-1..N (launches by default)
|
|
2514
2514
|
replica-power <group_id> running|stopped Stop/Start a whole replica group [--restart]
|
|
2515
2515
|
profile [agent] Show/set agent profile
|
|
@@ -2519,6 +2519,7 @@ DIAGNOSTICS:
|
|
|
2519
2519
|
doctor [--fix] Diagnose setup issues
|
|
2520
2520
|
compat Claude Code version compatibility
|
|
2521
2521
|
upgrade Upgrade meshcode to latest version
|
|
2522
|
+
self-upgrade Unattended deferral-safe upgrade (used by the hostd launcher)
|
|
2522
2523
|
|
|
2523
2524
|
ADMIN:
|
|
2524
2525
|
clear <proj> <name> Clear inbox
|
|
@@ -2540,16 +2541,14 @@ Run `meshcode <command> --help` for help on a specific command.
|
|
|
2540
2541
|
|
|
2541
2542
|
# Per-subcommand help texts
|
|
2542
2543
|
SUBCOMMAND_HELP = {
|
|
2543
|
-
"up": """meshcode up <project> [--agents a,b] [--
|
|
2544
|
+
"up": """meshcode up <project> [--agents a,b] [--visible]
|
|
2544
2545
|
|
|
2545
2546
|
Launch-All: opens a visible terminal for each agent in the project and
|
|
2546
2547
|
registers them as hostd-managed (so the watchdog daemon auto-relaunches
|
|
2547
|
-
|
|
2548
|
+
them if they crash). The fast way to bring a whole team online at once.
|
|
2548
2549
|
|
|
2549
2550
|
meshcode up myproject launch every agent in the roster
|
|
2550
2551
|
meshcode up myproject --agents a,b launch only a and b
|
|
2551
|
-
meshcode up myproject --recycle 80 set context-% recycle policy
|
|
2552
|
-
meshcode up myproject --recycle 12h set uptime recycle policy
|
|
2553
2552
|
|
|
2554
2553
|
Roster comes from the cloud (exact names). Pair with `meshcode hostd
|
|
2555
2554
|
install` so the launched team stays alive across crashes.
|
|
@@ -2557,18 +2556,16 @@ install` so the launched team stays alive across crashes.
|
|
|
2557
2556
|
"hostd": """meshcode hostd run|status|install|uninstall
|
|
2558
2557
|
|
|
2559
2558
|
Host-side watchdog daemon: auto-relaunches dead/stale managed agents
|
|
2560
|
-
(respawn)
|
|
2561
|
-
|
|
2562
|
-
only); hostd polls the cloud for heartbeat staleness + recycle triggers.
|
|
2559
|
+
(respawn). Complements supervisor.py (launchd KeepAlive = process-exit
|
|
2560
|
+
only); hostd polls the cloud for heartbeat staleness.
|
|
2563
2561
|
|
|
2564
2562
|
meshcode hostd run run the poll loop in the foreground
|
|
2565
2563
|
meshcode hostd status show agents this host would respawn (JSON)
|
|
2566
2564
|
meshcode hostd install install the launchd agent (auto-start, KeepAlive)
|
|
2567
2565
|
meshcode hostd uninstall remove the launchd agent
|
|
2568
2566
|
|
|
2569
|
-
Agents are managed via mc_host_set_agents (desired_state=running)
|
|
2570
|
-
|
|
2571
|
-
button (meshcode://) kickstarts it.
|
|
2567
|
+
Agents are managed via mc_host_set_agents (desired_state=running).
|
|
2568
|
+
NOT autostart-login; the Launch button (meshcode://) kickstarts it.
|
|
2572
2569
|
""",
|
|
2573
2570
|
"init": """meshcode init
|
|
2574
2571
|
|
|
@@ -3107,7 +3104,7 @@ if __name__ == "__main__":
|
|
|
3107
3104
|
|
|
3108
3105
|
# Auth guard: commands that talk to Supabase need a valid API key.
|
|
3109
3106
|
# doctor, help, version, login, prefs, launcher don't need auth.
|
|
3110
|
-
_NO_AUTH_CMDS = {"doctor", "compat", "upgrade", "help", "--help", "-h", "login",
|
|
3107
|
+
_NO_AUTH_CMDS = {"doctor", "compat", "upgrade", "self-upgrade", "help", "--help", "-h", "login",
|
|
3111
3108
|
"init", "prefs", "launcher", "--version", "-V", "version",
|
|
3112
3109
|
"whoami", "profiles", "scan", "setup-path"}
|
|
3113
3110
|
if cmd not in _NO_AUTH_CMDS:
|
|
@@ -3404,9 +3401,9 @@ if __name__ == "__main__":
|
|
|
3404
3401
|
# rows get desired_state='running' so hostd auto-spawns them as persistent
|
|
3405
3402
|
# terminals within ~one sweep; --no-launch creates them dormant (Start later
|
|
3406
3403
|
# from the dashboard or `meshcode replica-power <group> running`). The shared
|
|
3407
|
-
# replica_group_id (
|
|
3408
|
-
# while per-replica desired_state still allows killing one
|
|
3409
|
-
# Backend for task 069b5550.
|
|
3404
|
+
# replica_group_id (stored in the swarm_id column) lets the whole group be
|
|
3405
|
+
# powered as a unit while per-replica desired_state still allows killing one
|
|
3406
|
+
# without the group. Backend for task 069b5550.
|
|
3410
3407
|
_ak = _load_api_key_for_cli()
|
|
3411
3408
|
base = pos[0] if len(pos) > 0 else flags.get("agent", "")
|
|
3412
3409
|
proj = flags.get("project")
|
|
@@ -3424,9 +3421,9 @@ if __name__ == "__main__":
|
|
|
3424
3421
|
print("[meshcode] ERROR: --count must be an integer in 1..16")
|
|
3425
3422
|
sys.exit(1)
|
|
3426
3423
|
desired = None if flags.get("no-launch") else "running"
|
|
3427
|
-
# p_mode (task 986947b5): copilotos = shared
|
|
3428
|
-
# independientes = distinct
|
|
3429
|
-
# (Samuel's canonical spec). DB mig586 honors it on both overloads.
|
|
3424
|
+
# p_mode (task 986947b5): copilotos = one shared replica group (coordinate
|
|
3425
|
+
# together) | independientes = a distinct group per replica (isolated).
|
|
3426
|
+
# Default copilotos (Samuel's canonical spec). DB mig586 honors it on both overloads.
|
|
3430
3427
|
mode = str(flags.get("mode", "copilotos")).lower()
|
|
3431
3428
|
if mode not in ("copilotos", "independientes"):
|
|
3432
3429
|
print("[meshcode] ERROR: --mode must be 'copilotos' (shared/coordinate) or "
|
|
@@ -3475,9 +3472,9 @@ if __name__ == "__main__":
|
|
|
3475
3472
|
# meshcode replica-power <group_id> running|stopped [--restart]
|
|
3476
3473
|
#
|
|
3477
3474
|
# Sets desired_state for EVERY agent sharing replica_group_id (Stop/Start the
|
|
3478
|
-
# whole
|
|
3475
|
+
# whole replica group as a unit). CLI/agent path = mc_replica_group_power_as_agent
|
|
3479
3476
|
# (DB mig 20260616_573); the FE buttons call mc_replica_group_power (auth.uid,
|
|
3480
|
-
# no api_key in the browser). --restart
|
|
3477
|
+
# no api_key in the browser). --restart relaunches the group's terminals on power-on.
|
|
3481
3478
|
# Backend for task 069b5550 (FE Stop/Start/Launch route through here).
|
|
3482
3479
|
_ak = _load_api_key_for_cli()
|
|
3483
3480
|
group = pos[0] if len(pos) > 0 else flags.get("group", "")
|
|
@@ -4014,6 +4011,25 @@ if __name__ == "__main__":
|
|
|
4014
4011
|
print(f" Upgrade cancelled.")
|
|
4015
4012
|
sys.exit(0)
|
|
4016
4013
|
|
|
4014
|
+
elif cmd == "self-upgrade":
|
|
4015
|
+
# meshcode self-upgrade — UNATTENDED, deferral-safe self-upgrade for launcher
|
|
4016
|
+
# pre-steps (task d84cbfa1, version-split self-heal). Distinct from:
|
|
4017
|
+
# - `meshcode upgrade` (INTERACTIVE force escape-hatch; cancels on no-TTY and
|
|
4018
|
+
# overwrites the env even while live agents share it), and
|
|
4019
|
+
# - `meshcode update` (comms STATUS update — unrelated).
|
|
4020
|
+
# Uses the BLOCKING updater with force=False so it DEFERS when a live agent serve
|
|
4021
|
+
# shares the env (never clobbers a running MCP — the spec's live-agent deferral
|
|
4022
|
+
# constraint). Honors MESHCODE_NO_UPDATE / MESHCODE_NO_AUTO_UPDATE (the hostd
|
|
4023
|
+
# launcher clears them for this one explicit step). Never raises; always exits 0
|
|
4024
|
+
# so a failed/offline upgrade can never wedge the launcher.
|
|
4025
|
+
import importlib
|
|
4026
|
+
try:
|
|
4027
|
+
_su = importlib.import_module("meshcode.self_update")
|
|
4028
|
+
_su.check_and_maybe_update_blocking(verbose=True, force=False)
|
|
4029
|
+
except Exception as _e:
|
|
4030
|
+
print(f"[meshcode] self-upgrade skipped: {_e}", file=sys.stderr)
|
|
4031
|
+
sys.exit(0)
|
|
4032
|
+
|
|
4017
4033
|
elif cmd == "compat":
|
|
4018
4034
|
from meshcode.compat import check as cc_check, format_report, RECOMMENDED_VERSION
|
|
4019
4035
|
version, status, entry = cc_check()
|
|
@@ -4097,14 +4113,14 @@ if __name__ == "__main__":
|
|
|
4097
4113
|
print(f"Usage: meshcode supervisor [start|stop|status|--simple] <project> <agent>")
|
|
4098
4114
|
|
|
4099
4115
|
elif cmd == "hostd":
|
|
4100
|
-
# meshcode hostd run|status — local respawn
|
|
4116
|
+
# meshcode hostd run|status — local respawn watchdog daemon
|
|
4101
4117
|
# (auto-relaunch of stale/dead managed agents; mesh-core incident fix).
|
|
4102
4118
|
import importlib
|
|
4103
4119
|
_hostd = importlib.import_module("meshcode.hostd")
|
|
4104
4120
|
sys.exit(_hostd.cmd_hostd(pos))
|
|
4105
4121
|
|
|
4106
4122
|
elif cmd == "up":
|
|
4107
|
-
# meshcode up <project> [--agents a,b]
|
|
4123
|
+
# meshcode up <project> [--agents a,b] — Launch-All:
|
|
4108
4124
|
# opens a visible terminal per agent + registers them as hostd-managed.
|
|
4109
4125
|
import importlib
|
|
4110
4126
|
_up = importlib.import_module("meshcode.up")
|
|
@@ -4138,7 +4154,7 @@ if __name__ == "__main__":
|
|
|
4138
4154
|
"history", "clear", "unregister", "connect", "disconnect",
|
|
4139
4155
|
"setup", "add-agent", "run", "go", "invite", "join", "invites", "members",
|
|
4140
4156
|
"revoke-invite", "revoke-member", "login", "prefs", "launcher",
|
|
4141
|
-
"help", "init", "doctor", "compat", "upgrade", "profile", "validate-sessions", "wake-headless",
|
|
4157
|
+
"help", "init", "doctor", "compat", "upgrade", "self-upgrade", "profile", "validate-sessions", "wake-headless",
|
|
4142
4158
|
"supervisor", "hostd", "up", "upload", "quickstart", "patch-hooks", "wake-all",
|
|
4143
4159
|
"install-protocol", "launch-url", "launch-batch",
|
|
4144
4160
|
]
|