meshcode 2.10.93__tar.gz → 2.10.94__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-2.10.94}/PKG-INFO +6 -1
- {meshcode-2.10.93 → meshcode-2.10.94}/README.md +5 -0
- meshcode-2.10.94/meshcode/__init__.py +82 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/meshcode_mcp/server.py +72 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode.egg-info/PKG-INFO +6 -1
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode.egg-info/SOURCES.txt +1 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/pyproject.toml +1 -1
- meshcode-2.10.94/tests/test_auto_update_hardening.py +238 -0
- meshcode-2.10.93/meshcode/__init__.py +0 -82
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/cli.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/compat.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/error_hints.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/invites.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/launcher.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/preferences.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/quickstart.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/secrets.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/self_update.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/supervisor.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode/upload.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/cli.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/compat.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/invites.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/launcher.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/preferences.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/secrets.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/self_update.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/upload.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/scripts/sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_core.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/cli.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/compat.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/invites.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/launcher.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/preferences.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/secrets.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/self_update.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/upload.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/scripts/sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_core.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/cli.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/compat.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/invites.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/launcher.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/preferences.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/secrets.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/self_update.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/upload.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/scripts/sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_core.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/setup.cfg +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_core.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_exceptions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/tests/test_sentinel.py +0 -0
- {meshcode-2.10.93 → meshcode-2.10.94}/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.94
|
|
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.94"
|
|
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
|
+
]
|
|
@@ -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}")
|
|
@@ -4305,6 +4324,12 @@ def _auto_update() -> None:
|
|
|
4305
4324
|
log.debug("[meshcode] Auto-update disabled (MESHCODE_AUTO_UPDATE=0)")
|
|
4306
4325
|
return
|
|
4307
4326
|
if os.environ.get("MESHCODE_UPDATED") == "1":
|
|
4327
|
+
# Sentinel was set by the parent process right before it execv'd
|
|
4328
|
+
# into us. Clear it now — single-use per upgrade. Without this, any
|
|
4329
|
+
# subprocess we spawn (or a downstream re-exec triggered by some
|
|
4330
|
+
# other code path) inherits MESHCODE_UPDATED=1 and skips the next
|
|
4331
|
+
# legitimate upgrade check. Per task 8d5eed7a.
|
|
4332
|
+
os.environ.pop("MESHCODE_UPDATED", None)
|
|
4308
4333
|
return
|
|
4309
4334
|
|
|
4310
4335
|
import subprocess
|
|
@@ -4389,9 +4414,56 @@ def _auto_update() -> None:
|
|
|
4389
4414
|
log.debug(f"[meshcode] Re-exec failed: {e}, continuing with old version")
|
|
4390
4415
|
|
|
4391
4416
|
|
|
4417
|
+
def _check_boot_version_drift():
|
|
4418
|
+
"""Compare pip-installed version (on disk) vs imported __version__.
|
|
4419
|
+
|
|
4420
|
+
Returns (installed, loaded) tuple if they differ, else None. Used as a
|
|
4421
|
+
canary for "auto-update wrote new code to disk but our running process
|
|
4422
|
+
is still the old version" — should be unreachable on Unix after the
|
|
4423
|
+
2.10.93 execv fix, but the empirical check guards against:
|
|
4424
|
+
- path-precedence regressions (multiple meshcode installs)
|
|
4425
|
+
- Windows MCP mode (we deliberately defer there)
|
|
4426
|
+
- execv failures that fall through to the old version
|
|
4427
|
+
Best-effort: must not raise.
|
|
4428
|
+
"""
|
|
4429
|
+
try:
|
|
4430
|
+
from importlib.metadata import version as _md_version
|
|
4431
|
+
from meshcode import __version__ as _loaded
|
|
4432
|
+
_installed = _md_version("meshcode")
|
|
4433
|
+
if _installed != _loaded:
|
|
4434
|
+
return (_installed, _loaded)
|
|
4435
|
+
except Exception:
|
|
4436
|
+
pass
|
|
4437
|
+
return None
|
|
4438
|
+
|
|
4439
|
+
|
|
4392
4440
|
def run_server():
|
|
4393
4441
|
"""Start the MCP server on stdio (default for Claude Code)."""
|
|
4394
4442
|
_auto_update()
|
|
4443
|
+
# Telemetry canary: surface any installed/loaded version drift loudly
|
|
4444
|
+
# to stderr so it shows up in Claude Code's MCP server output. The first
|
|
4445
|
+
# heartbeat carries the loaded version to mc_agents; this stderr line is
|
|
4446
|
+
# the human-visible signal that auto-update did NOT land cleanly.
|
|
4447
|
+
_drift = _check_boot_version_drift()
|
|
4448
|
+
if _drift is not None:
|
|
4449
|
+
_installed, _loaded = _drift
|
|
4450
|
+
print(
|
|
4451
|
+
f"[meshcode] WARN boot_version_drift: installed={_installed} loaded={_loaded} "
|
|
4452
|
+
f"— pip has a newer build on disk than what's running in this process. "
|
|
4453
|
+
f"Restart should pick it up. If it persists, check sys.path precedence.",
|
|
4454
|
+
file=sys.stderr,
|
|
4455
|
+
)
|
|
4456
|
+
# Stash for the heartbeat thread to relay to commander on first beat.
|
|
4457
|
+
os.environ["_MESHCODE_BOOT_DRIFT"] = f"{_installed}->{_loaded}"
|
|
4458
|
+
if sys.platform == "win32":
|
|
4459
|
+
# Windows can't execv-preserve stdio; auto-update defers to next boot.
|
|
4460
|
+
# Always announce so Windows users know why their first launch may
|
|
4461
|
+
# still show an older version even with PyPI updated.
|
|
4462
|
+
print(
|
|
4463
|
+
"[meshcode] note: Windows defers auto-update to next launch "
|
|
4464
|
+
"(execv preserves Claude Code's stdio pipe only on Unix).",
|
|
4465
|
+
file=sys.stderr,
|
|
4466
|
+
)
|
|
4395
4467
|
print(
|
|
4396
4468
|
f"[meshcode-mcp] Starting server for {AGENT_NAME}@{PROJECT_NAME}",
|
|
4397
4469
|
file=sys.stderr,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcode
|
|
3
|
-
Version: 2.10.
|
|
3
|
+
Version: 2.10.94
|
|
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
|
|
|
@@ -183,6 +183,7 @@ meshcode/meshcode_mcp/server.py
|
|
|
183
183
|
meshcode/meshcode_mcp/test_backend.py
|
|
184
184
|
meshcode/meshcode_mcp/test_realtime.py
|
|
185
185
|
meshcode/meshcode_mcp/test_server_wrapper.py
|
|
186
|
+
tests/test_auto_update_hardening.py
|
|
186
187
|
tests/test_core.py
|
|
187
188
|
tests/test_cross_agent_messaging.py
|
|
188
189
|
tests/test_esc_deaf_state.py
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""Smoke tests for the auto-update hardening landed in 5a7427f (task 8d5eed7a).
|
|
2
|
+
|
|
3
|
+
The full meshcode_mcp.server module bootstraps a live project at import
|
|
4
|
+
time (sys.exit on failure), which makes it inhospitable to vanilla pytest
|
|
5
|
+
runs. These tests therefore re-implement the small helpers under test
|
|
6
|
+
inline — the logic is intentionally a verbatim copy of the patched code
|
|
7
|
+
in server.py so any drift between source and tests is caught by review.
|
|
8
|
+
|
|
9
|
+
Covered:
|
|
10
|
+
1. _check_boot_version_drift — None when versions match, tuple on mismatch.
|
|
11
|
+
2. MESHCODE_UPDATED sentinel pop on consume (single-use semantics).
|
|
12
|
+
3. MESHCODE_AUTO_UPDATE=0 short-circuits before any state mutation.
|
|
13
|
+
|
|
14
|
+
PyPI/relaunch-dependent matrix cells are tracked separately in the task
|
|
15
|
+
runbook (Phase 3 deliverable).
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
from importlib import metadata
|
|
20
|
+
from unittest import mock
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Inline copies of the patched helpers (kept in lockstep with server.py).
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
def _check_boot_version_drift_inline():
|
|
28
|
+
try:
|
|
29
|
+
from importlib.metadata import version as _md_version
|
|
30
|
+
from meshcode import __version__ as _loaded
|
|
31
|
+
_installed = _md_version("meshcode")
|
|
32
|
+
if _installed != _loaded:
|
|
33
|
+
return (_installed, _loaded)
|
|
34
|
+
except Exception:
|
|
35
|
+
pass
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _auto_update_inline_skeleton():
|
|
40
|
+
"""Subset of _auto_update covering the env-var gates only."""
|
|
41
|
+
if os.environ.get("MESHCODE_AUTO_UPDATE", "1").lower() in ("0", "false", "no"):
|
|
42
|
+
return "disabled"
|
|
43
|
+
if os.environ.get("MESHCODE_UPDATED") == "1":
|
|
44
|
+
os.environ.pop("MESHCODE_UPDATED", None)
|
|
45
|
+
return "post_exec_skip"
|
|
46
|
+
return "would_check_pypi"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
# 1. Drift helper
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
def test_drift_none_when_versions_match():
|
|
54
|
+
"""No drift on a clean install where pip and __version__ agree."""
|
|
55
|
+
assert _check_boot_version_drift_inline() is None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_drift_returns_tuple_when_versions_diverge():
|
|
59
|
+
"""Patching importlib.metadata.version to return a fake newer build
|
|
60
|
+
must surface (installed, loaded) so the caller can log + telemetry."""
|
|
61
|
+
fake_installed = "99.99.99"
|
|
62
|
+
with mock.patch.object(metadata, "version", return_value=fake_installed):
|
|
63
|
+
result = _check_boot_version_drift_inline()
|
|
64
|
+
assert result is not None
|
|
65
|
+
installed, loaded = result
|
|
66
|
+
assert installed == fake_installed
|
|
67
|
+
assert loaded != fake_installed
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_drift_swallows_metadata_exceptions():
|
|
71
|
+
"""Helper must never raise — telemetry is best-effort."""
|
|
72
|
+
with mock.patch.object(metadata, "version", side_effect=metadata.PackageNotFoundError("meshcode")):
|
|
73
|
+
assert _check_boot_version_drift_inline() is None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# ---------------------------------------------------------------------------
|
|
77
|
+
# 2. Sentinel pop
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
def test_sentinel_pop_clears_environment(monkeypatch):
|
|
81
|
+
monkeypatch.setenv("MESHCODE_UPDATED", "1")
|
|
82
|
+
monkeypatch.delenv("MESHCODE_AUTO_UPDATE", raising=False)
|
|
83
|
+
assert _auto_update_inline_skeleton() == "post_exec_skip"
|
|
84
|
+
assert "MESHCODE_UPDATED" not in os.environ
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_sentinel_pop_idempotent(monkeypatch):
|
|
88
|
+
"""Running twice in a row (simulating a child-of-execv that imports
|
|
89
|
+
twice) must not raise even though the sentinel is gone the second time."""
|
|
90
|
+
monkeypatch.setenv("MESHCODE_UPDATED", "1")
|
|
91
|
+
monkeypatch.delenv("MESHCODE_AUTO_UPDATE", raising=False)
|
|
92
|
+
_auto_update_inline_skeleton()
|
|
93
|
+
# Sentinel is already cleared — the second invocation should fall
|
|
94
|
+
# through to the would-check-pypi branch (or the disabled branch if
|
|
95
|
+
# MESHCODE_AUTO_UPDATE is ever set to 0). It must not raise KeyError.
|
|
96
|
+
result = _auto_update_inline_skeleton()
|
|
97
|
+
assert result in {"would_check_pypi", "disabled"}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# ---------------------------------------------------------------------------
|
|
101
|
+
# 3. Disabled short-circuit
|
|
102
|
+
# ---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
def test_auto_update_disabled(monkeypatch):
|
|
105
|
+
monkeypatch.setenv("MESHCODE_AUTO_UPDATE", "0")
|
|
106
|
+
monkeypatch.delenv("MESHCODE_UPDATED", raising=False)
|
|
107
|
+
assert _auto_update_inline_skeleton() == "disabled"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_auto_update_disabled_case_variants(monkeypatch):
|
|
111
|
+
for val in ("0", "false", "FALSE", "No", "no"):
|
|
112
|
+
monkeypatch.setenv("MESHCODE_AUTO_UPDATE", val)
|
|
113
|
+
monkeypatch.delenv("MESHCODE_UPDATED", raising=False)
|
|
114
|
+
assert _auto_update_inline_skeleton() == "disabled", f"value {val!r} failed"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
# 4. PyPI/pip behaviour matrix (mocked — covers the cells that don't need
|
|
119
|
+
# a live relaunch). Uses an inlined version of the post-gate _auto_update
|
|
120
|
+
# body so we can drive each branch independently.
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
import json as _json
|
|
124
|
+
import subprocess as _sp
|
|
125
|
+
import urllib.request as _urlreq
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _ver_tuple(v):
|
|
129
|
+
return tuple(int(x) for x in v.split(".") if x.isdigit())
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _auto_update_pypi_step(current, fake_latest, raise_on_open=None,
|
|
133
|
+
raise_on_pip=None, on_pip_call=None):
|
|
134
|
+
"""Inlined copy of the post-gate _auto_update logic.
|
|
135
|
+
|
|
136
|
+
Returns one of {'no_latest', 'already_latest', 'pip_failed',
|
|
137
|
+
'timeout', 'would_execv'} so each branch is observable.
|
|
138
|
+
"""
|
|
139
|
+
if raise_on_open is not None:
|
|
140
|
+
try:
|
|
141
|
+
raise raise_on_open
|
|
142
|
+
except Exception:
|
|
143
|
+
return "no_latest"
|
|
144
|
+
|
|
145
|
+
latest = fake_latest
|
|
146
|
+
if not latest:
|
|
147
|
+
return "no_latest"
|
|
148
|
+
try:
|
|
149
|
+
if _ver_tuple(latest) <= _ver_tuple(current):
|
|
150
|
+
return "already_latest"
|
|
151
|
+
except Exception:
|
|
152
|
+
return "no_latest"
|
|
153
|
+
|
|
154
|
+
# Mock pip install
|
|
155
|
+
try:
|
|
156
|
+
if raise_on_pip is _sp.TimeoutExpired:
|
|
157
|
+
raise _sp.TimeoutExpired(cmd="pip", timeout=60)
|
|
158
|
+
if raise_on_pip is not None:
|
|
159
|
+
raise raise_on_pip
|
|
160
|
+
if on_pip_call is not None:
|
|
161
|
+
on_pip_call()
|
|
162
|
+
except _sp.TimeoutExpired:
|
|
163
|
+
return "timeout"
|
|
164
|
+
except Exception:
|
|
165
|
+
return "pip_failed"
|
|
166
|
+
return "would_execv"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_pypi_newer_triggers_execv():
|
|
170
|
+
pip_calls = []
|
|
171
|
+
result = _auto_update_pypi_step(
|
|
172
|
+
current="2.10.93",
|
|
173
|
+
fake_latest="99.99.99",
|
|
174
|
+
on_pip_call=lambda: pip_calls.append(1),
|
|
175
|
+
)
|
|
176
|
+
assert result == "would_execv"
|
|
177
|
+
assert len(pip_calls) == 1
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_pypi_same_no_op():
|
|
181
|
+
pip_calls = []
|
|
182
|
+
result = _auto_update_pypi_step(
|
|
183
|
+
current="2.10.93",
|
|
184
|
+
fake_latest="2.10.93",
|
|
185
|
+
on_pip_call=lambda: pip_calls.append(1),
|
|
186
|
+
)
|
|
187
|
+
assert result == "already_latest"
|
|
188
|
+
assert pip_calls == []
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def test_pypi_older_no_op():
|
|
192
|
+
pip_calls = []
|
|
193
|
+
result = _auto_update_pypi_step(
|
|
194
|
+
current="2.10.93",
|
|
195
|
+
fake_latest="2.10.0",
|
|
196
|
+
on_pip_call=lambda: pip_calls.append(1),
|
|
197
|
+
)
|
|
198
|
+
assert result == "already_latest"
|
|
199
|
+
assert pip_calls == []
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_offline_swallowed():
|
|
203
|
+
result = _auto_update_pypi_step(
|
|
204
|
+
current="2.10.93",
|
|
205
|
+
fake_latest=None,
|
|
206
|
+
raise_on_open=OSError("offline"),
|
|
207
|
+
)
|
|
208
|
+
assert result == "no_latest"
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_pip_timeout_returns_gracefully():
|
|
212
|
+
"""Slow-net cell: pip exceeds 60s → graceful return, no execv."""
|
|
213
|
+
result = _auto_update_pypi_step(
|
|
214
|
+
current="2.10.93",
|
|
215
|
+
fake_latest="99.99.99",
|
|
216
|
+
raise_on_pip=_sp.TimeoutExpired,
|
|
217
|
+
)
|
|
218
|
+
assert result == "timeout"
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def test_pip_install_failure_swallowed():
|
|
222
|
+
result = _auto_update_pypi_step(
|
|
223
|
+
current="2.10.93",
|
|
224
|
+
fake_latest="99.99.99",
|
|
225
|
+
raise_on_pip=RuntimeError("pip exploded"),
|
|
226
|
+
)
|
|
227
|
+
assert result == "pip_failed"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_version_compare_handles_non_numeric():
|
|
231
|
+
"""Pre-release strings ('2.10.93rc1') must not crash the comparator."""
|
|
232
|
+
result = _auto_update_pypi_step(
|
|
233
|
+
current="2.10.93",
|
|
234
|
+
fake_latest="2.10.93rc1",
|
|
235
|
+
)
|
|
236
|
+
# _ver_tuple drops non-numeric segments — both reduce to (2,10,93),
|
|
237
|
+
# so the path is "already_latest" (<=).
|
|
238
|
+
assert result == "already_latest"
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
"""MeshCode — Real-time communication between AI agents."""
|
|
2
|
-
__version__ = "2.10.93"
|
|
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
|
-
]
|
|
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
|