meshcode 2.11.82__tar.gz → 2.11.84__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.82 → meshcode-2.11.84}/PKG-INFO +1 -1
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/__init__.py +1 -1
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/hostd.py +170 -6
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/protocol_handler.py +6 -2
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/run_agent.py +17 -1
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/setup_clients.py +98 -12
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode.egg-info/PKG-INFO +1 -1
- meshcode-2.11.84/meshcode.egg-info/SOURCES.txt +88 -0
- meshcode-2.11.84/meshcode.egg-info/top_level.txt +1 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/pyproject.toml +1 -1
- meshcode-2.11.82/meshcode/ascii_art.py +0 -638
- meshcode-2.11.82/meshcode/cli.py +0 -42
- meshcode-2.11.82/meshcode/compat.py +0 -174
- meshcode-2.11.82/meshcode/error_hints.py +0 -74
- meshcode-2.11.82/meshcode/exceptions.py +0 -52
- meshcode-2.11.82/meshcode/invites.py +0 -406
- meshcode-2.11.82/meshcode/launcher.py +0 -353
- meshcode-2.11.82/meshcode/launcher_install.py +0 -414
- meshcode-2.11.82/meshcode/meshcode_mcp/__init__.py +0 -22
- meshcode-2.11.82/meshcode/meshcode_mcp/__main__.py +0 -62
- meshcode-2.11.82/meshcode/meshcode_mcp/test_backend.py +0 -86
- meshcode-2.11.82/meshcode/meshcode_mcp/test_realtime.py +0 -95
- meshcode-2.11.82/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
- meshcode-2.11.82/meshcode/preferences.py +0 -260
- meshcode-2.11.82/meshcode/protocol_v2.py +0 -129
- meshcode-2.11.82/meshcode/secrets.py +0 -365
- meshcode-2.11.82/meshcode/supervisor.py +0 -186
- meshcode-2.11.82/meshcode/upload.py +0 -125
- meshcode-2.11.82/meshcode-backend-wt/comms_v4.py +0 -1941
- meshcode-2.11.82/meshcode-backend-wt/meshcode/__init__.py +0 -82
- meshcode-2.11.82/meshcode-backend-wt/meshcode/comms_v4.py +0 -3563
- meshcode-2.11.82/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -1261
- meshcode-2.11.82/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -460
- meshcode-2.11.82/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -4117
- meshcode-2.11.82/meshcode-backend-wt/meshcode/quickstart.py +0 -148
- meshcode-2.11.82/meshcode-backend-wt/meshcode/run_agent.py +0 -958
- meshcode-2.11.82/meshcode-backend-wt/meshcode/self_update.py +0 -345
- meshcode-2.11.82/meshcode-backend-wt/meshcode/setup_clients.py +0 -926
- meshcode-2.11.82/meshcode-backend-wt/scripts/sentinel.py +0 -257
- meshcode-2.11.82/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -387
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -82
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -638
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -42
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -3563
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -174
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -74
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -52
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -406
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -353
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -414
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -22
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -62
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -1261
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -460
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -4117
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -86
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -95
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -260
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -129
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -148
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -958
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -365
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -345
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -926
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -186
- meshcode-2.11.82/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -125
- meshcode-2.11.82/meshcode-noun-wt/comms_v4.py +0 -1941
- meshcode-2.11.82/meshcode-noun-wt/meshcode/__init__.py +0 -82
- meshcode-2.11.82/meshcode-noun-wt/meshcode/ascii_art.py +0 -638
- meshcode-2.11.82/meshcode-noun-wt/meshcode/cli.py +0 -42
- meshcode-2.11.82/meshcode-noun-wt/meshcode/comms_v4.py +0 -3563
- meshcode-2.11.82/meshcode-noun-wt/meshcode/compat.py +0 -174
- meshcode-2.11.82/meshcode-noun-wt/meshcode/error_hints.py +0 -74
- meshcode-2.11.82/meshcode-noun-wt/meshcode/exceptions.py +0 -52
- meshcode-2.11.82/meshcode-noun-wt/meshcode/invites.py +0 -406
- meshcode-2.11.82/meshcode-noun-wt/meshcode/launcher.py +0 -353
- meshcode-2.11.82/meshcode-noun-wt/meshcode/launcher_install.py +0 -414
- meshcode-2.11.82/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -22
- meshcode-2.11.82/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -62
- meshcode-2.11.82/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -1261
- meshcode-2.11.82/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -460
- meshcode-2.11.82/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -4117
- meshcode-2.11.82/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -86
- meshcode-2.11.82/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -95
- meshcode-2.11.82/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
- meshcode-2.11.82/meshcode-noun-wt/meshcode/preferences.py +0 -260
- meshcode-2.11.82/meshcode-noun-wt/meshcode/protocol_v2.py +0 -129
- meshcode-2.11.82/meshcode-noun-wt/meshcode/quickstart.py +0 -148
- meshcode-2.11.82/meshcode-noun-wt/meshcode/run_agent.py +0 -958
- meshcode-2.11.82/meshcode-noun-wt/meshcode/secrets.py +0 -365
- meshcode-2.11.82/meshcode-noun-wt/meshcode/self_update.py +0 -345
- meshcode-2.11.82/meshcode-noun-wt/meshcode/setup_clients.py +0 -926
- meshcode-2.11.82/meshcode-noun-wt/meshcode/supervisor.py +0 -186
- meshcode-2.11.82/meshcode-noun-wt/meshcode/upload.py +0 -125
- meshcode-2.11.82/meshcode-noun-wt/scripts/sentinel.py +0 -257
- meshcode-2.11.82/meshcode-noun-wt/tests/test_core.py +0 -216
- meshcode-2.11.82/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -366
- meshcode-2.11.82/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -361
- meshcode-2.11.82/meshcode-noun-wt/tests/test_exceptions.py +0 -107
- meshcode-2.11.82/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -200
- meshcode-2.11.82/meshcode-noun-wt/tests/test_migration_integrity.py +0 -176
- meshcode-2.11.82/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -236
- meshcode-2.11.82/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -255
- meshcode-2.11.82/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -387
- meshcode-2.11.82/meshcode-noun-wt/tests/test_security_regressions.py +0 -171
- meshcode-2.11.82/meshcode-noun-wt/tests/test_sentinel.py +0 -148
- meshcode-2.11.82/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -231
- meshcode-2.11.82/meshcode-tasks-wt/comms_v4.py +0 -1941
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/__init__.py +0 -82
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/ascii_art.py +0 -638
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/cli.py +0 -42
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/comms_v4.py +0 -3563
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/compat.py +0 -174
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/error_hints.py +0 -74
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/exceptions.py +0 -52
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/invites.py +0 -406
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/launcher.py +0 -353
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/launcher_install.py +0 -414
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -22
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -62
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -1261
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -460
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -4117
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -86
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -95
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/preferences.py +0 -260
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -129
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/quickstart.py +0 -148
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/run_agent.py +0 -958
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/secrets.py +0 -365
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/self_update.py +0 -345
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/setup_clients.py +0 -926
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/supervisor.py +0 -186
- meshcode-2.11.82/meshcode-tasks-wt/meshcode/upload.py +0 -125
- meshcode-2.11.82/meshcode-tasks-wt/scripts/sentinel.py +0 -257
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_core.py +0 -216
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -366
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -361
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_exceptions.py +0 -107
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -200
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -176
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -236
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -255
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -387
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_security_regressions.py +0 -171
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_sentinel.py +0 -148
- meshcode-2.11.82/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -231
- meshcode-2.11.82/meshcode.egg-info/SOURCES.txt +0 -238
- meshcode-2.11.82/meshcode.egg-info/top_level.txt +0 -4
- meshcode-2.11.82/tests/test_core.py +0 -216
- meshcode-2.11.82/tests/test_cross_agent_messaging.py +0 -366
- meshcode-2.11.82/tests/test_esc_deaf_state.py +0 -361
- meshcode-2.11.82/tests/test_exceptions.py +0 -107
- meshcode-2.11.82/tests/test_mark_read_batch.py +0 -200
- meshcode-2.11.82/tests/test_migration_integrity.py +0 -176
- meshcode-2.11.82/tests/test_realtime_event_freshness.py +0 -236
- meshcode-2.11.82/tests/test_rls_cross_tenant.py +0 -255
- meshcode-2.11.82/tests/test_security_regressions.py +0 -171
- meshcode-2.11.82/tests/test_sentinel.py +0 -148
- meshcode-2.11.82/tests/test_status_enum_coverage.py +0 -231
- {meshcode-2.11.82 → meshcode-2.11.84}/README.md +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/__main__.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/_stop_hook_template.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/atomic_push.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/claude_update.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/cli.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/compat.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/daemon.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/date_parse.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/doctor.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/error_hints.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/exceptions.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/invites.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/launcher.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/preferences.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/quickstart.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/rpc_allowlist.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/scripts/check_secrets.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/scripts/race_rate_harness.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/secrets.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/self_update.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/supervisor.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode/up.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/meshcode/upload.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/setup.cfg +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_auto_update_hardening.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_autonomous_closegap_1.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_autonomous_closegap_2.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_autonomous_closegap_3.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_autonomous_prompt_inject.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_boot_bug_regression.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_color_truecolor.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_core.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_date_parse.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_doctor.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_epistemic_v1_python_sdk.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_epistemic_v1_stop_conditions.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_exceptions.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_file_upload.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_init_device_code.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_install_guard.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_lease_sigterm_release.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_marketplace_ratings.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_rpc_grants.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_run_agent_dry_run.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_run_agent_no_server_import.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_self_update_user_site.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_sentinel.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_setup_path.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_sleep_signals.py +0 -0
- {meshcode-2.11.82/meshcode-backend-wt → meshcode-2.11.84}/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_stay_on_loop_hook.py +0 -0
- {meshcode-2.11.82 → meshcode-2.11.84}/tests/test_wait_open_tasks_contradiction.py +0 -0
|
@@ -36,6 +36,7 @@ import json
|
|
|
36
36
|
import os
|
|
37
37
|
import shlex
|
|
38
38
|
import shutil
|
|
39
|
+
import signal as _signal
|
|
39
40
|
import subprocess
|
|
40
41
|
import sys
|
|
41
42
|
import time
|
|
@@ -219,6 +220,11 @@ def _spawn_agent(project: str, agent: str, headless: bool = False) -> bool:
|
|
|
219
220
|
env = {k: v for k, v in os.environ.items()
|
|
220
221
|
if k not in ("CLAUDECODE", "CLAUDE_CODE_SESSION",
|
|
221
222
|
"MESHCODE_NO_UPDATE", "MESHCODE_NO_AUTO_UPDATE")}
|
|
223
|
+
# LAUNCH-NO-WINDOW (task 35bee961): tell run_agent it's headless so it spawns
|
|
224
|
+
# the Claude Code child with CREATE_NO_WINDOW. Without this, our DETACHED_PROCESS
|
|
225
|
+
# python parent (no console) spawns `cmd.exe /c claude.cmd` and Windows ALLOCATES
|
|
226
|
+
# A FRESH CONSOLE WINDOW for the child -> a terminal pops up despite "headless".
|
|
227
|
+
env["MESHCODE_HEADLESS"] = "1"
|
|
222
228
|
log_dir = STATE_DIR / "logs"
|
|
223
229
|
try:
|
|
224
230
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -255,8 +261,12 @@ def _spawn_agent(project: str, agent: str, headless: bool = False) -> bool:
|
|
|
255
261
|
try:
|
|
256
262
|
# `python -m meshcode` (NOT the meshcode.exe shim) so the .exe isn't held open by the
|
|
257
263
|
# agent -> a background `pip install -U` can replace it on Windows (task 14782bb4 #4).
|
|
258
|
-
subprocess.Popen([sys.executable, "-m", "meshcode", "run", target], **kwargs)
|
|
259
|
-
|
|
264
|
+
proc = subprocess.Popen([sys.executable, "-m", "meshcode", "run", target], **kwargs)
|
|
265
|
+
# LAUNCH-NO-WINDOW (task 35bee961): record the headless PID so the Stop sweep
|
|
266
|
+
# (_do_stops) can hard-kill it — a headless agent has NO window for Samuel to
|
|
267
|
+
# close, so desired_state='stopped' must be enforced by killing the process.
|
|
268
|
+
_record_headless_pid(target, proc.pid)
|
|
269
|
+
_log(f"spawned {target} HEADLESS (no window, {sys.platform}; pid={proc.pid}; log={log_path})")
|
|
260
270
|
return True
|
|
261
271
|
except Exception as e:
|
|
262
272
|
_log(f"WARN: headless spawn {target} failed: {e}")
|
|
@@ -364,6 +374,151 @@ def _save_state(st: dict) -> None:
|
|
|
364
374
|
pass
|
|
365
375
|
|
|
366
376
|
|
|
377
|
+
# ------------------------------------------------------------------
|
|
378
|
+
# Stop — hard-kill a HEADLESS agent (task 35bee961). A headless agent has no
|
|
379
|
+
# window Samuel can close, so a Stop (desired_state='stopped') that the agent
|
|
380
|
+
# doesn't honor cooperatively (must_exit) would otherwise run forever. We record
|
|
381
|
+
# each headless spawn's PID (persisted across hostd restarts) and kill it when the
|
|
382
|
+
# cloud says it should be stopped but it's still heartbeating.
|
|
383
|
+
# ------------------------------------------------------------------
|
|
384
|
+
|
|
385
|
+
def _record_headless_pid(target: str, pid: int) -> None:
|
|
386
|
+
try:
|
|
387
|
+
st = _load_state()
|
|
388
|
+
pids = st.get("headless_pids") or {}
|
|
389
|
+
pids[target] = pid
|
|
390
|
+
st["headless_pids"] = pids
|
|
391
|
+
_save_state(st)
|
|
392
|
+
except Exception:
|
|
393
|
+
pass
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _pid_cmdline(pid: int) -> str:
|
|
397
|
+
"""Best-effort command line for a pid (to avoid killing a reused PID). '' on failure."""
|
|
398
|
+
try:
|
|
399
|
+
if sys.platform == "win32":
|
|
400
|
+
out = subprocess.run(
|
|
401
|
+
["wmic", "process", "where", f"ProcessId={pid}", "get", "CommandLine"],
|
|
402
|
+
capture_output=True, text=True, timeout=5).stdout
|
|
403
|
+
else:
|
|
404
|
+
out = subprocess.run(
|
|
405
|
+
["ps", "-o", "args=", "-p", str(pid)],
|
|
406
|
+
capture_output=True, text=True, timeout=5).stdout
|
|
407
|
+
return out or ""
|
|
408
|
+
except Exception:
|
|
409
|
+
return ""
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _discover_agent_pids(target: str) -> list:
|
|
413
|
+
"""Fallback PID discovery by command line, for agents spawned before this hostd
|
|
414
|
+
(no recorded PID) or after a state-file loss. Matches `meshcode run <target>`.
|
|
415
|
+
Best-effort; returns [] on any failure."""
|
|
416
|
+
pids = []
|
|
417
|
+
try:
|
|
418
|
+
if sys.platform == "win32":
|
|
419
|
+
out = subprocess.run(
|
|
420
|
+
["wmic", "process", "where", "name='python.exe'", "get", "ProcessId,CommandLine", "/FORMAT:LIST"],
|
|
421
|
+
capture_output=True, text=True, timeout=8).stdout
|
|
422
|
+
block_pid = None
|
|
423
|
+
for line in out.splitlines():
|
|
424
|
+
line = line.strip()
|
|
425
|
+
if line.startswith("CommandLine="):
|
|
426
|
+
block_pid = ("meshcode" in line and f"run {target}" in line)
|
|
427
|
+
elif line.startswith("ProcessId=") and block_pid:
|
|
428
|
+
try:
|
|
429
|
+
pids.append(int(line.split("=", 1)[1]))
|
|
430
|
+
except Exception:
|
|
431
|
+
pass
|
|
432
|
+
block_pid = None
|
|
433
|
+
else:
|
|
434
|
+
out = subprocess.run(
|
|
435
|
+
["pgrep", "-f", f"meshcode run {target}"],
|
|
436
|
+
capture_output=True, text=True, timeout=8).stdout
|
|
437
|
+
for ln in out.split():
|
|
438
|
+
try:
|
|
439
|
+
pids.append(int(ln))
|
|
440
|
+
except Exception:
|
|
441
|
+
pass
|
|
442
|
+
except Exception:
|
|
443
|
+
pass
|
|
444
|
+
return pids
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def _kill_headless_pid(target: str, pid: int) -> bool:
|
|
448
|
+
"""Hard-kill a recorded headless PID + its child tree. Guards against PID reuse by
|
|
449
|
+
confirming the cmdline still looks like this meshcode agent. Returns True if killed."""
|
|
450
|
+
if not pid:
|
|
451
|
+
return False
|
|
452
|
+
cl = _pid_cmdline(pid).lower()
|
|
453
|
+
# Reuse guard: a headless agent's process is either `python -m meshcode run ...`
|
|
454
|
+
# (Windows: parent stays alive) or `claude ...` (POSIX: execvp'd to the editor). If
|
|
455
|
+
# we got a cmdline and it's NEITHER, the PID was reused by something unrelated — skip.
|
|
456
|
+
if cl and ("meshcode" not in cl and "claude" not in cl):
|
|
457
|
+
_log(f"STOP {target}: pid {pid} cmdline no longer matches (reused) — skip kill")
|
|
458
|
+
return False
|
|
459
|
+
try:
|
|
460
|
+
if sys.platform == "win32":
|
|
461
|
+
# /T kills the whole tree (python parent + cmd.exe + claude child), /F forces.
|
|
462
|
+
subprocess.run(["taskkill", "/PID", str(pid), "/T", "/F"],
|
|
463
|
+
capture_output=True, timeout=10)
|
|
464
|
+
else:
|
|
465
|
+
try:
|
|
466
|
+
os.killpg(os.getpgid(pid), _signal.SIGTERM) # start_new_session => own group
|
|
467
|
+
except Exception:
|
|
468
|
+
os.kill(pid, _signal.SIGTERM)
|
|
469
|
+
time.sleep(1.5)
|
|
470
|
+
try:
|
|
471
|
+
os.kill(pid, 0) # still alive?
|
|
472
|
+
os.killpg(os.getpgid(pid), _signal.SIGKILL)
|
|
473
|
+
except ProcessLookupError:
|
|
474
|
+
pass
|
|
475
|
+
except Exception:
|
|
476
|
+
try:
|
|
477
|
+
os.kill(pid, _signal.SIGKILL)
|
|
478
|
+
except Exception:
|
|
479
|
+
pass
|
|
480
|
+
_log(f"STOP {target}: hard-killed headless pid {pid}")
|
|
481
|
+
return True
|
|
482
|
+
except Exception as e:
|
|
483
|
+
_log(f"WARN: STOP {target} kill pid {pid} failed: {e}")
|
|
484
|
+
return False
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _do_stops(api_key: str, host_id: str) -> int:
|
|
488
|
+
"""One stop sweep: hard-kill headless agents the cloud marked stopped but that are
|
|
489
|
+
still alive (cooperative must_exit didn't land). Returns number killed."""
|
|
490
|
+
res = _rpc("mc_agents_to_stop", {"p_api_key": api_key, "p_host_id": host_id})
|
|
491
|
+
if not res or not res.get("ok"):
|
|
492
|
+
return 0
|
|
493
|
+
agents = res.get("agents") or []
|
|
494
|
+
if not agents:
|
|
495
|
+
return 0
|
|
496
|
+
st = _load_state()
|
|
497
|
+
pids = st.get("headless_pids") or {}
|
|
498
|
+
n = 0
|
|
499
|
+
for a in agents:
|
|
500
|
+
proj, agent = a.get("project_name"), a.get("agent")
|
|
501
|
+
if not proj or not agent:
|
|
502
|
+
continue
|
|
503
|
+
target = f"{proj}/{agent}"
|
|
504
|
+
pid = pids.get(target)
|
|
505
|
+
killed = bool(pid) and _kill_headless_pid(target, pid)
|
|
506
|
+
if pid and killed:
|
|
507
|
+
pids.pop(target, None)
|
|
508
|
+
if not killed:
|
|
509
|
+
# No recorded PID (spawned by an older hostd / state lost) OR the recorded
|
|
510
|
+
# PID was stale — discover by command line and kill.
|
|
511
|
+
for dpid in _discover_agent_pids(target):
|
|
512
|
+
if _kill_headless_pid(target, dpid):
|
|
513
|
+
killed = True
|
|
514
|
+
pids.pop(target, None)
|
|
515
|
+
if killed:
|
|
516
|
+
n += 1
|
|
517
|
+
st["headless_pids"] = pids
|
|
518
|
+
_save_state(st)
|
|
519
|
+
return n
|
|
520
|
+
|
|
521
|
+
|
|
367
522
|
def _do_recycles(api_key: str, host_id: str) -> int:
|
|
368
523
|
"""Uptime-based recycle at task boundary. Returns number recycled."""
|
|
369
524
|
cfg = _rpc("mc_host_config_get", {"p_api_key": api_key, "p_host_id": host_id})
|
|
@@ -811,6 +966,10 @@ def cmd_hostd(args: list) -> int:
|
|
|
811
966
|
else:
|
|
812
967
|
_log(f"WARN: host registration failed (dashboard may not list this host): {_reg}")
|
|
813
968
|
_log(f"hostd starting — host_id={host_id} interval={POLL_INTERVAL_SEC}s stale={STALE_SECONDS}s")
|
|
969
|
+
# uptime-since-spawn (core's suggestion): if the daemon dies <2min, the last alive log +
|
|
970
|
+
# the uptime stamped on the FATAL line reveal the <2min pattern for RC.
|
|
971
|
+
_spawn_mono = time.monotonic()
|
|
972
|
+
_last_alive_log = 0.0
|
|
814
973
|
# OUTER resilience (P1): a code exception in the poll loop must NEVER kill the daemon.
|
|
815
974
|
while True:
|
|
816
975
|
try:
|
|
@@ -824,17 +983,22 @@ def cmd_hostd(args: list) -> int:
|
|
|
824
983
|
relaunched = _do_respawns(api_key, host_id)
|
|
825
984
|
recycled = _do_recycles(api_key, host_id)
|
|
826
985
|
ver_recycled = _do_version_recycles(api_key, host_id)
|
|
827
|
-
|
|
828
|
-
|
|
986
|
+
stopped = _do_stops(api_key, host_id)
|
|
987
|
+
_up = int(time.monotonic() - _spawn_mono)
|
|
988
|
+
if relaunched or recycled or ver_recycled or stopped:
|
|
989
|
+
_log(f"sweep done (uptime={_up}s) — {relaunched} respawned, {recycled} recycled, {ver_recycled} version-recycled, {stopped} stopped")
|
|
990
|
+
elif time.monotonic() - _last_alive_log >= 60:
|
|
991
|
+
_log(f"alive — uptime={_up}s")
|
|
992
|
+
_last_alive_log = time.monotonic()
|
|
829
993
|
except Exception as e:
|
|
830
994
|
import traceback as _tb
|
|
831
|
-
_log(f"WARN: sweep error: {e}\n{_tb.format_exc()}")
|
|
995
|
+
_log(f"WARN: sweep error (uptime={int(time.monotonic() - _spawn_mono)}s): {e}\n{_tb.format_exc()}")
|
|
832
996
|
time.sleep(POLL_INTERVAL_SEC)
|
|
833
997
|
except (KeyboardInterrupt, SystemExit):
|
|
834
998
|
raise # honor a real shutdown signal
|
|
835
999
|
except BaseException as e:
|
|
836
1000
|
import traceback as _tb
|
|
837
|
-
_log(f"FATAL: poll loop crashed (restarting loop in 5s): {e}\n{_tb.format_exc()}")
|
|
1001
|
+
_log(f"FATAL: poll loop crashed (uptime={int(time.monotonic() - _spawn_mono)}s, restarting loop in 5s): {e}\n{_tb.format_exc()}")
|
|
838
1002
|
time.sleep(5)
|
|
839
1003
|
|
|
840
1004
|
print(f"[hostd] unknown subcommand: {sub}", file=sys.stderr)
|
|
@@ -117,8 +117,12 @@ def _spawn_terminal_windows(cmd: str) -> tuple[bool, str]:
|
|
|
117
117
|
wt = str(cand)
|
|
118
118
|
if wt:
|
|
119
119
|
try:
|
|
120
|
-
#
|
|
121
|
-
|
|
120
|
+
# LAUNCH-NO-WINDOW (task 35bee961, commander-corrected): `-w new` forces a
|
|
121
|
+
# BRAND-NEW Windows Terminal window (freshly created => FOREGROUND/focused), which
|
|
122
|
+
# is what Samuel wants ("ventana nueva ENFOCADA"). The old `-w 0 nt` opened a tab in
|
|
123
|
+
# the most-recently-used window — frequently minimized/background, so the launch
|
|
124
|
+
# looked like "nada pasó".
|
|
125
|
+
subprocess.Popen([wt, "-w", "new", "nt", "cmd", "/k", cmd])
|
|
122
126
|
return True, "wt"
|
|
123
127
|
except Exception as e:
|
|
124
128
|
return False, f"wt.exe: {e}"
|
|
@@ -1082,7 +1082,23 @@ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str
|
|
|
1082
1082
|
if use_shell:
|
|
1083
1083
|
print(f"[meshcode] (Windows: launching via shell for .cmd wrapper)")
|
|
1084
1084
|
launch_cwd = str(ws) if os.path.isdir(ws) else None
|
|
1085
|
-
|
|
1085
|
+
# LAUNCH-NO-WINDOW (task 35bee961): when hostd spawned us HEADLESS, our parent
|
|
1086
|
+
# python has NO console (DETACHED_PROCESS|CREATE_NO_WINDOW). Spawning the Claude
|
|
1087
|
+
# child here WITHOUT CREATE_NO_WINDOW makes Windows allocate a FRESH console
|
|
1088
|
+
# window for it -> a terminal pops up despite "headless". Pass CREATE_NO_WINDOW so
|
|
1089
|
+
# the child stays windowless too. Detect headless via the env flag hostd sets, OR
|
|
1090
|
+
# by having no console of our own (GetConsoleWindow()==0) as a belt-and-suspenders.
|
|
1091
|
+
_kwargs = {"shell": use_shell, "cwd": launch_cwd}
|
|
1092
|
+
_headless = os.environ.get("MESHCODE_HEADLESS", "").lower() in ("1", "true", "yes")
|
|
1093
|
+
if not _headless:
|
|
1094
|
+
try:
|
|
1095
|
+
import ctypes as _ct
|
|
1096
|
+
_headless = _ct.windll.kernel32.GetConsoleWindow() == 0
|
|
1097
|
+
except Exception:
|
|
1098
|
+
_headless = False
|
|
1099
|
+
if _headless:
|
|
1100
|
+
_kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
|
|
1101
|
+
result = _sp.run(cmd, **_kwargs)
|
|
1086
1102
|
sys.exit(result.returncode)
|
|
1087
1103
|
else:
|
|
1088
1104
|
# Unix: replace this process with the editor
|
|
@@ -254,6 +254,79 @@ def _toml_escape(s: str) -> str:
|
|
|
254
254
|
return s.replace("\\", "\\\\").replace("\"", "\\\"")
|
|
255
255
|
|
|
256
256
|
|
|
257
|
+
def _stop_hook_command() -> str:
|
|
258
|
+
"""Windows-safe Stop-hook command string for .claude/settings.json.
|
|
259
|
+
|
|
260
|
+
WIN-HOOKS-PYTHON3-TEMPLATE (task 77581e18): a bare ``python3`` resolves to
|
|
261
|
+
the Microsoft Store stub on Windows — it exits non-zero and the hook
|
|
262
|
+
silently no-ops, so stay_on_loop (and the PreCompact/SessionStart handoff
|
|
263
|
+
hooks) never fire on Windows. Resolve the interpreter to the absolute path
|
|
264
|
+
of the process running this scaffolder (same idiom as the MCP server
|
|
265
|
+
command, see ~line 219: ``sys.executable or "python3"``), in POSIX slash
|
|
266
|
+
form so the path is safe to quote under BOTH ``sh -c`` (macOS/Linux) and
|
|
267
|
+
Git Bash (Windows). The script is still referenced via
|
|
268
|
+
``$CLAUDE_PROJECT_DIR`` — Claude Code substitutes that placeholder itself
|
|
269
|
+
before invoking the shell, cross-platform, so ``python3`` was the only
|
|
270
|
+
break. Shell form (single command string) is kept over the exec
|
|
271
|
+
``args`` form for backward-compatibility with older Claude Code builds
|
|
272
|
+
that predate the ``args`` field.
|
|
273
|
+
"""
|
|
274
|
+
interp = (
|
|
275
|
+
Path(sys.executable).as_posix() if sys.executable
|
|
276
|
+
else ("py" if sys.platform == "win32" else "python3")
|
|
277
|
+
)
|
|
278
|
+
return f'"{interp}" "$CLAUDE_PROJECT_DIR/.claude/hooks/stay_on_loop.py"'
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _heal_settings_stop_hook(ws: Path, dry_run: bool = False):
|
|
282
|
+
"""Self-heal the Stop-hook command in ws/.claude/settings.json.
|
|
283
|
+
|
|
284
|
+
WIN-HOOKS-PYTHON3-TEMPLATE deliverable 2: rewriting only the hook .py body
|
|
285
|
+
leaves existing Windows workspaces broken because their settings.json still
|
|
286
|
+
carries the legacy ``python3 "$CLAUDE_PROJECT_DIR/..."`` command (the
|
|
287
|
+
MS-Store stub). Surgically replace just the command of any Stop hook that
|
|
288
|
+
references ``stay_on_loop.py`` and differs from the Windows-safe form —
|
|
289
|
+
every other key/hook in the file is preserved, so a user who added their
|
|
290
|
+
own hooks does not get clobbered. Missing settings.json is backfilled.
|
|
291
|
+
|
|
292
|
+
Returns ``(changed: bool, note: str)``.
|
|
293
|
+
"""
|
|
294
|
+
settings_path = ws / ".claude" / "settings.json"
|
|
295
|
+
desired = _stop_hook_command()
|
|
296
|
+
if not settings_path.exists():
|
|
297
|
+
if dry_run:
|
|
298
|
+
return True, "would create settings.json"
|
|
299
|
+
try:
|
|
300
|
+
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
301
|
+
settings_path.write_text(json.dumps({
|
|
302
|
+
"hooks": {"Stop": [{"hooks": [{"type": "command", "command": desired}]}]},
|
|
303
|
+
}, indent=2) + "\n", encoding="utf-8")
|
|
304
|
+
return True, "created settings.json"
|
|
305
|
+
except Exception as e: # noqa: BLE001 — best-effort heal
|
|
306
|
+
return False, f"settings.json create failed: {e}"
|
|
307
|
+
try:
|
|
308
|
+
data = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
309
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
310
|
+
return False, f"settings.json unreadable ({e}) — left untouched"
|
|
311
|
+
changed = False
|
|
312
|
+
for entry in ((data or {}).get("hooks") or {}).get("Stop") or []:
|
|
313
|
+
for hook in (entry.get("hooks") or []):
|
|
314
|
+
cmd = hook.get("command")
|
|
315
|
+
if isinstance(cmd, str) and "stay_on_loop.py" in cmd and cmd != desired:
|
|
316
|
+
changed = True
|
|
317
|
+
if not dry_run:
|
|
318
|
+
hook["command"] = desired
|
|
319
|
+
if not changed:
|
|
320
|
+
return False, "settings.json already current"
|
|
321
|
+
if dry_run:
|
|
322
|
+
return True, "would patch settings.json"
|
|
323
|
+
try:
|
|
324
|
+
settings_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
325
|
+
except Exception as e: # noqa: BLE001
|
|
326
|
+
return False, f"settings.json write failed: {e}"
|
|
327
|
+
return True, "patched settings.json"
|
|
328
|
+
|
|
329
|
+
|
|
257
330
|
def _render_codex_block(server_id: str, server_block: Dict[str, Any]) -> str:
|
|
258
331
|
"""Render a [mcp_servers.<server_id>] section (plus [.env] subtable) for
|
|
259
332
|
Codex CLI's ~/.codex/config.toml format. Returns a text block starting
|
|
@@ -1337,7 +1410,7 @@ Call `meshcode_wait` now.
|
|
|
1337
1410
|
"Stop": [{
|
|
1338
1411
|
"hooks": [{
|
|
1339
1412
|
"type": "command",
|
|
1340
|
-
"command":
|
|
1413
|
+
"command": _stop_hook_command(),
|
|
1341
1414
|
}],
|
|
1342
1415
|
}],
|
|
1343
1416
|
},
|
|
@@ -1582,19 +1655,32 @@ def patch_hooks(dry_run: bool = False) -> int:
|
|
|
1582
1655
|
try:
|
|
1583
1656
|
existing = hook_path.read_text(encoding="utf-8") if hook_path.exists() else ""
|
|
1584
1657
|
if existing == _STOP_HOOK_BODY:
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
patched.append(ws.name)
|
|
1658
|
+
body_note = "body current"
|
|
1659
|
+
elif dry_run:
|
|
1660
|
+
body_note = "body would patch"
|
|
1661
|
+
else:
|
|
1662
|
+
hook_path.write_text(_STOP_HOOK_BODY, encoding="utf-8")
|
|
1663
|
+
try:
|
|
1664
|
+
hook_path.chmod(0o755)
|
|
1665
|
+
except OSError:
|
|
1666
|
+
pass
|
|
1667
|
+
body_note = "body patched"
|
|
1596
1668
|
except Exception as e:
|
|
1597
1669
|
failed.append((ws.name, str(e)))
|
|
1670
|
+
continue
|
|
1671
|
+
|
|
1672
|
+
# WIN-HOOKS-PYTHON3-TEMPLATE deliverable 2: patching the .py body alone
|
|
1673
|
+
# leaves the legacy `python3` command in settings.json (MS-Store stub on
|
|
1674
|
+
# Windows), so always self-heal settings.json too — independent of body
|
|
1675
|
+
# status, since a workspace can have a current body but a stale command.
|
|
1676
|
+
try:
|
|
1677
|
+
settings_changed, settings_note = _heal_settings_stop_hook(ws, dry_run)
|
|
1678
|
+
except Exception as e: # noqa: BLE001 — never let one ws abort the sweep
|
|
1679
|
+
settings_changed, settings_note = False, f"settings heal failed: {e}"
|
|
1680
|
+
|
|
1681
|
+
body_changed = body_note in ("body patched", "body would patch")
|
|
1682
|
+
record = f"{ws.name} ({body_note}; {settings_note})"
|
|
1683
|
+
(patched if (body_changed or settings_changed) else skipped).append(record)
|
|
1598
1684
|
|
|
1599
1685
|
verb = "would patch" if dry_run else "patched"
|
|
1600
1686
|
print(f"[meshcode patch-hooks] {verb} {len(patched)} workspace(s):")
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
meshcode/__init__.py
|
|
4
|
+
meshcode/__main__.py
|
|
5
|
+
meshcode/_stop_hook_template.py
|
|
6
|
+
meshcode/ascii_art.py
|
|
7
|
+
meshcode/atomic_push.py
|
|
8
|
+
meshcode/claude_update.py
|
|
9
|
+
meshcode/cli.py
|
|
10
|
+
meshcode/comms_v4.py
|
|
11
|
+
meshcode/compat.py
|
|
12
|
+
meshcode/daemon.py
|
|
13
|
+
meshcode/date_parse.py
|
|
14
|
+
meshcode/doctor.py
|
|
15
|
+
meshcode/error_hints.py
|
|
16
|
+
meshcode/exceptions.py
|
|
17
|
+
meshcode/hostd.py
|
|
18
|
+
meshcode/invites.py
|
|
19
|
+
meshcode/launcher.py
|
|
20
|
+
meshcode/launcher_install.py
|
|
21
|
+
meshcode/preferences.py
|
|
22
|
+
meshcode/protocol_handler.py
|
|
23
|
+
meshcode/protocol_v2.py
|
|
24
|
+
meshcode/quickstart.py
|
|
25
|
+
meshcode/rpc_allowlist.py
|
|
26
|
+
meshcode/run_agent.py
|
|
27
|
+
meshcode/secrets.py
|
|
28
|
+
meshcode/self_update.py
|
|
29
|
+
meshcode/setup_clients.py
|
|
30
|
+
meshcode/supervisor.py
|
|
31
|
+
meshcode/up.py
|
|
32
|
+
meshcode/upload.py
|
|
33
|
+
meshcode.egg-info/PKG-INFO
|
|
34
|
+
meshcode.egg-info/SOURCES.txt
|
|
35
|
+
meshcode.egg-info/dependency_links.txt
|
|
36
|
+
meshcode.egg-info/entry_points.txt
|
|
37
|
+
meshcode.egg-info/requires.txt
|
|
38
|
+
meshcode.egg-info/top_level.txt
|
|
39
|
+
meshcode/meshcode_mcp/__init__.py
|
|
40
|
+
meshcode/meshcode_mcp/__main__.py
|
|
41
|
+
meshcode/meshcode_mcp/backend.py
|
|
42
|
+
meshcode/meshcode_mcp/realtime.py
|
|
43
|
+
meshcode/meshcode_mcp/server.py
|
|
44
|
+
meshcode/meshcode_mcp/sleep_signals.py
|
|
45
|
+
meshcode/meshcode_mcp/test_backend.py
|
|
46
|
+
meshcode/meshcode_mcp/test_boot_timing.py
|
|
47
|
+
meshcode/meshcode_mcp/test_install_guard.py
|
|
48
|
+
meshcode/meshcode_mcp/test_prefs_claude_version.py
|
|
49
|
+
meshcode/meshcode_mcp/test_realtime.py
|
|
50
|
+
meshcode/meshcode_mcp/test_server_wrapper.py
|
|
51
|
+
meshcode/scripts/check_secrets.py
|
|
52
|
+
meshcode/scripts/race_rate_harness.py
|
|
53
|
+
tests/test_auto_update_hardening.py
|
|
54
|
+
tests/test_autonomous_closegap_1.py
|
|
55
|
+
tests/test_autonomous_closegap_2.py
|
|
56
|
+
tests/test_autonomous_closegap_3.py
|
|
57
|
+
tests/test_autonomous_prompt_inject.py
|
|
58
|
+
tests/test_boot_bug_regression.py
|
|
59
|
+
tests/test_color_truecolor.py
|
|
60
|
+
tests/test_core.py
|
|
61
|
+
tests/test_cross_agent_messaging.py
|
|
62
|
+
tests/test_date_parse.py
|
|
63
|
+
tests/test_doctor.py
|
|
64
|
+
tests/test_epistemic_v1_python_sdk.py
|
|
65
|
+
tests/test_epistemic_v1_stop_conditions.py
|
|
66
|
+
tests/test_esc_deaf_state.py
|
|
67
|
+
tests/test_exceptions.py
|
|
68
|
+
tests/test_file_upload.py
|
|
69
|
+
tests/test_init_device_code.py
|
|
70
|
+
tests/test_install_guard.py
|
|
71
|
+
tests/test_lease_sigterm_release.py
|
|
72
|
+
tests/test_mark_read_batch.py
|
|
73
|
+
tests/test_marketplace_ratings.py
|
|
74
|
+
tests/test_migration_integrity.py
|
|
75
|
+
tests/test_realtime_event_freshness.py
|
|
76
|
+
tests/test_rls_cross_tenant.py
|
|
77
|
+
tests/test_rpc_grants.py
|
|
78
|
+
tests/test_rpc_migrations.py
|
|
79
|
+
tests/test_run_agent_dry_run.py
|
|
80
|
+
tests/test_run_agent_no_server_import.py
|
|
81
|
+
tests/test_security_regressions.py
|
|
82
|
+
tests/test_self_update_user_site.py
|
|
83
|
+
tests/test_sentinel.py
|
|
84
|
+
tests/test_setup_path.py
|
|
85
|
+
tests/test_sleep_signals.py
|
|
86
|
+
tests/test_status_enum_coverage.py
|
|
87
|
+
tests/test_stay_on_loop_hook.py
|
|
88
|
+
tests/test_wait_open_tasks_contradiction.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
meshcode
|