meshcode 2.10.17__tar.gz → 2.10.18__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.10.17 → meshcode-2.10.18}/PKG-INFO +1 -1
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/__init__.py +1 -1
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/meshcode_mcp/backend.py +110 -40
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/meshcode_mcp/server.py +26 -20
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.10.17 → meshcode-2.10.18}/pyproject.toml +1 -1
- {meshcode-2.10.17 → meshcode-2.10.18}/README.md +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/cli.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/invites.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/launcher.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/preferences.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/secrets.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/self_update.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/setup.cfg +0 -0
- {meshcode-2.10.17 → meshcode-2.10.18}/tests/test_status_enum_coverage.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""MeshCode — Real-time communication between AI agents."""
|
|
2
|
-
__version__ = "2.10.
|
|
2
|
+
__version__ = "2.10.18"
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
"""Thin Supabase REST backend used by both the MCP server and tests.
|
|
2
2
|
|
|
3
3
|
Reuses the helpers from comms_v4.py without going through subprocess.
|
|
4
|
-
Zero deps beyond stdlib (urllib).
|
|
4
|
+
Zero deps beyond stdlib (urllib + http.client for connection pooling).
|
|
5
5
|
"""
|
|
6
|
+
import http.client
|
|
6
7
|
import json
|
|
7
8
|
import os
|
|
9
|
+
import ssl
|
|
8
10
|
import time as _time
|
|
9
11
|
import threading as _threading
|
|
10
12
|
from datetime import datetime
|
|
11
13
|
from pathlib import Path
|
|
12
14
|
from typing import Any, Dict, List, Optional
|
|
13
15
|
from urllib.error import HTTPError, URLError
|
|
14
|
-
from urllib.parse import quote
|
|
16
|
+
from urllib.parse import quote, urlparse
|
|
15
17
|
from urllib.request import Request, urlopen
|
|
16
18
|
|
|
17
19
|
|
|
@@ -97,6 +99,74 @@ SUPABASE_SERVICE_ROLE_KEY = (
|
|
|
97
99
|
SCHEMA = "meshcode"
|
|
98
100
|
|
|
99
101
|
|
|
102
|
+
# ── Persistent HTTPS Connection Pool ──────────────────────────────
|
|
103
|
+
# Each urlopen() opens a new TCP+TLS connection (~1-2s overhead).
|
|
104
|
+
# This pool reuses connections, cutting typical latency from ~3s to ~100ms.
|
|
105
|
+
class _ConnectionPool:
|
|
106
|
+
"""Thread-safe persistent HTTPS connection to Supabase."""
|
|
107
|
+
|
|
108
|
+
def __init__(self, url: str, max_idle: float = 30.0):
|
|
109
|
+
parsed = urlparse(url)
|
|
110
|
+
self._host = parsed.hostname
|
|
111
|
+
self._port = parsed.port or 443
|
|
112
|
+
self._conn: Optional[http.client.HTTPSConnection] = None
|
|
113
|
+
self._lock = _threading.Lock()
|
|
114
|
+
self._max_idle = max_idle
|
|
115
|
+
self._last_used = 0.0
|
|
116
|
+
self._ctx = ssl.create_default_context()
|
|
117
|
+
|
|
118
|
+
def _get_conn(self) -> http.client.HTTPSConnection:
|
|
119
|
+
now = _time.monotonic()
|
|
120
|
+
if self._conn is not None:
|
|
121
|
+
# Close stale connections
|
|
122
|
+
if now - self._last_used > self._max_idle:
|
|
123
|
+
try:
|
|
124
|
+
self._conn.close()
|
|
125
|
+
except Exception:
|
|
126
|
+
pass
|
|
127
|
+
self._conn = None
|
|
128
|
+
if self._conn is None:
|
|
129
|
+
self._conn = http.client.HTTPSConnection(
|
|
130
|
+
self._host, self._port, timeout=10, context=self._ctx
|
|
131
|
+
)
|
|
132
|
+
return self._conn
|
|
133
|
+
|
|
134
|
+
def request(self, method: str, path: str, body: Optional[bytes], headers: Dict[str, str]) -> tuple:
|
|
135
|
+
"""Returns (status, response_body_str). Thread-safe with retry on broken pipe."""
|
|
136
|
+
with self._lock:
|
|
137
|
+
for attempt in range(2):
|
|
138
|
+
conn = self._get_conn()
|
|
139
|
+
try:
|
|
140
|
+
conn.request(method, path, body=body, headers=headers)
|
|
141
|
+
resp = conn.getresponse()
|
|
142
|
+
data = resp.read().decode("utf-8")
|
|
143
|
+
self._last_used = _time.monotonic()
|
|
144
|
+
return resp.status, data
|
|
145
|
+
except (http.client.RemoteDisconnected, BrokenPipeError,
|
|
146
|
+
ConnectionResetError, OSError) as e:
|
|
147
|
+
# Connection went stale — close and retry once
|
|
148
|
+
try:
|
|
149
|
+
self._conn.close()
|
|
150
|
+
except Exception:
|
|
151
|
+
pass
|
|
152
|
+
self._conn = None
|
|
153
|
+
if attempt == 0:
|
|
154
|
+
continue
|
|
155
|
+
raise
|
|
156
|
+
|
|
157
|
+
def close(self):
|
|
158
|
+
with self._lock:
|
|
159
|
+
if self._conn:
|
|
160
|
+
try:
|
|
161
|
+
self._conn.close()
|
|
162
|
+
except Exception:
|
|
163
|
+
pass
|
|
164
|
+
self._conn = None
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
_pool = _ConnectionPool(SUPABASE_URL)
|
|
168
|
+
|
|
169
|
+
|
|
100
170
|
def _now_iso() -> str:
|
|
101
171
|
return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S+00:00")
|
|
102
172
|
|
|
@@ -118,27 +188,29 @@ def _headers(*, prefer: Optional[str] = None, content_profile: bool = True) -> D
|
|
|
118
188
|
def _request(method: str, path: str, *, data: Any = None, prefer: Optional[str] = None) -> Any:
|
|
119
189
|
if not _circuit.can_execute():
|
|
120
190
|
return {"_error": "circuit breaker open — Supabase temporarily unavailable", "_code": 503}
|
|
121
|
-
|
|
191
|
+
rest_path = f"/rest/v1/{path}"
|
|
122
192
|
body = json.dumps(data).encode("utf-8") if data else None
|
|
123
|
-
|
|
193
|
+
hdrs = _headers(prefer=prefer)
|
|
124
194
|
try:
|
|
125
|
-
|
|
126
|
-
|
|
195
|
+
status, raw = _pool.request(method, rest_path, body, hdrs)
|
|
196
|
+
if 200 <= status < 300:
|
|
127
197
|
_circuit.record_success()
|
|
128
198
|
return json.loads(raw) if raw.strip() else None
|
|
129
|
-
|
|
130
|
-
err = e.read().decode("utf-8", errors="replace")
|
|
131
|
-
# 4xx = client error (not a backend failure), don't trip breaker
|
|
132
|
-
if 400 <= e.code < 500:
|
|
199
|
+
elif 400 <= status < 500:
|
|
133
200
|
_circuit.record_success()
|
|
201
|
+
try:
|
|
202
|
+
err_obj = json.loads(raw)
|
|
203
|
+
return {"_error": err_obj.get("message", raw[:200]), "_code": status}
|
|
204
|
+
except Exception:
|
|
205
|
+
return {"_error": raw[:200], "_code": status}
|
|
134
206
|
else:
|
|
135
207
|
_circuit.record_failure()
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
except (URLError, OSError, TimeoutError) as e:
|
|
208
|
+
try:
|
|
209
|
+
err_obj = json.loads(raw)
|
|
210
|
+
return {"_error": err_obj.get("message", raw[:200]), "_code": status}
|
|
211
|
+
except Exception:
|
|
212
|
+
return {"_error": raw[:200], "_code": status}
|
|
213
|
+
except (URLError, OSError, TimeoutError, http.client.HTTPException) as e:
|
|
142
214
|
_circuit.record_failure()
|
|
143
215
|
return {"_error": str(getattr(e, 'reason', e)), "_code": 0}
|
|
144
216
|
|
|
@@ -200,34 +272,31 @@ def sb_rpc(fn_name: str, params: Dict, *, _max_retries: int = 3) -> Any:
|
|
|
200
272
|
if not _circuit.can_execute():
|
|
201
273
|
return {"_error": "circuit breaker open — Supabase temporarily unavailable", "_circuit": "open"}
|
|
202
274
|
last_err = None
|
|
275
|
+
rpc_path = f"/rest/v1/rpc/{fn_name}"
|
|
276
|
+
body = json.dumps(params).encode("utf-8")
|
|
277
|
+
hdrs = _headers(content_profile=False)
|
|
203
278
|
for attempt in range(_max_retries):
|
|
204
|
-
url = f"{SUPABASE_URL}/rest/v1/rpc/{fn_name}"
|
|
205
|
-
body = json.dumps(params).encode("utf-8")
|
|
206
|
-
req = Request(url, data=body, method="POST", headers=_headers(content_profile=False))
|
|
207
279
|
try:
|
|
208
|
-
|
|
209
|
-
|
|
280
|
+
status, raw = _pool.request("POST", rpc_path, body, hdrs)
|
|
281
|
+
if 200 <= status < 300:
|
|
210
282
|
result = json.loads(raw) if raw.strip() else None
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
except HTTPError as e:
|
|
217
|
-
err = e.read().decode("utf-8", errors="replace")
|
|
218
|
-
# 4xx errors are not transient — don't retry, don't trip breaker
|
|
219
|
-
if 400 <= e.code < 500:
|
|
283
|
+
_circuit.record_success()
|
|
284
|
+
if _recording_enabled and fn_name not in _SKIP_RECORDING:
|
|
285
|
+
_bg_record("tool_call", {"rpc": fn_name})
|
|
286
|
+
return result
|
|
287
|
+
elif 400 <= status < 500:
|
|
220
288
|
_circuit.record_success()
|
|
221
289
|
try:
|
|
222
|
-
result = {"_error": json.loads(
|
|
290
|
+
result = {"_error": json.loads(raw).get("message", raw[:200])}
|
|
223
291
|
except Exception:
|
|
224
|
-
result = {"_error":
|
|
292
|
+
result = {"_error": raw[:200]}
|
|
225
293
|
if _recording_enabled and fn_name not in _SKIP_RECORDING:
|
|
226
|
-
_bg_record("error", {"rpc": fn_name, "error":
|
|
294
|
+
_bg_record("error", {"rpc": fn_name, "error": raw[:200]})
|
|
227
295
|
return result
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
296
|
+
else:
|
|
297
|
+
_circuit.record_failure()
|
|
298
|
+
last_err = raw[:200]
|
|
299
|
+
except (URLError, OSError, TimeoutError, http.client.HTTPException) as e:
|
|
231
300
|
_circuit.record_failure()
|
|
232
301
|
last_err = str(getattr(e, 'reason', e))
|
|
233
302
|
# Retry with jitter for transient errors (5xx, network)
|
|
@@ -260,13 +329,14 @@ def _bg_record(event_type: str, payload: dict):
|
|
|
260
329
|
|
|
261
330
|
def sb_rpc_raw(fn_name: str, params: Dict) -> Any:
|
|
262
331
|
"""Raw RPC call without recording (to avoid infinite recursion)."""
|
|
263
|
-
|
|
332
|
+
rpc_path = f"/rest/v1/rpc/{fn_name}"
|
|
264
333
|
body = json.dumps(params).encode("utf-8")
|
|
265
|
-
|
|
334
|
+
hdrs = _headers(content_profile=False)
|
|
266
335
|
try:
|
|
267
|
-
|
|
268
|
-
|
|
336
|
+
status, raw = _pool.request("POST", rpc_path, body, hdrs)
|
|
337
|
+
if 200 <= status < 300:
|
|
269
338
|
return json.loads(raw) if raw.strip() else None
|
|
339
|
+
return None
|
|
270
340
|
except Exception:
|
|
271
341
|
return None
|
|
272
342
|
|
|
@@ -85,11 +85,11 @@ def _mc_log(msg: str, level: str = "info") -> None:
|
|
|
85
85
|
c = _agent_color(agent) if agent else "\033[36m"
|
|
86
86
|
prefix = f"{c}{_ANSI_BOLD}[meshcode-mcp]{_ANSI_RESET}"
|
|
87
87
|
if level == "error":
|
|
88
|
-
print(f"{prefix} \033[91mERROR:{_ANSI_RESET} {msg}",
|
|
88
|
+
print(f"{prefix} \033[91mERROR:{_ANSI_RESET} {msg}", file=sys.stderr)
|
|
89
89
|
elif level == "warn":
|
|
90
|
-
print(f"{prefix} \033[33mWARNING:{_ANSI_RESET} {msg}",
|
|
90
|
+
print(f"{prefix} \033[33mWARNING:{_ANSI_RESET} {msg}", file=sys.stderr)
|
|
91
91
|
else:
|
|
92
|
-
print(f"{prefix} {c}{msg}{_ANSI_RESET}",
|
|
92
|
+
print(f"{prefix} {c}{msg}{_ANSI_RESET}", file=sys.stderr)
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
# ============================================================
|
|
@@ -576,6 +576,10 @@ def with_working_status(func):
|
|
|
576
576
|
_record_event_bg("tool_call", {"tool": name, "args_keys": list(kwargs.keys())})
|
|
577
577
|
try:
|
|
578
578
|
return await func(*args, **kwargs)
|
|
579
|
+
except asyncio.CancelledError:
|
|
580
|
+
# User pressed ESC or MCP client cancelled the request.
|
|
581
|
+
# Let it propagate cleanly — FastMCP handles cancellation.
|
|
582
|
+
raise
|
|
579
583
|
except Exception as e:
|
|
580
584
|
if not skip:
|
|
581
585
|
_auto_learn_error(name, e, list(kwargs.keys()))
|
|
@@ -713,7 +717,7 @@ def _boot_diagnostic() -> None:
|
|
|
713
717
|
be.sb_select("mc_projects", f"id=eq.{_PROJECT_ID}", limit=1)
|
|
714
718
|
checks_passed += 1
|
|
715
719
|
except Exception as e:
|
|
716
|
-
print(f"[meshcode] BOOT CHECK FAILED: Supabase API unreachable ({e}). Fix: check network/VPN.",
|
|
720
|
+
print(f"[meshcode] BOOT CHECK FAILED: Supabase API unreachable ({e}). Fix: check network/VPN.", file=sys.stderr)
|
|
717
721
|
|
|
718
722
|
# Check 2: Lease valid
|
|
719
723
|
try:
|
|
@@ -723,11 +727,11 @@ def _boot_diagnostic() -> None:
|
|
|
723
727
|
if agent.get("instance_id") == _INSTANCE_ID:
|
|
724
728
|
checks_passed += 1
|
|
725
729
|
else:
|
|
726
|
-
print(f"[meshcode] BOOT CHECK FAILED: Lease mismatch — expected {_INSTANCE_ID}, got {agent.get('instance_id')}. Fix: restart agent.",
|
|
730
|
+
print(f"[meshcode] BOOT CHECK FAILED: Lease mismatch — expected {_INSTANCE_ID}, got {agent.get('instance_id')}. Fix: restart agent.", file=sys.stderr)
|
|
727
731
|
else:
|
|
728
|
-
print(f"[meshcode] BOOT CHECK FAILED: Agent '{AGENT_NAME}' not found in project. Fix: register agent first.",
|
|
732
|
+
print(f"[meshcode] BOOT CHECK FAILED: Agent '{AGENT_NAME}' not found in project. Fix: register agent first.", file=sys.stderr)
|
|
729
733
|
except Exception as e:
|
|
730
|
-
print(f"[meshcode] BOOT CHECK FAILED: Could not verify lease ({e}).",
|
|
734
|
+
print(f"[meshcode] BOOT CHECK FAILED: Could not verify lease ({e}).", file=sys.stderr)
|
|
731
735
|
|
|
732
736
|
# Check 3: Heartbeat recent
|
|
733
737
|
try:
|
|
@@ -736,7 +740,7 @@ def _boot_diagnostic() -> None:
|
|
|
736
740
|
if hb:
|
|
737
741
|
checks_passed += 1
|
|
738
742
|
else:
|
|
739
|
-
print(f"[meshcode] BOOT CHECK WARNING: No heartbeat recorded yet.",
|
|
743
|
+
print(f"[meshcode] BOOT CHECK WARNING: No heartbeat recorded yet.", file=sys.stderr)
|
|
740
744
|
else:
|
|
741
745
|
checks_passed += 1 # skip if no agent data
|
|
742
746
|
except Exception:
|
|
@@ -753,9 +757,9 @@ def _boot_diagnostic() -> None:
|
|
|
753
757
|
checks_passed += 1 # non-critical
|
|
754
758
|
|
|
755
759
|
if checks_passed == checks_total:
|
|
756
|
-
print(f"[meshcode] All boot checks passed ({checks_passed}/{checks_total}).",
|
|
760
|
+
print(f"[meshcode] All boot checks passed ({checks_passed}/{checks_total}).", file=sys.stderr)
|
|
757
761
|
else:
|
|
758
|
-
print(f"[meshcode] Boot checks: {checks_passed}/{checks_total} passed. Agent starting anyway.",
|
|
762
|
+
print(f"[meshcode] Boot checks: {checks_passed}/{checks_total} passed. Agent starting anyway.", file=sys.stderr)
|
|
759
763
|
|
|
760
764
|
|
|
761
765
|
_boot_diagnostic()
|
|
@@ -783,9 +787,11 @@ _SHUTDOWN_LOGGED = False
|
|
|
783
787
|
def _log_crash_to_db(reason: str = "unknown", error_detail: str = "") -> None:
|
|
784
788
|
"""Best-effort crash log to mc_agent_crash_logs table. Non-fatal if table doesn't exist."""
|
|
785
789
|
global _SHUTDOWN_LOGGED
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
790
|
+
# Only suppress duplicates for process-level shutdown events, not tool exceptions
|
|
791
|
+
if reason in ("process_exit", "signal", "keyboard_interrupt", "system_exit", "unhandled_exception"):
|
|
792
|
+
if _SHUTDOWN_LOGGED:
|
|
793
|
+
return
|
|
794
|
+
_SHUTDOWN_LOGGED = True
|
|
789
795
|
try:
|
|
790
796
|
be.sb_rpc("mc_log_error", {
|
|
791
797
|
"p_api_key": _get_api_key(),
|
|
@@ -1462,9 +1468,9 @@ try:
|
|
|
1462
1468
|
elif isinstance(_ls_val, str):
|
|
1463
1469
|
_LAST_SEEN_TS = _ls_val
|
|
1464
1470
|
if _LAST_SEEN_TS:
|
|
1465
|
-
print(f"[meshcode] Restored last_seen={_LAST_SEEN_TS} from mesh memory.",
|
|
1471
|
+
print(f"[meshcode] Restored last_seen={_LAST_SEEN_TS} from mesh memory.", file=sys.stderr)
|
|
1466
1472
|
except Exception as _e:
|
|
1467
|
-
print(f"[meshcode] Could not restore last_seen: {_e}",
|
|
1473
|
+
print(f"[meshcode] Could not restore last_seen: {_e}", file=sys.stderr)
|
|
1468
1474
|
|
|
1469
1475
|
|
|
1470
1476
|
def _get_pending_tasks_summary() -> Optional[List[Dict[str, str]]]:
|
|
@@ -2624,7 +2630,7 @@ def _auto_update() -> None:
|
|
|
2624
2630
|
return
|
|
2625
2631
|
|
|
2626
2632
|
# 3. Install the new version (blocking, 60s timeout)
|
|
2627
|
-
print(f"[meshcode] Updating {current} → {latest}...",
|
|
2633
|
+
print(f"[meshcode] Updating {current} → {latest}...", file=sys.stderr)
|
|
2628
2634
|
try:
|
|
2629
2635
|
result = subprocess.run(
|
|
2630
2636
|
[sys.executable, "-m", "pip", "install", "--upgrade",
|
|
@@ -2644,11 +2650,11 @@ def _auto_update() -> None:
|
|
|
2644
2650
|
# 4. In MCP mode, NEVER re-exec — it kills the stdio pipe to Claude Code.
|
|
2645
2651
|
# The new version will load on the next clean boot.
|
|
2646
2652
|
if os.environ.get("MESHCODE_MCP_SERVE") == "1":
|
|
2647
|
-
print(f"[meshcode] Updated {current} → {latest}. Will load on next boot (MCP mode — cannot restart).",
|
|
2653
|
+
print(f"[meshcode] Updated {current} → {latest}. Will load on next boot (MCP mode — cannot restart).", file=sys.stderr)
|
|
2648
2654
|
return
|
|
2649
2655
|
|
|
2650
2656
|
# CLI mode: safe to re-exec
|
|
2651
|
-
print(f"[meshcode] Updated to {latest}, restarting...",
|
|
2657
|
+
print(f"[meshcode] Updated to {latest}, restarting...", file=sys.stderr)
|
|
2652
2658
|
os.environ["MESHCODE_UPDATED"] = "1"
|
|
2653
2659
|
try:
|
|
2654
2660
|
os.execv(sys.executable, [sys.executable] + sys.argv)
|
|
@@ -2683,6 +2689,6 @@ def run_server():
|
|
|
2683
2689
|
import traceback as _tb
|
|
2684
2690
|
tb_str = _tb.format_exc()
|
|
2685
2691
|
_log_crash_to_db("unhandled_exception", f"{type(e).__name__}: {e}\n{tb_str}")
|
|
2686
|
-
print(f"[meshcode-mcp] FATAL: {e}",
|
|
2687
|
-
print(f"[meshcode-mcp] Stack trace logged to mc_agent_crash_logs",
|
|
2692
|
+
print(f"[meshcode-mcp] FATAL: {e}", file=sys.stderr)
|
|
2693
|
+
print(f"[meshcode-mcp] Stack trace logged to mc_agent_crash_logs", file=sys.stderr)
|
|
2688
2694
|
sys.exit(1)
|
|
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
|