meshcode 2.10.59__tar.gz → 2.10.61__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.59 → meshcode-2.10.61}/PKG-INFO +1 -1
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/__init__.py +1 -1
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/comms_v4.py +10 -1
- meshcode-2.10.61/meshcode/error_hints.py +74 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/meshcode_mcp/server.py +10 -0
- meshcode-2.10.61/meshcode/upload.py +125 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode.egg-info/SOURCES.txt +2 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/pyproject.toml +1 -1
- {meshcode-2.10.59 → meshcode-2.10.61}/README.md +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/cli.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/compat.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/exceptions.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/invites.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/launcher.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/preferences.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/secrets.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/self_update.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode/supervisor.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/setup.cfg +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_core.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_exceptions.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_sentinel.py +0 -0
- {meshcode-2.10.59 → meshcode-2.10.61}/tests/test_status_enum_coverage.py +0 -0
|
@@ -3324,6 +3324,15 @@ if __name__ == "__main__":
|
|
|
3324
3324
|
from meshcode.launcher_install import main as _launcher_main # type: ignore
|
|
3325
3325
|
sys.exit(_launcher_main(sys.argv[2:]))
|
|
3326
3326
|
|
|
3327
|
+
elif cmd == "upload":
|
|
3328
|
+
# meshcode upload <file>
|
|
3329
|
+
if len(pos) < 1:
|
|
3330
|
+
print("Usage: meshcode upload <file>")
|
|
3331
|
+
sys.exit(1)
|
|
3332
|
+
import importlib
|
|
3333
|
+
_upl = importlib.import_module("meshcode.upload")
|
|
3334
|
+
sys.exit(_upl.cmd_upload(pos[0]))
|
|
3335
|
+
|
|
3327
3336
|
elif cmd in ("help", "--help", "-h"):
|
|
3328
3337
|
show_help()
|
|
3329
3338
|
|
|
@@ -3360,7 +3369,7 @@ if __name__ == "__main__":
|
|
|
3360
3369
|
"setup", "run", "go", "invite", "join", "invites", "members",
|
|
3361
3370
|
"revoke-invite", "revoke-member", "login", "prefs", "launcher",
|
|
3362
3371
|
"help", "init", "doctor", "compat", "upgrade", "profile", "validate-sessions", "wake-headless",
|
|
3363
|
-
"supervisor",
|
|
3372
|
+
"supervisor", "upload",
|
|
3364
3373
|
]
|
|
3365
3374
|
# Simple fuzzy: prefix match + Levenshtein-like best match
|
|
3366
3375
|
suggestions = [c for c in known_cmds if c.startswith(cmd)]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Smart error messages — maps common error patterns to actionable fix commands.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
from meshcode.error_hints import hint_for_error
|
|
5
|
+
print(hint_for_error("agent not found"))
|
|
6
|
+
# → "Run: meshcode register <project> <agent> <role>"
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
_HINTS = [
|
|
10
|
+
# Auth errors
|
|
11
|
+
("invalid api_key", "Run: meshcode login <api_key> to set your credentials"),
|
|
12
|
+
("api key", "Run: meshcode login <api_key> to refresh credentials"),
|
|
13
|
+
("auth_failed", "Run: meshcode login to re-authenticate"),
|
|
14
|
+
("forbidden", "You don't have permission. Check: meshcode doctor"),
|
|
15
|
+
("not authenticated", "Run: meshcode login <api_key>"),
|
|
16
|
+
|
|
17
|
+
# Agent errors
|
|
18
|
+
("agent not found", "Run: meshcode register <project> <agent_name> <role>"),
|
|
19
|
+
("agent.*not registered", "Run: meshcode register <project> <agent_name> <role>"),
|
|
20
|
+
|
|
21
|
+
# Project errors
|
|
22
|
+
("project.*not found", "Check project name with: meshcode projects"),
|
|
23
|
+
("no projects found", "Create one at meshcode.io/dashboard or: meshcode init <project_name>"),
|
|
24
|
+
|
|
25
|
+
# Network errors
|
|
26
|
+
("circuit breaker open", "Supabase is temporarily unavailable. Wait 30s and retry. Details: meshcode doctor"),
|
|
27
|
+
("connection refused", "Check internet connection. If persists: meshcode doctor"),
|
|
28
|
+
("network error", "Check internet connection. If persists: meshcode doctor"),
|
|
29
|
+
("timeout", "Request timed out. Check connection: meshcode doctor"),
|
|
30
|
+
|
|
31
|
+
# MCP errors
|
|
32
|
+
("MCP server crashed", "Restart: meshcode run <agent>"),
|
|
33
|
+
("stdin EOF", "MCP pipe closed. Restart the MCP server or Claude Code session"),
|
|
34
|
+
("mcp.*failed", "Check MCP config: meshcode doctor"),
|
|
35
|
+
|
|
36
|
+
# Task errors
|
|
37
|
+
("task.*already claimed", "This task is being worked on. Try: meshcode tasks to see available tasks"),
|
|
38
|
+
("task not found", "Check task ID. List tasks: meshcode tasks"),
|
|
39
|
+
|
|
40
|
+
# Config errors
|
|
41
|
+
("comms_v4.py not found", "Reinstall: pip install --upgrade meshcode"),
|
|
42
|
+
("\.mcp\.json", "Run: meshcode setup <project> <agent> to create config"),
|
|
43
|
+
|
|
44
|
+
# Token errors
|
|
45
|
+
("token expired", "Generate a new token from the dashboard"),
|
|
46
|
+
("token already used", "Generate a new token from the dashboard"),
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def hint_for_error(error_msg: str) -> str:
|
|
51
|
+
"""Return an actionable fix suggestion for a given error message.
|
|
52
|
+
|
|
53
|
+
Returns empty string if no hint matches.
|
|
54
|
+
"""
|
|
55
|
+
import re
|
|
56
|
+
lower = error_msg.lower()
|
|
57
|
+
for pattern, hint in _HINTS:
|
|
58
|
+
if re.search(pattern, lower):
|
|
59
|
+
return hint
|
|
60
|
+
return ""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def format_error(error_msg: str, prefix: str = "[meshcode]") -> str:
|
|
64
|
+
"""Format an error with an actionable hint if available.
|
|
65
|
+
|
|
66
|
+
Example output:
|
|
67
|
+
[meshcode] ERROR: agent not found
|
|
68
|
+
[meshcode] Fix: Run: meshcode register <project> <agent_name> <role>
|
|
69
|
+
"""
|
|
70
|
+
lines = [f"{prefix} ERROR: {error_msg}"]
|
|
71
|
+
hint = hint_for_error(error_msg)
|
|
72
|
+
if hint:
|
|
73
|
+
lines.append(f"{prefix} Fix: {hint}")
|
|
74
|
+
return "\n".join(lines)
|
|
@@ -2183,6 +2183,16 @@ def _mark_realtime_msgs_read_in_db(messages: List[Dict[str, Any]]) -> None:
|
|
|
2183
2183
|
except Exception as e:
|
|
2184
2184
|
log.debug(f"mark_read failed for msg {mid}: {e}")
|
|
2185
2185
|
|
|
2186
|
+
# Confirm delivery (best-effort, background) — completes the
|
|
2187
|
+
# delivery pipeline: sent → read → confirmed.
|
|
2188
|
+
try:
|
|
2189
|
+
be.sb_rpc("mc_confirm_delivery", {
|
|
2190
|
+
"p_api_key": api_key,
|
|
2191
|
+
"p_message_ids": msg_ids,
|
|
2192
|
+
})
|
|
2193
|
+
except Exception:
|
|
2194
|
+
pass # Non-critical: messages still work without confirmation
|
|
2195
|
+
|
|
2186
2196
|
|
|
2187
2197
|
async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[str, Any]:
|
|
2188
2198
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""MeshCode file upload — register metadata + upload to Supabase Storage."""
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import json
|
|
5
|
+
import mimetypes
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from urllib.request import Request, urlopen
|
|
8
|
+
from urllib.error import HTTPError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def upload_file(file_path: str, api_key: str, supabase_url: str, supabase_key: str) -> dict:
|
|
12
|
+
"""Upload a file to MeshCode via Supabase Storage.
|
|
13
|
+
|
|
14
|
+
1. Register file metadata via mc_register_file RPC
|
|
15
|
+
2. Upload binary to Supabase Storage bucket
|
|
16
|
+
3. Return file_id for task attachment
|
|
17
|
+
|
|
18
|
+
Returns: {"ok": True, "file_id": "...", "storage_path": "..."}
|
|
19
|
+
"""
|
|
20
|
+
path = Path(file_path)
|
|
21
|
+
if not path.exists():
|
|
22
|
+
return {"error": f"file not found: {file_path}"}
|
|
23
|
+
|
|
24
|
+
file_name = path.name
|
|
25
|
+
mime_type = mimetypes.guess_type(file_name)[0] or "application/octet-stream"
|
|
26
|
+
size_bytes = path.stat().st_size
|
|
27
|
+
|
|
28
|
+
if size_bytes > 52428800:
|
|
29
|
+
return {"error": "file too large (max 50MB)"}
|
|
30
|
+
|
|
31
|
+
# 1. Register metadata
|
|
32
|
+
rpc_url = f"{supabase_url}/rest/v1/rpc/mc_register_file"
|
|
33
|
+
payload = json.dumps({
|
|
34
|
+
"p_api_key": api_key,
|
|
35
|
+
"p_file_name": file_name,
|
|
36
|
+
"p_mime_type": mime_type,
|
|
37
|
+
"p_size_bytes": size_bytes,
|
|
38
|
+
}).encode("utf-8")
|
|
39
|
+
|
|
40
|
+
req = Request(rpc_url, data=payload, method="POST")
|
|
41
|
+
req.add_header("apikey", supabase_key)
|
|
42
|
+
req.add_header("Content-Type", "application/json")
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
with urlopen(req) as resp:
|
|
46
|
+
result = json.loads(resp.read())
|
|
47
|
+
except HTTPError as e:
|
|
48
|
+
body = e.read().decode("utf-8", errors="replace")
|
|
49
|
+
return {"error": f"register failed: {body[:200]}"}
|
|
50
|
+
|
|
51
|
+
if not result or result.get("error"):
|
|
52
|
+
return {"error": f"register failed: {result}"}
|
|
53
|
+
|
|
54
|
+
storage_path = result["storage_path"]
|
|
55
|
+
bucket = result.get("bucket", "meshcode-files")
|
|
56
|
+
file_id = result["file_id"]
|
|
57
|
+
|
|
58
|
+
# 2. Upload to Supabase Storage
|
|
59
|
+
storage_url = f"{supabase_url}/storage/v1/object/{bucket}/{storage_path}"
|
|
60
|
+
with open(path, "rb") as f:
|
|
61
|
+
file_data = f.read()
|
|
62
|
+
|
|
63
|
+
upload_req = Request(storage_url, data=file_data, method="POST")
|
|
64
|
+
upload_req.add_header("apikey", supabase_key)
|
|
65
|
+
upload_req.add_header("Authorization", f"Bearer {supabase_key}")
|
|
66
|
+
upload_req.add_header("Content-Type", mime_type)
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
with urlopen(upload_req) as resp:
|
|
70
|
+
upload_result = json.loads(resp.read())
|
|
71
|
+
except HTTPError as e:
|
|
72
|
+
body = e.read().decode("utf-8", errors="replace")
|
|
73
|
+
return {"error": f"upload failed: {body[:200]}", "file_id": file_id, "note": "metadata registered but upload failed"}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
"ok": True,
|
|
77
|
+
"file_id": file_id,
|
|
78
|
+
"file_name": file_name,
|
|
79
|
+
"size_bytes": size_bytes,
|
|
80
|
+
"storage_path": storage_path,
|
|
81
|
+
"mime_type": mime_type,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def cmd_upload(file_path: str, project: str = None) -> int:
|
|
86
|
+
"""CLI entry point for meshcode upload <file>."""
|
|
87
|
+
from meshcode.meshcode_mcp import backend as be
|
|
88
|
+
|
|
89
|
+
api_key = None
|
|
90
|
+
# Try to get API key from environment or config
|
|
91
|
+
env_key = os.environ.get("MESHCODE_API_KEY")
|
|
92
|
+
if env_key:
|
|
93
|
+
api_key = env_key
|
|
94
|
+
else:
|
|
95
|
+
# Try keychain profile
|
|
96
|
+
try:
|
|
97
|
+
profile_path = Path.home() / ".meshcode" / "profile_meta.json"
|
|
98
|
+
if profile_path.exists():
|
|
99
|
+
meta = json.loads(profile_path.read_text())
|
|
100
|
+
api_key = meta.get("api_key")
|
|
101
|
+
except Exception:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
if not api_key:
|
|
105
|
+
print("[meshcode upload] ERROR: No API key. Set MESHCODE_API_KEY or run meshcode login.")
|
|
106
|
+
return 1
|
|
107
|
+
|
|
108
|
+
print(f"[meshcode upload] Uploading {file_path}...")
|
|
109
|
+
result = upload_file(
|
|
110
|
+
file_path=file_path,
|
|
111
|
+
api_key=api_key,
|
|
112
|
+
supabase_url=be.SUPABASE_URL,
|
|
113
|
+
supabase_key=be.SUPABASE_KEY,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if result.get("ok"):
|
|
117
|
+
print(f"[meshcode upload] Success!")
|
|
118
|
+
print(f" file_id: {result['file_id']}")
|
|
119
|
+
print(f" file_name: {result['file_name']}")
|
|
120
|
+
print(f" size: {result['size_bytes']} bytes")
|
|
121
|
+
print(f" path: {result['storage_path']}")
|
|
122
|
+
return 0
|
|
123
|
+
else:
|
|
124
|
+
print(f"[meshcode upload] ERROR: {result.get('error', 'unknown error')}")
|
|
125
|
+
return 1
|
|
@@ -5,6 +5,7 @@ meshcode/ascii_art.py
|
|
|
5
5
|
meshcode/cli.py
|
|
6
6
|
meshcode/comms_v4.py
|
|
7
7
|
meshcode/compat.py
|
|
8
|
+
meshcode/error_hints.py
|
|
8
9
|
meshcode/exceptions.py
|
|
9
10
|
meshcode/invites.py
|
|
10
11
|
meshcode/launcher.py
|
|
@@ -16,6 +17,7 @@ meshcode/secrets.py
|
|
|
16
17
|
meshcode/self_update.py
|
|
17
18
|
meshcode/setup_clients.py
|
|
18
19
|
meshcode/supervisor.py
|
|
20
|
+
meshcode/upload.py
|
|
19
21
|
meshcode.egg-info/PKG-INFO
|
|
20
22
|
meshcode.egg-info/SOURCES.txt
|
|
21
23
|
meshcode.egg-info/dependency_links.txt
|
|
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
|