meshcode 2.11.81__tar.gz → 2.11.83__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.81 → meshcode-2.11.83}/PKG-INFO +1 -1
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/__init__.py +1 -1
- meshcode-2.11.83/meshcode/claude_update.py +258 -0
- meshcode-2.11.81/meshcode/hostd.py → meshcode-2.11.83/meshcode/hostd 2.py +46 -19
- meshcode-2.11.83/meshcode/hostd.py +854 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/protocol_handler.py +2 -1
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/run_agent.py +17 -1
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/setup_clients.py +98 -12
- meshcode-2.11.83/meshcode/up.py +257 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode.egg-info/SOURCES.txt +4 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/pyproject.toml +1 -1
- meshcode-2.11.83/tests/test_autonomous_prompt_inject.py +126 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/README.md +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/__main__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/_stop_hook_template.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/atomic_push.py +0 -0
- /meshcode-2.11.81/meshcode/claude_update.py → /meshcode-2.11.83/meshcode/claude_update 2.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/cli.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/compat.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/daemon.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/date_parse.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/doctor.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/error_hints.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/exceptions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/invites.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/launcher.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/preferences.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/quickstart.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/rpc_allowlist.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/scripts/check_secrets.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/scripts/race_rate_harness.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/secrets.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/self_update.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/supervisor.py +0 -0
- /meshcode-2.11.81/meshcode/up.py → /meshcode-2.11.83/meshcode/up 2.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode/upload.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/comms_v4.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/__init__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/cli.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/compat.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/invites.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/launcher.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/preferences.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/secrets.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/self_update.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/meshcode/upload.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/scripts/sentinel.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_core.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-backend-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/comms_v4.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/__init__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/cli.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/compat.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/invites.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/launcher.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/preferences.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/secrets.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/self_update.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/meshcode/upload.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/scripts/sentinel.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_core.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/comms_v4.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/__init__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/cli.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/compat.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/invites.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/launcher.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/preferences.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/secrets.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/self_update.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/meshcode/upload.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/scripts/sentinel.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_core.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/setup.cfg +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_auto_update_hardening.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_autonomous_closegap_1.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_autonomous_closegap_2.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_autonomous_closegap_3.py +0 -0
- /meshcode-2.11.81/tests/test_autonomous_prompt_inject.py → /meshcode-2.11.83/tests/test_autonomous_prompt_inject 2.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_boot_bug_regression.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_color_truecolor.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_core.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_date_parse.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_doctor.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_epistemic_v1_python_sdk.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_epistemic_v1_stop_conditions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_exceptions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_file_upload.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_init_device_code.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_install_guard.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_lease_sigterm_release.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_marketplace_ratings.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_rpc_grants.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_run_agent_dry_run.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_run_agent_no_server_import.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_self_update_user_site.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_sentinel.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_setup_path.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_sleep_signals.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_stay_on_loop_hook.py +0 -0
- {meshcode-2.11.81 → meshcode-2.11.83}/tests/test_wait_open_tasks_contradiction.py +0 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Synchronous Claude Code CLI auto-update for `meshcode run`.
|
|
2
|
+
|
|
3
|
+
Companion to self_update.py (which handles the `meshcode` Python package).
|
|
4
|
+
When `meshcode run` launches an agent, this module probes the npm registry
|
|
5
|
+
for the latest @anthropic-ai/claude-code release and runs `npm install -g`
|
|
6
|
+
inline if the installed version is older. The launch BLOCKS until the
|
|
7
|
+
upgrade finishes (or times out).
|
|
8
|
+
|
|
9
|
+
This is intentional: Samuel's requirement is "siempre arrancar con la
|
|
10
|
+
ultima version". The two-launch async model in self_update.py cannot
|
|
11
|
+
satisfy that — it always uses the old binary on launch N. The blocking
|
|
12
|
+
model trades ~5-30s extra latency on launches where an upgrade is
|
|
13
|
+
available for the guarantee that the freshly-spawned editor uses the
|
|
14
|
+
newest CLI.
|
|
15
|
+
|
|
16
|
+
Skip conditions (any one → no-op):
|
|
17
|
+
- MESHCODE_NO_UPDATE=1 / --no-update on argv
|
|
18
|
+
- Inside an existing Claude Code session (CLAUDECODE=1)
|
|
19
|
+
- User pinned a specific version via env / prefs (let _resolve_pinned_claude handle it)
|
|
20
|
+
- Editor is not `claude` (cursor / code / codex / windsurf)
|
|
21
|
+
- npm not in PATH
|
|
22
|
+
- Network unreachable / npm registry down
|
|
23
|
+
- Already at latest version
|
|
24
|
+
|
|
25
|
+
Failures are best-effort: any error prints a single WARN line and lets
|
|
26
|
+
launch continue with whatever version is installed.
|
|
27
|
+
"""
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import json
|
|
31
|
+
import os
|
|
32
|
+
import re
|
|
33
|
+
import shutil
|
|
34
|
+
import subprocess
|
|
35
|
+
import sys
|
|
36
|
+
import time
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
from typing import Optional, Tuple
|
|
39
|
+
|
|
40
|
+
NPM_PKG = "@anthropic-ai/claude-code"
|
|
41
|
+
NPM_REGISTRY_URL = f"https://registry.npmjs.org/{NPM_PKG}/latest"
|
|
42
|
+
NETWORK_TIMEOUT_SEC = 2.5
|
|
43
|
+
INSTALL_TIMEOUT_SEC = 90 # npm i -g can be slow on cold-cache machines
|
|
44
|
+
|
|
45
|
+
STATE_DIR = Path.home() / ".meshcode"
|
|
46
|
+
LOG_PATH = STATE_DIR / "claude_update.log"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ============================================================
|
|
50
|
+
# Skip-condition helpers
|
|
51
|
+
# ============================================================
|
|
52
|
+
|
|
53
|
+
def _is_claude_session() -> bool:
|
|
54
|
+
"""True if we're being called from inside an existing Claude Code session.
|
|
55
|
+
|
|
56
|
+
Replacing the npm binary while claude is running is unsafe and pointless
|
|
57
|
+
— the running claude won't pick up the new code anyway.
|
|
58
|
+
"""
|
|
59
|
+
return os.environ.get("CLAUDECODE") == "1" or bool(os.environ.get("CLAUDE_CODE_SESSION"))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _update_disabled() -> bool:
|
|
63
|
+
# task 399d7b51 FIX A (unify): honor MESHCODE_NO_AUTO_UPDATE too. hostd launcher / run_agent /
|
|
64
|
+
# schtasks set that var to disable ALL launch-time auto-update; previously only the meshcode-pip
|
|
65
|
+
# path checked MESHCODE_NO_UPDATE, so the claude-CLI blocking update still fired (extra hang).
|
|
66
|
+
if os.environ.get("MESHCODE_NO_UPDATE") == "1":
|
|
67
|
+
return True
|
|
68
|
+
if os.environ.get("MESHCODE_NO_AUTO_UPDATE") == "1":
|
|
69
|
+
return True
|
|
70
|
+
if "--no-update" in sys.argv or "--no-auto-update" in sys.argv:
|
|
71
|
+
return True
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _resolve_user_pin() -> Optional[str]:
|
|
76
|
+
"""Return a specific version pin if set; None if 'latest' / unset.
|
|
77
|
+
|
|
78
|
+
Sources checked (first non-empty wins):
|
|
79
|
+
1. MESHCODE_CLAUDE_VERSION env var
|
|
80
|
+
2. prefs['claude_version']
|
|
81
|
+
|
|
82
|
+
Note: 'latest' / 'none' / 'skip' values are treated as "no pin" — the
|
|
83
|
+
user wants whatever is current. We do NOT consult the global admin
|
|
84
|
+
config from mc_global_config here; if an admin pins a specific
|
|
85
|
+
version, _resolve_pinned_claude in run_agent.py will route through
|
|
86
|
+
npx regardless of what's installed locally.
|
|
87
|
+
"""
|
|
88
|
+
pinned = (os.environ.get("MESHCODE_CLAUDE_VERSION") or "").strip()
|
|
89
|
+
if not pinned:
|
|
90
|
+
try:
|
|
91
|
+
from .preferences import load_prefs
|
|
92
|
+
pinned = (load_prefs().get("claude_version") or "").strip()
|
|
93
|
+
except Exception:
|
|
94
|
+
pinned = ""
|
|
95
|
+
if not pinned:
|
|
96
|
+
return None
|
|
97
|
+
if pinned.lower() in ("latest", "none", "skip"):
|
|
98
|
+
return None
|
|
99
|
+
return pinned
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# ============================================================
|
|
103
|
+
# Version probes
|
|
104
|
+
# ============================================================
|
|
105
|
+
|
|
106
|
+
def _get_installed_version(editor_cmd: str) -> Optional[str]:
|
|
107
|
+
"""Run `<editor_cmd> --version` and pull the first semver from stdout."""
|
|
108
|
+
try:
|
|
109
|
+
use_shell = sys.platform == "win32" and editor_cmd.lower().endswith((".cmd", ".bat"))
|
|
110
|
+
r = subprocess.run(
|
|
111
|
+
[editor_cmd, "--version"],
|
|
112
|
+
capture_output=True, text=True, timeout=10,
|
|
113
|
+
shell=use_shell,
|
|
114
|
+
)
|
|
115
|
+
m = re.search(r"\d+\.\d+\.\d+", r.stdout or "")
|
|
116
|
+
return m.group(0) if m else None
|
|
117
|
+
except Exception:
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _fetch_npm_latest() -> Optional[str]:
|
|
122
|
+
"""GET https://registry.npmjs.org/<pkg>/latest — returns the JSON manifest
|
|
123
|
+
of the version tagged `latest`, with a `version` field.
|
|
124
|
+
|
|
125
|
+
Do NOT send the `application/vnd.npm.install-v1+json` Accept header
|
|
126
|
+
here: the slim-manifest content type is only valid on the package
|
|
127
|
+
ROOT (/<pkg>), not on /<pkg>/<tag>. Sending it on the tag endpoint
|
|
128
|
+
returns HTTP 406. Default Accept (*/*) gets the canonical JSON.
|
|
129
|
+
"""
|
|
130
|
+
try:
|
|
131
|
+
import urllib.request
|
|
132
|
+
req = urllib.request.Request(NPM_REGISTRY_URL)
|
|
133
|
+
with urllib.request.urlopen(req, timeout=NETWORK_TIMEOUT_SEC) as resp:
|
|
134
|
+
data = json.loads(resp.read().decode("utf-8"))
|
|
135
|
+
v = data.get("version")
|
|
136
|
+
if isinstance(v, str):
|
|
137
|
+
return v
|
|
138
|
+
except Exception:
|
|
139
|
+
pass
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _version_tuple(v: str) -> Tuple[int, ...]:
|
|
144
|
+
parts = []
|
|
145
|
+
for p in v.split("."):
|
|
146
|
+
digits = "".join(c for c in p if c.isdigit())
|
|
147
|
+
parts.append(int(digits) if digits else 0)
|
|
148
|
+
return tuple(parts)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _is_newer(remote: str, local: str) -> bool:
|
|
152
|
+
try:
|
|
153
|
+
return _version_tuple(remote) > _version_tuple(local)
|
|
154
|
+
except Exception:
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ============================================================
|
|
159
|
+
# Editor-stem detection (only act when editor is claude)
|
|
160
|
+
# ============================================================
|
|
161
|
+
|
|
162
|
+
def _editor_is_claude(editor_cmd: str) -> bool:
|
|
163
|
+
name = os.path.basename(editor_cmd).lower()
|
|
164
|
+
if sys.platform == "win32":
|
|
165
|
+
name = name.rsplit(".", 1)[0] if "." in name else name
|
|
166
|
+
return name == "claude"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# ============================================================
|
|
170
|
+
# Public entrypoint
|
|
171
|
+
# ============================================================
|
|
172
|
+
|
|
173
|
+
def check_and_maybe_update_claude_blocking(
|
|
174
|
+
editor_cmd: str,
|
|
175
|
+
verbose: bool = True,
|
|
176
|
+
timeout_sec: int = INSTALL_TIMEOUT_SEC,
|
|
177
|
+
) -> Optional[str]:
|
|
178
|
+
"""Probe npm + foreground-install latest claude if newer.
|
|
179
|
+
|
|
180
|
+
Returns the new version on successful upgrade, None otherwise.
|
|
181
|
+
Never raises — all errors are swallowed and logged via WARN line.
|
|
182
|
+
"""
|
|
183
|
+
if _update_disabled():
|
|
184
|
+
return None
|
|
185
|
+
if _is_claude_session():
|
|
186
|
+
return None
|
|
187
|
+
if _resolve_user_pin():
|
|
188
|
+
return None
|
|
189
|
+
if not _editor_is_claude(editor_cmd):
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
current = _get_installed_version(editor_cmd)
|
|
193
|
+
latest = _fetch_npm_latest()
|
|
194
|
+
if not current:
|
|
195
|
+
if verbose:
|
|
196
|
+
print(f"[meshcode] (Could not read claude --version; skipping auto-update)", file=sys.stderr)
|
|
197
|
+
return None
|
|
198
|
+
if not latest:
|
|
199
|
+
if verbose:
|
|
200
|
+
print(f"[meshcode] (Could not reach npm registry; staying on claude {current})", file=sys.stderr)
|
|
201
|
+
return None
|
|
202
|
+
if not _is_newer(latest, current):
|
|
203
|
+
if verbose:
|
|
204
|
+
print(f"[meshcode] claude {current} (already latest)", file=sys.stderr)
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
npm = shutil.which("npm")
|
|
208
|
+
if not npm:
|
|
209
|
+
if verbose:
|
|
210
|
+
print(
|
|
211
|
+
f"[meshcode] WARN: claude {current} < {latest} on npm, but `npm` not in PATH; "
|
|
212
|
+
f"continuing on {current}",
|
|
213
|
+
file=sys.stderr,
|
|
214
|
+
)
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
if verbose:
|
|
218
|
+
print(
|
|
219
|
+
f"[meshcode] Upgrading claude {current} -> {latest} (blocking, can take ~30s)...",
|
|
220
|
+
file=sys.stderr,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
cmd = [npm, "install", "-g", f"{NPM_PKG}@{latest}"]
|
|
224
|
+
try:
|
|
225
|
+
STATE_DIR.mkdir(parents=True, exist_ok=True)
|
|
226
|
+
with open(LOG_PATH, "ab") as logf:
|
|
227
|
+
logf.write(
|
|
228
|
+
f"\n=== {time.strftime('%Y-%m-%d %H:%M:%S')} blocking "
|
|
229
|
+
f"npm i -g {NPM_PKG}@{latest} ===\n".encode()
|
|
230
|
+
)
|
|
231
|
+
logf.flush()
|
|
232
|
+
proc = subprocess.run(cmd, stdout=logf, stderr=logf, timeout=timeout_sec)
|
|
233
|
+
if proc.returncode == 0:
|
|
234
|
+
if verbose:
|
|
235
|
+
print(f"[meshcode] claude upgraded to {latest}", file=sys.stderr)
|
|
236
|
+
return latest
|
|
237
|
+
if verbose:
|
|
238
|
+
print(
|
|
239
|
+
f"[meshcode] WARN: npm install exit {proc.returncode}; "
|
|
240
|
+
f"continuing on {current} (log: {LOG_PATH})",
|
|
241
|
+
file=sys.stderr,
|
|
242
|
+
)
|
|
243
|
+
return None
|
|
244
|
+
except subprocess.TimeoutExpired:
|
|
245
|
+
if verbose:
|
|
246
|
+
print(
|
|
247
|
+
f"[meshcode] WARN: npm install timed out after {timeout_sec}s; "
|
|
248
|
+
f"continuing on {current}",
|
|
249
|
+
file=sys.stderr,
|
|
250
|
+
)
|
|
251
|
+
return None
|
|
252
|
+
except Exception as e:
|
|
253
|
+
if verbose:
|
|
254
|
+
print(
|
|
255
|
+
f"[meshcode] WARN: npm install failed: {e}; continuing on {current}",
|
|
256
|
+
file=sys.stderr,
|
|
257
|
+
)
|
|
258
|
+
return None
|
|
@@ -502,7 +502,7 @@ def _hostd_install_windows() -> int:
|
|
|
502
502
|
'$a = New-ScheduledTaskAction -Execute "cmd.exe" -Argument \'/c "' + str(launch_cmd) + '"\';'
|
|
503
503
|
'$t1 = New-ScheduledTaskTrigger -AtLogOn;'
|
|
504
504
|
'$t2 = New-ScheduledTaskTrigger -Once -At (Get-Date) '
|
|
505
|
-
'-RepetitionInterval (New-TimeSpan -Minutes
|
|
505
|
+
'-RepetitionInterval (New-TimeSpan -Minutes 1) -RepetitionDuration (New-TimeSpan -Days 3650);'
|
|
506
506
|
'$s = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries'
|
|
507
507
|
' -ExecutionTimeLimit ([TimeSpan]::Zero) -RestartCount 999'
|
|
508
508
|
' -RestartInterval (New-TimeSpan -Minutes 1) -MultipleInstances IgnoreNew -Hidden;'
|
|
@@ -791,32 +791,59 @@ def cmd_hostd(args: list) -> int:
|
|
|
791
791
|
if not api_key:
|
|
792
792
|
_log("FATAL: no api key — run `meshcode login` (key is read from the keychain)")
|
|
793
793
|
return 1
|
|
794
|
-
#
|
|
795
|
-
#
|
|
796
|
-
#
|
|
797
|
-
#
|
|
794
|
+
# P1 hostd_CRASH_LOOP: the detached Windows daemon has NO console, so crashes (incl hard
|
|
795
|
+
# faults / uncaught exceptions) went to INVISIBLE stderr — hostd.log showed nothing. Capture
|
|
796
|
+
# stderr + faulthandler tracebacks to a file + install an excepthook so the NEXT crash is
|
|
797
|
+
# diagnosable, and the daemon never dies silently.
|
|
798
|
+
try:
|
|
799
|
+
import faulthandler, traceback as _tbmod
|
|
800
|
+
(STATE_DIR / "logs").mkdir(parents=True, exist_ok=True)
|
|
801
|
+
_errf = open(STATE_DIR / "logs" / "hostd.stderr.log", "a", buffering=1, encoding="utf-8")
|
|
802
|
+
sys.stderr = _errf
|
|
803
|
+
faulthandler.enable(file=_errf, all_threads=True)
|
|
804
|
+
sys.excepthook = lambda t, v, tb: _log("UNCAUGHT: " + "".join(_tbmod.format_exception(t, v, tb)))
|
|
805
|
+
except Exception:
|
|
806
|
+
pass
|
|
807
|
+
# Register this host in mc_host_config so the dashboard can list it as a launch target.
|
|
798
808
|
_reg = _rpc("mc_host_config_set", {"p_api_key": api_key, "p_host_id": host_id})
|
|
799
809
|
if _reg and _reg.get("ok"):
|
|
800
810
|
_log(f"registered host {host_id} in mc_host_config")
|
|
801
811
|
else:
|
|
802
812
|
_log(f"WARN: host registration failed (dashboard may not list this host): {_reg}")
|
|
803
813
|
_log(f"hostd starting — host_id={host_id} interval={POLL_INTERVAL_SEC}s stale={STALE_SECONDS}s")
|
|
814
|
+
# uptime-since-spawn (core's suggestion): if the daemon dies <2min, the last alive log +
|
|
815
|
+
# the uptime stamped on the FATAL line reveal the <2min pattern for RC.
|
|
816
|
+
_spawn_mono = time.monotonic()
|
|
817
|
+
_last_alive_log = 0.0
|
|
818
|
+
# OUTER resilience (P1): a code exception in the poll loop must NEVER kill the daemon.
|
|
804
819
|
while True:
|
|
805
820
|
try:
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
821
|
+
while True:
|
|
822
|
+
try:
|
|
823
|
+
# heartbeat so the cloud knows this daemon is ALIVE (host_offline detection).
|
|
824
|
+
try:
|
|
825
|
+
_rpc("mc_host_heartbeat", {"p_api_key": api_key, "p_host_id": host_id})
|
|
826
|
+
except Exception:
|
|
827
|
+
pass
|
|
828
|
+
relaunched = _do_respawns(api_key, host_id)
|
|
829
|
+
recycled = _do_recycles(api_key, host_id)
|
|
830
|
+
ver_recycled = _do_version_recycles(api_key, host_id)
|
|
831
|
+
_up = int(time.monotonic() - _spawn_mono)
|
|
832
|
+
if relaunched or recycled or ver_recycled:
|
|
833
|
+
_log(f"sweep done (uptime={_up}s) — {relaunched} respawned, {recycled} recycled, {ver_recycled} version-recycled")
|
|
834
|
+
elif time.monotonic() - _last_alive_log >= 60:
|
|
835
|
+
_log(f"alive — uptime={_up}s")
|
|
836
|
+
_last_alive_log = time.monotonic()
|
|
837
|
+
except Exception as e:
|
|
838
|
+
import traceback as _tb
|
|
839
|
+
_log(f"WARN: sweep error (uptime={int(time.monotonic() - _spawn_mono)}s): {e}\n{_tb.format_exc()}")
|
|
840
|
+
time.sleep(POLL_INTERVAL_SEC)
|
|
841
|
+
except (KeyboardInterrupt, SystemExit):
|
|
842
|
+
raise # honor a real shutdown signal
|
|
843
|
+
except BaseException as e:
|
|
844
|
+
import traceback as _tb
|
|
845
|
+
_log(f"FATAL: poll loop crashed (uptime={int(time.monotonic() - _spawn_mono)}s, restarting loop in 5s): {e}\n{_tb.format_exc()}")
|
|
846
|
+
time.sleep(5)
|
|
820
847
|
|
|
821
848
|
print(f"[hostd] unknown subcommand: {sub}", file=sys.stderr)
|
|
822
849
|
return 1
|