meshcode 2.11.118__tar.gz → 2.11.120__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.118 → meshcode-2.11.120}/PKG-INFO +1 -1
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/__init__.py +1 -1
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/server.py +68 -1
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/upload.py +5 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.11.118 → meshcode-2.11.120}/pyproject.toml +1 -1
- {meshcode-2.11.118 → meshcode-2.11.120}/README.md +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/__main__.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/_session_handoff_template.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/_stop_hook_template.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/atomic_push.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/claude_update.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/cli.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/compat.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/daemon.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/date_parse.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/doctor.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/error_hints.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/exceptions.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/hooks/__init__.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/hooks/repo_path_lock.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/hostd.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/invites.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/launcher.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/preferences.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/protocol_handler.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/quickstart.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/rpc_allowlist.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/run_agent.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/scripts/check_secrets.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/scripts/race_rate_harness.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/secrets.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/self_update.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/supervisor.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/up.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/setup.cfg +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_auto_update_hardening.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_autonomous_closegap_1.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_autonomous_closegap_2.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_autonomous_closegap_3.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_autonomous_prompt_inject.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_boot_bug_regression.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_color_truecolor.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_core.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_date_parse.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_doctor.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_epistemic_v1_python_sdk.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_epistemic_v1_stop_conditions.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_exceptions.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_file_upload.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_init_device_code.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_install_guard.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_lease_sigterm_release.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_marketplace_ratings.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_rpc_grants.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_run_agent_dry_run.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_run_agent_no_server_import.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_self_update_user_site.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_sentinel.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_setup_path.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_sleep_signals.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_stay_on_loop_hook.py +0 -0
- {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_wait_open_tasks_contradiction.py +0 -0
|
@@ -2906,7 +2906,8 @@ def meshcode_send(to: Union[str, List[str]], message: Any, in_reply_to: Optional
|
|
|
2906
2906
|
# mode-2 soft-fail (R2-3): a malformed in_reply_to (not a valid uuid) would hit
|
|
2907
2907
|
# the uuid cast at the RPC boundary and reject the WHOLE message. Drop it so the
|
|
2908
2908
|
# message still sends (pairs with mig463's nonexistent-but-valid-uuid soft-fail).
|
|
2909
|
-
|
|
2909
|
+
import re as _re # .119 regression: no module-level `re`; local like lines 3809/3942/4008
|
|
2910
|
+
if in_reply_to is not None and not _re.fullmatch(
|
|
2910
2911
|
r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}",
|
|
2911
2912
|
str(in_reply_to)):
|
|
2912
2913
|
in_reply_to = None
|
|
@@ -3460,6 +3461,72 @@ def meshcode_download_file(file_id: str) -> Dict[str, Any]:
|
|
|
3460
3461
|
return _consume(content, mime_type, file_name)
|
|
3461
3462
|
|
|
3462
3463
|
|
|
3464
|
+
@mcp.tool()
|
|
3465
|
+
@with_working_status
|
|
3466
|
+
def meshcode_send_file(to: Union[str, List[str]], file_path: str,
|
|
3467
|
+
caption: str = "", in_reply_to: Optional[str] = None,
|
|
3468
|
+
sensitive: bool = False) -> Dict[str, Any]:
|
|
3469
|
+
"""Send a local file to other agent(s) as a mesh attachment — one call
|
|
3470
|
+
instead of the 3-call paste dance (chunked text sends).
|
|
3471
|
+
|
|
3472
|
+
Uploads file_path to mesh storage (mc_register_file + meshcode-files
|
|
3473
|
+
bucket, 50MB cap) and delivers a message whose payload carries the
|
|
3474
|
+
FE-standard attachment shape {file: {file_id, file_name, mime_type}}.
|
|
3475
|
+
The dashboard renders a file chip; agent recipients retrieve content via
|
|
3476
|
+
meshcode_download_file(file_id). `to` accepts the same forms as
|
|
3477
|
+
meshcode_send (agent / CSV / list / agent@meshwork).
|
|
3478
|
+
|
|
3479
|
+
Args:
|
|
3480
|
+
to: recipient(s), meshcode_send forms.
|
|
3481
|
+
file_path: local path of the file to attach.
|
|
3482
|
+
caption: optional message text shown with the chip (default:
|
|
3483
|
+
"file: <name> (<size> bytes)").
|
|
3484
|
+
in_reply_to: optional message UUID to thread under.
|
|
3485
|
+
sensitive: hide from exports (secret/PII payloads).
|
|
3486
|
+
"""
|
|
3487
|
+
from pathlib import Path as _P
|
|
3488
|
+
api_key = _get_api_key()
|
|
3489
|
+
if not api_key:
|
|
3490
|
+
return {"error": "no api key", "error_code": "auth_failed"}
|
|
3491
|
+
p = _P(file_path).expanduser()
|
|
3492
|
+
if not p.is_file():
|
|
3493
|
+
return {"error": f"file not found: {file_path}", "error_code": "file_not_found"}
|
|
3494
|
+
|
|
3495
|
+
# Upload reuses the proven CLI path (meshcode/upload.py): mc_register_file
|
|
3496
|
+
# RPC (api_key-authorized, mints file_id + storage_path) then a storage
|
|
3497
|
+
# POST. The POST prefers this workspace's service key when present —
|
|
3498
|
+
# mirrors meshcode_download_file's fallback order; guest workspaces
|
|
3499
|
+
# without it fall back to the anon key (storage policy permitting).
|
|
3500
|
+
from meshcode.upload import upload_file as _upload_file
|
|
3501
|
+
_storage_key = os.environ.get("MESHCODE_SUPABASE_SERVICE_KEY", "") or be.SUPABASE_KEY
|
|
3502
|
+
up = _upload_file(str(p), api_key, be.SUPABASE_URL, _storage_key)
|
|
3503
|
+
if not up.get("ok"):
|
|
3504
|
+
out = {"error": up.get("error", "upload failed"), "error_code": "upload_failed"}
|
|
3505
|
+
if up.get("file_id"):
|
|
3506
|
+
# metadata row exists but the binary never landed — surface the id
|
|
3507
|
+
# so the caller doesn't re-register on retry confusion.
|
|
3508
|
+
out["file_id"] = up["file_id"]
|
|
3509
|
+
out["note"] = "metadata registered, binary upload failed — retry sends a fresh file_id"
|
|
3510
|
+
return out
|
|
3511
|
+
|
|
3512
|
+
payload = {
|
|
3513
|
+
"type": "file_attachment",
|
|
3514
|
+
"text": caption or f"file: {up['file_name']} ({up['size_bytes']} bytes)",
|
|
3515
|
+
"file": {"file_id": up["file_id"], "file_name": up["file_name"],
|
|
3516
|
+
"mime_type": up["mime_type"]},
|
|
3517
|
+
}
|
|
3518
|
+
# msg_type stays default 'msg' — mc_messages.type carries a DB CHECK
|
|
3519
|
+
# (chk_mc_messages_type) that REFUSES unknown tags (live-caught 2026-06-09;
|
|
3520
|
+
# the "soft-validate" is SDK-side only). The attachment semantic rides in
|
|
3521
|
+
# payload.type + payload.file, which is what the FE chip renderer keys on.
|
|
3522
|
+
res = meshcode_send(to, payload, in_reply_to=in_reply_to, sensitive=sensitive)
|
|
3523
|
+
if isinstance(res, dict) and not res.get("error"):
|
|
3524
|
+
res = dict(res)
|
|
3525
|
+
res.update({"file_id": up["file_id"], "file_name": up["file_name"],
|
|
3526
|
+
"size_bytes": up["size_bytes"], "mime_type": up["mime_type"]})
|
|
3527
|
+
return res
|
|
3528
|
+
|
|
3529
|
+
|
|
3463
3530
|
def _detect_global_done(messages: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
|
3464
3531
|
"""Return {reason, from} if the list contains a global_done signal, else None."""
|
|
3465
3532
|
for m in messages:
|
|
@@ -4,6 +4,11 @@ import sys
|
|
|
4
4
|
import json
|
|
5
5
|
import mimetypes
|
|
6
6
|
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Python's mimetypes table lacks markdown — without this, .md uploads land as
|
|
9
|
+
# application/octet-stream and meshcode_download_file hands recipients a temp
|
|
10
|
+
# file instead of inline text (live-caught in the send_file E2E 2026-06-09).
|
|
11
|
+
mimetypes.add_type("text/markdown", ".md")
|
|
7
12
|
from urllib.request import Request, urlopen
|
|
8
13
|
from urllib.error import HTTPError
|
|
9
14
|
|
|
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
|
|
File without changes
|