meshcode 2.10.93__tar.gz → 2.10.95__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.93/meshcode.egg-info → meshcode-2.10.95}/PKG-INFO +6 -1
- {meshcode-2.10.93 → meshcode-2.10.95}/README.md +5 -0
- meshcode-2.10.95/meshcode/__init__.py +82 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/meshcode_mcp/backend.py +92 -16
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/meshcode_mcp/server.py +124 -19
- meshcode-2.10.95/meshcode.egg-info/PKG-INFO +446 -0
- meshcode-2.10.93/PKG-INFO → meshcode-2.10.95/meshcode.egg-info/PKG-INFO 2 +5 -0
- meshcode-2.10.93/meshcode.egg-info/SOURCES.txt → meshcode-2.10.95/meshcode.egg-info/SOURCES 2.txt +1 -0
- meshcode-2.10.95/meshcode.egg-info/SOURCES.txt +204 -0
- meshcode-2.10.95/meshcode.egg-info/dependency_links.txt +1 -0
- meshcode-2.10.95/meshcode.egg-info/entry_points.txt +3 -0
- meshcode-2.10.95/meshcode.egg-info/requires.txt +5 -0
- meshcode-2.10.95/meshcode.egg-info/top_level.txt +4 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/pyproject.toml +1 -1
- meshcode-2.10.95/tests/test_auto_update_hardening.py +238 -0
- meshcode-2.10.93/meshcode/__init__.py +0 -82
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/cli.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/compat.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/error_hints.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/invites.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/launcher.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/preferences.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/quickstart.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/secrets.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/self_update.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/supervisor.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode/upload.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/cli.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/compat.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/invites.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/launcher.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/preferences.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/secrets.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/self_update.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/meshcode/upload.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/scripts/sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_core.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-backend-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/cli.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/compat.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/invites.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/launcher.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/preferences.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/secrets.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/self_update.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/meshcode/upload.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/scripts/sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_core.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/cli.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/compat.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/invites.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/launcher.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/preferences.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/secrets.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/self_update.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/meshcode/upload.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/scripts/sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_core.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -0
- /meshcode-2.10.93/meshcode.egg-info/dependency_links.txt → /meshcode-2.10.95/meshcode.egg-info/dependency_links 2.txt +0 -0
- /meshcode-2.10.93/meshcode.egg-info/entry_points.txt → /meshcode-2.10.95/meshcode.egg-info/entry_points 2.txt +0 -0
- /meshcode-2.10.93/meshcode.egg-info/requires.txt → /meshcode-2.10.95/meshcode.egg-info/requires 2.txt +0 -0
- /meshcode-2.10.93/meshcode.egg-info/top_level.txt → /meshcode-2.10.95/meshcode.egg-info/top_level 2.txt +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/setup.cfg +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_core.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.95}/tests/test_status_enum_coverage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcode
|
|
3
|
-
Version: 2.10.
|
|
3
|
+
Version: 2.10.95
|
|
4
4
|
Summary: Real-time communication between AI agents — Supabase-backed CLI
|
|
5
5
|
Author-email: MeshCode <hello@meshcode.io>
|
|
6
6
|
License: MIT
|
|
@@ -422,6 +422,11 @@ $env:MESHCODE_PROJECT_ID="your-project-uuid" # Windows PowerShell
|
|
|
422
422
|
|
|
423
423
|
You can also run `meshcode doctor` (v2.10.41+) to diagnose stale paths, missing dependencies, and config issues across all your workspaces.
|
|
424
424
|
|
|
425
|
+
**9b. Windows: first relaunch shows the old version after a PyPI upgrade**
|
|
426
|
+
Auto-update on Unix uses `os.execv` to swap the running Python image in-place — one launch, latest code (v2.10.93+). On Windows this isn't possible: an `os.execv` replacement breaks Claude Code's stdio pipe to the MCP child (the new process has a different PID). Meshcode therefore installs the new version but defers the swap to the **next** launch on Windows. Expect: `[meshcode] note: Windows defers auto-update to next launch ...` on stderr, then close and re-open Claude Code once to get the new code. To force-load now, run `pip install --upgrade meshcode` manually before launching Claude Code.
|
|
427
|
+
|
|
428
|
+
If the dashboard tags an agent as having `boot_version_drift`, that means pip wrote the new version to disk but our running process is still the old one — typically benign on Windows (next launch fixes it) and worth investigating on Unix (path-precedence regression).
|
|
429
|
+
|
|
425
430
|
**10. `MCP server failed to start` in the Claude Code `/mcp` panel**
|
|
426
431
|
Run `claude --debug` to see the underlying error. Nine times out of ten it's a stale or missing key — run `meshcode login mc_xxx` again.
|
|
427
432
|
|
|
@@ -396,6 +396,11 @@ $env:MESHCODE_PROJECT_ID="your-project-uuid" # Windows PowerShell
|
|
|
396
396
|
|
|
397
397
|
You can also run `meshcode doctor` (v2.10.41+) to diagnose stale paths, missing dependencies, and config issues across all your workspaces.
|
|
398
398
|
|
|
399
|
+
**9b. Windows: first relaunch shows the old version after a PyPI upgrade**
|
|
400
|
+
Auto-update on Unix uses `os.execv` to swap the running Python image in-place — one launch, latest code (v2.10.93+). On Windows this isn't possible: an `os.execv` replacement breaks Claude Code's stdio pipe to the MCP child (the new process has a different PID). Meshcode therefore installs the new version but defers the swap to the **next** launch on Windows. Expect: `[meshcode] note: Windows defers auto-update to next launch ...` on stderr, then close and re-open Claude Code once to get the new code. To force-load now, run `pip install --upgrade meshcode` manually before launching Claude Code.
|
|
401
|
+
|
|
402
|
+
If the dashboard tags an agent as having `boot_version_drift`, that means pip wrote the new version to disk but our running process is still the old one — typically benign on Windows (next launch fixes it) and worth investigating on Unix (path-precedence regression).
|
|
403
|
+
|
|
399
404
|
**10. `MCP server failed to start` in the Claude Code `/mcp` panel**
|
|
400
405
|
Run `claude --debug` to see the underlying error. Nine times out of ten it's a stale or missing key — run `meshcode login mc_xxx` again.
|
|
401
406
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""MeshCode — Real-time communication between AI agents."""
|
|
2
|
+
__version__ = "2.10.95"
|
|
3
|
+
|
|
4
|
+
# Exception hierarchy — eagerly imported (lightweight, no deps)
|
|
5
|
+
from meshcode.exceptions import ( # noqa: F401
|
|
6
|
+
MeshCodeError,
|
|
7
|
+
AuthError,
|
|
8
|
+
RPCError,
|
|
9
|
+
MeshCodeTimeoutError,
|
|
10
|
+
MeshCodeConnectionError,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# Public API — lazy imports to avoid heavy deps at import time
|
|
14
|
+
def __getattr__(name):
|
|
15
|
+
if name == "backend":
|
|
16
|
+
from meshcode.meshcode_mcp import backend
|
|
17
|
+
return backend
|
|
18
|
+
if name in _BACKEND_EXPORTS:
|
|
19
|
+
from meshcode.meshcode_mcp import backend
|
|
20
|
+
return getattr(backend, name)
|
|
21
|
+
if name in _SECRETS_EXPORTS:
|
|
22
|
+
from meshcode import secrets
|
|
23
|
+
return getattr(secrets, name)
|
|
24
|
+
raise AttributeError(f"module 'meshcode' has no attribute {name!r}")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Backend: core messaging & agent management
|
|
28
|
+
_BACKEND_EXPORTS = {
|
|
29
|
+
"send_message",
|
|
30
|
+
"read_inbox",
|
|
31
|
+
"count_pending",
|
|
32
|
+
"get_board",
|
|
33
|
+
"heartbeat",
|
|
34
|
+
"set_status",
|
|
35
|
+
"register_agent",
|
|
36
|
+
"get_project_id",
|
|
37
|
+
"sb_rpc",
|
|
38
|
+
"task_create",
|
|
39
|
+
"task_list",
|
|
40
|
+
"encrypt_payload",
|
|
41
|
+
"decrypt_payload",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Secrets: credential management
|
|
45
|
+
_SECRETS_EXPORTS = {
|
|
46
|
+
"get_api_key",
|
|
47
|
+
"set_api_key",
|
|
48
|
+
"list_profiles",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"__version__",
|
|
53
|
+
"backend",
|
|
54
|
+
# Exceptions
|
|
55
|
+
"MeshCodeError",
|
|
56
|
+
"AuthError",
|
|
57
|
+
"RPCError",
|
|
58
|
+
"MeshCodeTimeoutError",
|
|
59
|
+
"MeshCodeConnectionError",
|
|
60
|
+
# Messaging
|
|
61
|
+
"send_message",
|
|
62
|
+
"read_inbox",
|
|
63
|
+
"count_pending",
|
|
64
|
+
# Agent management
|
|
65
|
+
"register_agent",
|
|
66
|
+
"get_project_id",
|
|
67
|
+
"get_board",
|
|
68
|
+
"heartbeat",
|
|
69
|
+
"set_status",
|
|
70
|
+
# Tasks
|
|
71
|
+
"task_create",
|
|
72
|
+
"task_list",
|
|
73
|
+
# Low-level
|
|
74
|
+
"sb_rpc",
|
|
75
|
+
# Encryption
|
|
76
|
+
"encrypt_payload",
|
|
77
|
+
"decrypt_payload",
|
|
78
|
+
# Credentials
|
|
79
|
+
"get_api_key",
|
|
80
|
+
"set_api_key",
|
|
81
|
+
"list_profiles",
|
|
82
|
+
]
|
|
@@ -826,14 +826,29 @@ def read_inbox(project_id: str, agent: str, mark_read: bool = True, api_key: Opt
|
|
|
826
826
|
for m in messages:
|
|
827
827
|
p = m.get("payload")
|
|
828
828
|
if isinstance(p, dict) and "_encrypted" in p:
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
829
|
+
msg_aad = p.get("_aad", project_id)
|
|
830
|
+
from_project_marker = p.get("_from_project")
|
|
831
|
+
decrypted = None
|
|
832
|
+
# Cross-mesh: payload tagged with sender's project.
|
|
833
|
+
# Fetch per-link shared key (NOT local mesh key).
|
|
834
|
+
if from_project_marker and from_project_marker != project_id:
|
|
835
|
+
link_key = get_link_shared_key(api_key, project_id, from_project_marker)
|
|
836
|
+
if link_key:
|
|
837
|
+
decrypted = decrypt_payload(p["_encrypted"], link_key, aad=msg_aad)
|
|
838
|
+
# Decrypt fail → key rotation; refetch once.
|
|
839
|
+
if decrypted is None:
|
|
840
|
+
link_key = get_link_shared_key(api_key, project_id, from_project_marker, bypass_cache=True)
|
|
841
|
+
if link_key:
|
|
842
|
+
decrypted = decrypt_payload(p["_encrypted"], link_key, aad=msg_aad)
|
|
843
|
+
# In-mesh OR no _from_project marker (legacy): local key.
|
|
844
|
+
if decrypted is None:
|
|
845
|
+
if mesh_key is None:
|
|
846
|
+
mesh_key = get_mesh_key(api_key, project_id)
|
|
847
|
+
if mesh_key:
|
|
848
|
+
decrypted = decrypt_payload(p["_encrypted"], mesh_key, aad=msg_aad)
|
|
849
|
+
if decrypted is not None:
|
|
850
|
+
m["payload"] = decrypted
|
|
851
|
+
m["_was_encrypted"] = True
|
|
837
852
|
if mark_read and messages:
|
|
838
853
|
import datetime as _dt
|
|
839
854
|
now = _dt.datetime.now(_dt.timezone.utc)
|
|
@@ -872,14 +887,25 @@ def read_inbox(project_id: str, agent: str, mark_read: bool = True, api_key: Opt
|
|
|
872
887
|
for m in messages:
|
|
873
888
|
p = m.get("payload")
|
|
874
889
|
if isinstance(p, dict) and "_encrypted" in p:
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
if
|
|
881
|
-
|
|
882
|
-
|
|
890
|
+
msg_aad = p.get("_aad", project_id)
|
|
891
|
+
from_project_marker = p.get("_from_project")
|
|
892
|
+
decrypted = None
|
|
893
|
+
if from_project_marker and from_project_marker != project_id:
|
|
894
|
+
link_key = get_link_shared_key(api_key, project_id, from_project_marker)
|
|
895
|
+
if link_key:
|
|
896
|
+
decrypted = decrypt_payload(p["_encrypted"], link_key, aad=msg_aad)
|
|
897
|
+
if decrypted is None:
|
|
898
|
+
link_key = get_link_shared_key(api_key, project_id, from_project_marker, bypass_cache=True)
|
|
899
|
+
if link_key:
|
|
900
|
+
decrypted = decrypt_payload(p["_encrypted"], link_key, aad=msg_aad)
|
|
901
|
+
if decrypted is None:
|
|
902
|
+
if mesh_key is None:
|
|
903
|
+
mesh_key = get_mesh_key(api_key, project_id)
|
|
904
|
+
if mesh_key:
|
|
905
|
+
decrypted = decrypt_payload(p["_encrypted"], mesh_key, aad=msg_aad)
|
|
906
|
+
if decrypted is not None:
|
|
907
|
+
m["payload"] = decrypted
|
|
908
|
+
m["_was_encrypted"] = True
|
|
883
909
|
if mark_read and messages:
|
|
884
910
|
for m in messages:
|
|
885
911
|
marked = False
|
|
@@ -983,6 +1009,56 @@ def get_mesh_key(api_key: str, project_id: str) -> Optional[str]:
|
|
|
983
1009
|
return None
|
|
984
1010
|
|
|
985
1011
|
|
|
1012
|
+
# Per-link shared key cache (cross-mesh encrypted msgs). Keyed by
|
|
1013
|
+
# (my_project, other_project) tuple. Populated on demand when an encrypted
|
|
1014
|
+
# cross-mesh message arrives. Cache invalidates on decrypt failure (rotation).
|
|
1015
|
+
_link_key_cache: Dict[tuple, tuple] = {} # (my_proj, other_proj) -> (hex_key, expiry_ts)
|
|
1016
|
+
_LINK_KEY_CACHE_TTL = 600
|
|
1017
|
+
_LINK_KEY_CACHE_MAX = 64
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
def get_link_shared_key(api_key: str, my_project: str, other_project: str,
|
|
1021
|
+
bypass_cache: bool = False) -> Optional[str]:
|
|
1022
|
+
"""Retrieve the per-link symmetric encryption key (hex-encoded AES-256).
|
|
1023
|
+
|
|
1024
|
+
Used for cross-mesh encrypted messages — backend `mc_get_link_shared_key`
|
|
1025
|
+
returns a key SHARED by both ends of the link, so encrypt-on-sender +
|
|
1026
|
+
decrypt-on-recipient roundtrips cleanly. `my_project` and `other_project`
|
|
1027
|
+
accept either project name OR project_id; the RPC resolves both.
|
|
1028
|
+
|
|
1029
|
+
Set `bypass_cache=True` after a decrypt failure to handle key rotation
|
|
1030
|
+
(`mc_rotate_link_shared_key`).
|
|
1031
|
+
"""
|
|
1032
|
+
cache_key = (my_project, other_project)
|
|
1033
|
+
if not bypass_cache:
|
|
1034
|
+
entry = _link_key_cache.get(cache_key)
|
|
1035
|
+
if entry and entry[1] > _time.time():
|
|
1036
|
+
return entry[0]
|
|
1037
|
+
_link_key_cache.pop(cache_key, None)
|
|
1038
|
+
result = sb_rpc("mc_get_link_shared_key", {
|
|
1039
|
+
"p_api_key": api_key,
|
|
1040
|
+
"p_my_project": my_project,
|
|
1041
|
+
"p_other_project": other_project,
|
|
1042
|
+
})
|
|
1043
|
+
if isinstance(result, dict) and result.get("ok"):
|
|
1044
|
+
try:
|
|
1045
|
+
import base64 as _b64
|
|
1046
|
+
hex_key = _b64.b64decode(result["shared_encryption_key_b64"]).hex()
|
|
1047
|
+
except Exception:
|
|
1048
|
+
return None
|
|
1049
|
+
if len(_link_key_cache) >= _LINK_KEY_CACHE_MAX:
|
|
1050
|
+
now = _time.time()
|
|
1051
|
+
expired = [k for k, v in _link_key_cache.items() if v[1] <= now]
|
|
1052
|
+
for k in expired:
|
|
1053
|
+
del _link_key_cache[k]
|
|
1054
|
+
if len(_link_key_cache) >= _LINK_KEY_CACHE_MAX:
|
|
1055
|
+
oldest = min(_link_key_cache, key=lambda k: _link_key_cache[k][1])
|
|
1056
|
+
del _link_key_cache[oldest]
|
|
1057
|
+
_link_key_cache[cache_key] = (hex_key, _time.time() + _LINK_KEY_CACHE_TTL)
|
|
1058
|
+
return hex_key
|
|
1059
|
+
return None
|
|
1060
|
+
|
|
1061
|
+
|
|
986
1062
|
def encrypt_payload(payload: Dict, hex_key: str, aad: Optional[str] = None) -> str:
|
|
987
1063
|
"""Encrypt a JSON payload using AES-256-GCM with optional AAD.
|
|
988
1064
|
|
|
@@ -1747,6 +1747,25 @@ async def lifespan(_app):
|
|
|
1747
1747
|
be.set_status(_PROJECT_ID, AGENT_NAME, "idle", "MCP session active", api_key=_get_api_key())
|
|
1748
1748
|
log.info(f"[meshcode] Agent {AGENT_NAME} online — initial heartbeat sent")
|
|
1749
1749
|
_log_activity_bg("agent_online", f"{AGENT_NAME} came online")
|
|
1750
|
+
# Soft-relay any boot_version_drift detected at startup. One-shot:
|
|
1751
|
+
# consume the env-var sentinel set in run_server() so we don't
|
|
1752
|
+
# spam the commander on every retry attempt or future heartbeat.
|
|
1753
|
+
_drift = os.environ.pop("_MESHCODE_BOOT_DRIFT", None)
|
|
1754
|
+
if _drift:
|
|
1755
|
+
try:
|
|
1756
|
+
be.send_message(
|
|
1757
|
+
_PROJECT_ID, AGENT_NAME, "mesh-commander",
|
|
1758
|
+
{
|
|
1759
|
+
"type": "boot_version_drift",
|
|
1760
|
+
"drift": _drift,
|
|
1761
|
+
"agent": AGENT_NAME,
|
|
1762
|
+
"platform": sys.platform,
|
|
1763
|
+
},
|
|
1764
|
+
msg_type="report",
|
|
1765
|
+
api_key=_get_api_key(),
|
|
1766
|
+
)
|
|
1767
|
+
except Exception as _drift_e:
|
|
1768
|
+
log.debug(f"boot_version_drift relay failed: {_drift_e}")
|
|
1750
1769
|
break
|
|
1751
1770
|
except Exception as e:
|
|
1752
1771
|
log.warning(f"initial heartbeat attempt {_attempt+1} failed: {e}")
|
|
@@ -1987,23 +2006,36 @@ def meshcode_send(to: str, message: Any, in_reply_to: Optional[str] = None,
|
|
|
1987
2006
|
return {"error": "sensitive messages must use encrypted=True for cross-mesh"}
|
|
1988
2007
|
api_key = _get_api_key()
|
|
1989
2008
|
|
|
1990
|
-
#
|
|
1991
|
-
#
|
|
1992
|
-
#
|
|
2009
|
+
# Cross-mesh encryption: per-link symmetric AES-256-GCM key (backend
|
|
2010
|
+
# commit cc6a988 / mig 2026-05-04). Old `mc_get_cross_mesh_key`
|
|
2011
|
+
# returned target's per-mesh key, which empirically failed to decrypt
|
|
2012
|
+
# on recipient (incident 2026-05-04 chief@mesh-dev). New
|
|
2013
|
+
# `mc_get_link_shared_key` returns a key SHARED by sender + recipient
|
|
2014
|
+
# via the link record — both sides resolve to the same bytes, so
|
|
2015
|
+
# encryption roundtrips cleanly. Tag payload with _from_project so the
|
|
2016
|
+
# recipient knows to fetch the link key (not its local mesh key) on
|
|
2017
|
+
# decrypt.
|
|
1993
2018
|
if encrypted:
|
|
1994
|
-
key_result = be.sb_rpc("
|
|
2019
|
+
key_result = be.sb_rpc("mc_get_link_shared_key", {
|
|
1995
2020
|
"p_api_key": api_key,
|
|
1996
|
-
"
|
|
1997
|
-
"
|
|
1998
|
-
"p_agent_name": AGENT_NAME,
|
|
2021
|
+
"p_my_project": PROJECT_NAME,
|
|
2022
|
+
"p_other_project": target_meshwork,
|
|
1999
2023
|
})
|
|
2000
2024
|
if not isinstance(key_result, dict) or not key_result.get("ok"):
|
|
2001
|
-
err = key_result.get("error", "unknown") if isinstance(key_result, dict) else "RPC failed"
|
|
2025
|
+
err = key_result.get("error_code") or key_result.get("error", "unknown") if isinstance(key_result, dict) else "RPC failed"
|
|
2002
2026
|
return {"error": f"cross-mesh encryption failed: {err}"}
|
|
2003
|
-
|
|
2027
|
+
link_key_b64 = key_result["shared_encryption_key_b64"]
|
|
2028
|
+
link_id = key_result.get("link_id", "")
|
|
2004
2029
|
tgt_project_id = key_result.get("target_project_id", "")
|
|
2005
|
-
|
|
2006
|
-
|
|
2030
|
+
import base64 as _b64
|
|
2031
|
+
link_key_hex = _b64.b64decode(link_key_b64).hex()
|
|
2032
|
+
encrypted_data = be.encrypt_payload(payload, link_key_hex, aad=link_id or tgt_project_id)
|
|
2033
|
+
payload = {
|
|
2034
|
+
"_encrypted": encrypted_data,
|
|
2035
|
+
"_aad": link_id or tgt_project_id,
|
|
2036
|
+
"_from_project": PROJECT_NAME,
|
|
2037
|
+
"_link_id": link_id,
|
|
2038
|
+
}
|
|
2007
2039
|
|
|
2008
2040
|
result = be.sb_rpc("mc_send_cross_mesh", {
|
|
2009
2041
|
"p_api_key": api_key,
|
|
@@ -2669,20 +2701,40 @@ def _mark_realtime_msgs_read_in_db(messages: List[Dict[str, Any]]) -> None:
|
|
|
2669
2701
|
async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[str, Any]:
|
|
2670
2702
|
|
|
2671
2703
|
def _return_from_buffered(buffered: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
|
2672
|
-
# Auto-decrypt encrypted payloads
|
|
2704
|
+
# Auto-decrypt encrypted payloads. Cross-mesh msgs carry _from_project
|
|
2705
|
+
# marker → use per-link shared key. In-mesh → local mesh key.
|
|
2673
2706
|
_mesh_key = None
|
|
2707
|
+
_api_key = _get_api_key()
|
|
2674
2708
|
for msg in buffered:
|
|
2675
2709
|
p = msg.get("payload")
|
|
2676
2710
|
if isinstance(p, dict) and "_encrypted" in p:
|
|
2677
|
-
|
|
2711
|
+
_msg_aad = p.get("_aad", PROJECT_NAME)
|
|
2712
|
+
_from_proj = p.get("_from_project")
|
|
2713
|
+
_decrypted = None
|
|
2714
|
+
if _from_proj and _from_proj != PROJECT_NAME:
|
|
2678
2715
|
try:
|
|
2679
|
-
|
|
2716
|
+
_link_key = be.get_link_shared_key(_api_key, PROJECT_NAME, _from_proj)
|
|
2717
|
+
if _link_key:
|
|
2718
|
+
_decrypted = be.decrypt_payload(p["_encrypted"], _link_key, aad=_msg_aad)
|
|
2719
|
+
if _decrypted is None:
|
|
2720
|
+
_link_key = be.get_link_shared_key(_api_key, PROJECT_NAME, _from_proj, bypass_cache=True)
|
|
2721
|
+
if _link_key:
|
|
2722
|
+
_decrypted = be.decrypt_payload(p["_encrypted"], _link_key, aad=_msg_aad)
|
|
2680
2723
|
except Exception:
|
|
2681
|
-
|
|
2682
|
-
if
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2724
|
+
pass
|
|
2725
|
+
if _decrypted is None:
|
|
2726
|
+
if _mesh_key is None:
|
|
2727
|
+
try:
|
|
2728
|
+
_mesh_key = be.get_mesh_key(_api_key, _PROJECT_ID) or ""
|
|
2729
|
+
except Exception:
|
|
2730
|
+
_mesh_key = ""
|
|
2731
|
+
if _mesh_key:
|
|
2732
|
+
try:
|
|
2733
|
+
_decrypted = be.decrypt_payload(p["_encrypted"], _mesh_key, aad=_msg_aad)
|
|
2734
|
+
except Exception:
|
|
2735
|
+
_decrypted = None
|
|
2736
|
+
if _decrypted is not None:
|
|
2737
|
+
msg["payload"] = _decrypted
|
|
2686
2738
|
deduped = _filter_and_mark(buffered)
|
|
2687
2739
|
if not deduped:
|
|
2688
2740
|
return None
|
|
@@ -4305,6 +4357,12 @@ def _auto_update() -> None:
|
|
|
4305
4357
|
log.debug("[meshcode] Auto-update disabled (MESHCODE_AUTO_UPDATE=0)")
|
|
4306
4358
|
return
|
|
4307
4359
|
if os.environ.get("MESHCODE_UPDATED") == "1":
|
|
4360
|
+
# Sentinel was set by the parent process right before it execv'd
|
|
4361
|
+
# into us. Clear it now — single-use per upgrade. Without this, any
|
|
4362
|
+
# subprocess we spawn (or a downstream re-exec triggered by some
|
|
4363
|
+
# other code path) inherits MESHCODE_UPDATED=1 and skips the next
|
|
4364
|
+
# legitimate upgrade check. Per task 8d5eed7a.
|
|
4365
|
+
os.environ.pop("MESHCODE_UPDATED", None)
|
|
4308
4366
|
return
|
|
4309
4367
|
|
|
4310
4368
|
import subprocess
|
|
@@ -4389,9 +4447,56 @@ def _auto_update() -> None:
|
|
|
4389
4447
|
log.debug(f"[meshcode] Re-exec failed: {e}, continuing with old version")
|
|
4390
4448
|
|
|
4391
4449
|
|
|
4450
|
+
def _check_boot_version_drift():
|
|
4451
|
+
"""Compare pip-installed version (on disk) vs imported __version__.
|
|
4452
|
+
|
|
4453
|
+
Returns (installed, loaded) tuple if they differ, else None. Used as a
|
|
4454
|
+
canary for "auto-update wrote new code to disk but our running process
|
|
4455
|
+
is still the old version" — should be unreachable on Unix after the
|
|
4456
|
+
2.10.93 execv fix, but the empirical check guards against:
|
|
4457
|
+
- path-precedence regressions (multiple meshcode installs)
|
|
4458
|
+
- Windows MCP mode (we deliberately defer there)
|
|
4459
|
+
- execv failures that fall through to the old version
|
|
4460
|
+
Best-effort: must not raise.
|
|
4461
|
+
"""
|
|
4462
|
+
try:
|
|
4463
|
+
from importlib.metadata import version as _md_version
|
|
4464
|
+
from meshcode import __version__ as _loaded
|
|
4465
|
+
_installed = _md_version("meshcode")
|
|
4466
|
+
if _installed != _loaded:
|
|
4467
|
+
return (_installed, _loaded)
|
|
4468
|
+
except Exception:
|
|
4469
|
+
pass
|
|
4470
|
+
return None
|
|
4471
|
+
|
|
4472
|
+
|
|
4392
4473
|
def run_server():
|
|
4393
4474
|
"""Start the MCP server on stdio (default for Claude Code)."""
|
|
4394
4475
|
_auto_update()
|
|
4476
|
+
# Telemetry canary: surface any installed/loaded version drift loudly
|
|
4477
|
+
# to stderr so it shows up in Claude Code's MCP server output. The first
|
|
4478
|
+
# heartbeat carries the loaded version to mc_agents; this stderr line is
|
|
4479
|
+
# the human-visible signal that auto-update did NOT land cleanly.
|
|
4480
|
+
_drift = _check_boot_version_drift()
|
|
4481
|
+
if _drift is not None:
|
|
4482
|
+
_installed, _loaded = _drift
|
|
4483
|
+
print(
|
|
4484
|
+
f"[meshcode] WARN boot_version_drift: installed={_installed} loaded={_loaded} "
|
|
4485
|
+
f"— pip has a newer build on disk than what's running in this process. "
|
|
4486
|
+
f"Restart should pick it up. If it persists, check sys.path precedence.",
|
|
4487
|
+
file=sys.stderr,
|
|
4488
|
+
)
|
|
4489
|
+
# Stash for the heartbeat thread to relay to commander on first beat.
|
|
4490
|
+
os.environ["_MESHCODE_BOOT_DRIFT"] = f"{_installed}->{_loaded}"
|
|
4491
|
+
if sys.platform == "win32":
|
|
4492
|
+
# Windows can't execv-preserve stdio; auto-update defers to next boot.
|
|
4493
|
+
# Always announce so Windows users know why their first launch may
|
|
4494
|
+
# still show an older version even with PyPI updated.
|
|
4495
|
+
print(
|
|
4496
|
+
"[meshcode] note: Windows defers auto-update to next launch "
|
|
4497
|
+
"(execv preserves Claude Code's stdio pipe only on Unix).",
|
|
4498
|
+
file=sys.stderr,
|
|
4499
|
+
)
|
|
4395
4500
|
print(
|
|
4396
4501
|
f"[meshcode-mcp] Starting server for {AGENT_NAME}@{PROJECT_NAME}",
|
|
4397
4502
|
file=sys.stderr,
|