meshcode 2.11.25__tar.gz → 2.11.27__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.
Files changed (232) hide show
  1. {meshcode-2.11.25 → meshcode-2.11.27}/PKG-INFO +1 -1
  2. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/comms_v4.py +14 -2
  4. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/server.py +36 -0
  5. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/rpc_allowlist.py +3 -0
  6. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/run_agent.py +35 -16
  7. meshcode-2.11.27/meshcode/self_update.py +370 -0
  8. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode.egg-info/PKG-INFO +1 -1
  9. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode.egg-info/SOURCES.txt +3 -0
  10. {meshcode-2.11.25 → meshcode-2.11.27}/pyproject.toml +1 -1
  11. meshcode-2.11.27/tests/test_run_agent_dry_run.py +128 -0
  12. meshcode-2.11.27/tests/test_run_agent_no_server_import.py +85 -0
  13. meshcode-2.11.27/tests/test_self_update_user_site.py +139 -0
  14. meshcode-2.11.25/meshcode/self_update.py +0 -345
  15. {meshcode-2.11.25 → meshcode-2.11.27}/README.md +0 -0
  16. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/__main__.py +0 -0
  17. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/_stop_hook_template.py +0 -0
  18. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/ascii_art.py +0 -0
  19. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/atomic_push.py +0 -0
  20. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/cli.py +0 -0
  21. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/compat.py +0 -0
  22. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/daemon.py +0 -0
  23. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/date_parse.py +0 -0
  24. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/doctor.py +0 -0
  25. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/error_hints.py +0 -0
  26. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/exceptions.py +0 -0
  27. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/invites.py +0 -0
  28. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/launcher.py +0 -0
  29. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/launcher_install.py +0 -0
  30. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/__init__.py +0 -0
  31. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/__main__.py +0 -0
  32. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/backend.py +0 -0
  33. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/realtime.py +0 -0
  34. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
  35. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/test_backend.py +0 -0
  36. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  37. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  38. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  39. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  40. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  41. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/preferences.py +0 -0
  42. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/protocol_handler.py +0 -0
  43. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/protocol_v2.py +0 -0
  44. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/quickstart.py +0 -0
  45. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/scripts/check_secrets.py +0 -0
  46. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/scripts/race_rate_harness.py +0 -0
  47. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/secrets.py +0 -0
  48. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/setup_clients.py +0 -0
  49. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/supervisor.py +0 -0
  50. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode/upload.py +0 -0
  51. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/comms_v4.py +0 -0
  52. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/__init__.py +0 -0
  53. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/ascii_art.py +0 -0
  54. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/cli.py +0 -0
  55. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/comms_v4.py +0 -0
  56. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/compat.py +0 -0
  57. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/error_hints.py +0 -0
  58. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/exceptions.py +0 -0
  59. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/invites.py +0 -0
  60. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/launcher.py +0 -0
  61. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/launcher_install.py +0 -0
  62. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  63. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  64. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -0
  65. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  66. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -0
  67. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  68. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  69. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  70. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/preferences.py +0 -0
  71. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/protocol_v2.py +0 -0
  72. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/quickstart.py +0 -0
  73. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/run_agent.py +0 -0
  74. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/secrets.py +0 -0
  75. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/self_update.py +0 -0
  76. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/setup_clients.py +0 -0
  77. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/supervisor.py +0 -0
  78. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/meshcode/upload.py +0 -0
  79. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/scripts/sentinel.py +0 -0
  80. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_core.py +0 -0
  81. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_cross_agent_messaging.py +0 -0
  82. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_esc_deaf_state.py +0 -0
  83. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_exceptions.py +0 -0
  84. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_mark_read_batch.py +0 -0
  85. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_migration_integrity.py +0 -0
  86. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_realtime_event_freshness.py +0 -0
  87. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_rls_cross_tenant.py +0 -0
  88. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -0
  89. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_security_regressions.py +0 -0
  90. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_sentinel.py +0 -0
  91. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-backend-wt/tests/test_status_enum_coverage.py +0 -0
  92. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -0
  93. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -0
  94. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -0
  95. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -0
  96. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -0
  97. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -0
  98. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -0
  99. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -0
  100. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -0
  101. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -0
  102. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -0
  103. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -0
  104. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -0
  105. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -0
  106. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -0
  107. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -0
  108. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -0
  109. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  110. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -0
  111. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -0
  112. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -0
  113. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -0
  114. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -0
  115. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -0
  116. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -0
  117. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -0
  118. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -0
  119. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/comms_v4.py +0 -0
  120. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/__init__.py +0 -0
  121. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/ascii_art.py +0 -0
  122. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/cli.py +0 -0
  123. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/comms_v4.py +0 -0
  124. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/compat.py +0 -0
  125. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/error_hints.py +0 -0
  126. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/exceptions.py +0 -0
  127. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/invites.py +0 -0
  128. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/launcher.py +0 -0
  129. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/launcher_install.py +0 -0
  130. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  131. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  132. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -0
  133. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  134. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -0
  135. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  136. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  137. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  138. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/preferences.py +0 -0
  139. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/protocol_v2.py +0 -0
  140. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/quickstart.py +0 -0
  141. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/run_agent.py +0 -0
  142. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/secrets.py +0 -0
  143. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/self_update.py +0 -0
  144. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/setup_clients.py +0 -0
  145. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/supervisor.py +0 -0
  146. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/meshcode/upload.py +0 -0
  147. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/scripts/sentinel.py +0 -0
  148. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_core.py +0 -0
  149. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -0
  150. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -0
  151. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_exceptions.py +0 -0
  152. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -0
  153. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_migration_integrity.py +0 -0
  154. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -0
  155. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -0
  156. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -0
  157. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_security_regressions.py +0 -0
  158. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_sentinel.py +0 -0
  159. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -0
  160. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/comms_v4.py +0 -0
  161. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/__init__.py +0 -0
  162. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/ascii_art.py +0 -0
  163. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/cli.py +0 -0
  164. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/comms_v4.py +0 -0
  165. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/compat.py +0 -0
  166. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/error_hints.py +0 -0
  167. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/exceptions.py +0 -0
  168. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/invites.py +0 -0
  169. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/launcher.py +0 -0
  170. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/launcher_install.py +0 -0
  171. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  172. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  173. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -0
  174. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  175. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -0
  176. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  177. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  178. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  179. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/preferences.py +0 -0
  180. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -0
  181. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/quickstart.py +0 -0
  182. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/run_agent.py +0 -0
  183. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/secrets.py +0 -0
  184. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/self_update.py +0 -0
  185. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/setup_clients.py +0 -0
  186. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/supervisor.py +0 -0
  187. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/meshcode/upload.py +0 -0
  188. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/scripts/sentinel.py +0 -0
  189. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_core.py +0 -0
  190. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -0
  191. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -0
  192. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_exceptions.py +0 -0
  193. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -0
  194. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -0
  195. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -0
  196. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -0
  197. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -0
  198. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_security_regressions.py +0 -0
  199. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_sentinel.py +0 -0
  200. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -0
  201. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode.egg-info/dependency_links.txt +0 -0
  202. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode.egg-info/entry_points.txt +0 -0
  203. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode.egg-info/requires.txt +0 -0
  204. {meshcode-2.11.25 → meshcode-2.11.27}/meshcode.egg-info/top_level.txt +0 -0
  205. {meshcode-2.11.25 → meshcode-2.11.27}/setup.cfg +0 -0
  206. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_auto_update_hardening.py +0 -0
  207. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_boot_bug_regression.py +0 -0
  208. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_color_truecolor.py +0 -0
  209. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_core.py +0 -0
  210. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_cross_agent_messaging.py +0 -0
  211. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_date_parse.py +0 -0
  212. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_doctor.py +0 -0
  213. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_epistemic_v1_python_sdk.py +0 -0
  214. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  215. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_esc_deaf_state.py +0 -0
  216. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_exceptions.py +0 -0
  217. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_init_device_code.py +0 -0
  218. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_install_guard.py +0 -0
  219. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_lease_sigterm_release.py +0 -0
  220. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_mark_read_batch.py +0 -0
  221. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_migration_integrity.py +0 -0
  222. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_realtime_event_freshness.py +0 -0
  223. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_rls_cross_tenant.py +0 -0
  224. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_rpc_grants.py +0 -0
  225. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_rpc_migrations.py +0 -0
  226. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_security_regressions.py +0 -0
  227. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_sentinel.py +0 -0
  228. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_setup_path.py +0 -0
  229. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_sleep_signals.py +0 -0
  230. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_status_enum_coverage.py +0 -0
  231. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_stay_on_loop_hook.py +0 -0
  232. {meshcode-2.11.25 → meshcode-2.11.27}/tests/test_wait_open_tasks_contradiction.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.25
3
+ Version: 2.11.27
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "2.11.25"
2
+ __version__ = "2.11.27"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -2597,13 +2597,20 @@ EXAMPLES:
2597
2597
  meshcode run backend
2598
2598
  meshcode run my-app/backend --editor cursor
2599
2599
  """,
2600
- "go": """meshcode go <agent> [--project <name>]
2600
+ "go": """meshcode go <agent> [--project <name>] [--dry-run]
2601
2601
 
2602
2602
  Shortcut for setup + run. Creates workspace if needed, then launches.
2603
2603
 
2604
+ --dry-run Run every bootstrap step (auth, workspace, .mcp.json,
2605
+ editor detection, banner) but stop just before the
2606
+ heartbeat + editor exec. Returns 0 on a clean path.
2607
+ Use in CI / pre-publish smokes to catch import-time
2608
+ sys.exit and env-guard regressions.
2609
+
2604
2610
  EXAMPLES:
2605
2611
  meshcode go backend
2606
2612
  meshcode go my-app/frontend
2613
+ meshcode go my-app/frontend --dry-run
2607
2614
  """,
2608
2615
  "scan": """meshcode scan
2609
2616
 
@@ -3234,6 +3241,11 @@ if __name__ == "__main__":
3234
3241
  perm_override = "bypass"
3235
3242
  elif "--safe" in sys.argv:
3236
3243
  perm_override = "safe"
3244
+ # --dry-run: exercise the full bootstrap codepath but stop just
3245
+ # before pre-flight heartbeat + execvp(editor). Returns 0 on a
3246
+ # clean path. Catches import-time sys.exit / env-guard regressions
3247
+ # before they hit users (cf. 2.11.11 BUG-COMMANDER-MCP-BOOT).
3248
+ dry_run = "--dry-run" in sys.argv
3237
3249
  # Auth pre-check: if not logged in, prompt inline
3238
3250
  api_key = _load_api_key_for_cli()
3239
3251
  if not api_key:
@@ -3249,7 +3261,7 @@ if __name__ == "__main__":
3249
3261
  sys.exit(1)
3250
3262
  import importlib
3251
3263
  _run = importlib.import_module("meshcode.run_agent").run
3252
- sys.exit(_run(agent, project=proj_override, editor_override=editor_override, permission_override=perm_override))
3264
+ sys.exit(_run(agent, project=proj_override, editor_override=editor_override, permission_override=perm_override, dry_run=dry_run))
3253
3265
 
3254
3266
  elif cmd == "scan":
3255
3267
  # meshcode scan — detect identicon from stdin/clipboard and launch agent
@@ -5398,6 +5398,42 @@ def meshcode_remember(
5398
5398
  return result
5399
5399
 
5400
5400
 
5401
+ @mcp.tool()
5402
+ @with_working_status
5403
+ def meshcode_seed_fixture(
5404
+ table: str,
5405
+ overrides: Optional[Dict[str, Any]] = None,
5406
+ ttl_seconds: int = 3600,
5407
+ ) -> Dict[str, Any]:
5408
+ """Seed a fixture row in a whitelisted table for Playwright/verify.
5409
+ MESH-IMPROVE-7 (mig 345). Whitelist: mc_tasks, mc_messages, mc_agents.
5410
+ Auto-swept after ttl_seconds (min 60). Pair with meshcode_release_fixture.
5411
+ """
5412
+ if table not in ("mc_tasks", "mc_messages", "mc_agents"):
5413
+ return {"error": "table not seedable",
5414
+ "hint": "whitelist: mc_tasks, mc_messages, mc_agents"}
5415
+ return be.sb_rpc("mc_seed_fixture", {
5416
+ "p_api_key": _get_api_key(),
5417
+ "p_project_id": _PROJECT_ID,
5418
+ "p_table": table,
5419
+ "p_overrides": overrides or {},
5420
+ "p_ttl_seconds": max(int(ttl_seconds), 60),
5421
+ })
5422
+
5423
+
5424
+ @mcp.tool()
5425
+ @with_working_status
5426
+ def meshcode_release_fixture(row_id: str) -> Dict[str, Any]:
5427
+ """Release a fixture row immediately (don't wait for sweeper).
5428
+ MESH-IMPROVE-7 (mig 345)."""
5429
+ if not row_id or not row_id.strip():
5430
+ return {"error": "row_id cannot be empty"}
5431
+ return be.sb_rpc("mc_release_fixture", {
5432
+ "p_api_key": _get_api_key(),
5433
+ "p_row_id": row_id.strip(),
5434
+ })
5435
+
5436
+
5401
5437
  @mcp.tool()
5402
5438
  @with_working_status
5403
5439
  def meshcode_register_skills(skills: List[str]) -> Dict[str, Any]:
@@ -107,6 +107,9 @@ AGENT_CALLABLE_RPCS = [
107
107
  # Skill parity (mig 344)
108
108
  "mc_register_skills",
109
109
  "mc_mandate_skill",
110
+ # Fixture seeding (mig 345)
111
+ "mc_seed_fixture",
112
+ "mc_release_fixture",
110
113
  ]
111
114
 
112
115
 
@@ -678,13 +678,24 @@ def _preflight_heartbeat(agent: str, project: str) -> None:
678
678
  print(f"[meshcode] Pre-flight heartbeat skipped: {e}", file=sys.stderr)
679
679
 
680
680
 
681
- def run(agent: str, project: Optional[str] = None, editor_override: Optional[str] = None, permission_override: Optional[str] = None) -> int:
682
- """Launch the user's editor with ONLY the named agent's MCP server loaded."""
683
- # Non-blocking self-update check (consumes prior result, may spawn bg pip)
684
- try:
685
- self_update.check_and_maybe_update()
686
- except Exception:
687
- pass
681
+ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str] = None, permission_override: Optional[str] = None, dry_run: bool = False) -> int:
682
+ """Launch the user's editor with ONLY the named agent's MCP server loaded.
683
+
684
+ dry_run: when True, exercise the full bootstrap codepath (auth check,
685
+ workspace + .mcp.json validation, editor detection, banner generation,
686
+ env-var assembly, cmd construction) but stop just before mutating shared
687
+ state (pre-flight heartbeat) and replacing the process (execvp). Print
688
+ "[meshcode] DRY-RUN OK: ..." and return 0. Designed to catch bootstrap-
689
+ class bugs (import-time sys.exit, missing env-var guards, broken cross-
690
+ process imports — cf. BUG-COMMANDER-MCP-BOOT 2026-05-20) before publish.
691
+ """
692
+ # Non-blocking self-update check (consumes prior result, may spawn bg pip).
693
+ # Skip in dry-run — we don't want CI / pre-publish smokes to fork a pip.
694
+ if not dry_run:
695
+ try:
696
+ self_update.check_and_maybe_update()
697
+ except Exception:
698
+ pass
688
699
 
689
700
  # Detect: are we already inside a Claude Code session? os.execvp(claude)
690
701
  # from inside an existing claude won't work — claude needs a fresh
@@ -846,15 +857,12 @@ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str
846
857
  except Exception:
847
858
  pass # Soft-fail — boot must proceed regardless.
848
859
 
849
- # BUG-AUDIO-COLOR fix: wait briefly for profile fetch to populate
850
- # _CACHED_AGENT_COLOR before rendering the banner, so the boot art
851
- # doesn't flash the deterministic-hash fallback color and then "jump"
852
- # to the canonical profile color. Upper-bound 350ms.
853
- try:
854
- from .meshcode_mcp.server import _REGISTER_PROFILE_READY
855
- _REGISTER_PROFILE_READY.wait(timeout=0.35)
856
- except Exception:
857
- pass
860
+ # NOTE: do not import meshcode_mcp.server from this module. server.py has
861
+ # a module-level MESHCODE_PROJECT/AGENT precondition that sys.exit(2)s when
862
+ # those vars are not set in the parent shell kills `meshcode go` on
863
+ # fresh machines. _fetch_or_generate_art below already fetches the profile
864
+ # color synchronously, so the banner gets the real color without any
865
+ # cross-process coordination.
858
866
 
859
867
  # ── Welcome banner with unique ASCII art + personality ──────────
860
868
  try:
@@ -993,6 +1001,17 @@ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str
993
1001
  except OSError:
994
1002
  pass # No devnull (unlikely) — let the editor handle it
995
1003
 
1004
+ # Dry-run bail-out: every validation above has passed, the editor cmd
1005
+ # is fully assembled, and the next step (heartbeat + execvp) would
1006
+ # mutate shared state. CI / pre-publish smokes stop here.
1007
+ if dry_run:
1008
+ print(
1009
+ f"[meshcode] DRY-RUN OK: would exec {cmd[0]!r} for "
1010
+ f"{resolved_project}/{agent} from cwd {os.getcwd()!r}",
1011
+ file=sys.stderr,
1012
+ )
1013
+ return 0
1014
+
996
1015
  # Pre-flight heartbeat: mark the agent as online BEFORE the editor
997
1016
  # subprocess spawns. On Windows without bypass mode, Claude Code may
998
1017
  # delay starting the MCP server until the user interacts — leaving the
@@ -0,0 +1,370 @@
1
+ """Background self-update for the meshcode CLI.
2
+
3
+ Design goals:
4
+ - ZERO added latency to `meshcode run`. The PyPI check + pip install
5
+ runs in a fully detached background subprocess. The current launch
6
+ is never blocked.
7
+ - Opt-in (first-run prompt, like permission_mode). Default ON for
8
+ interactive TTY users, default OFF for CI / non-TTY.
9
+ - Cache the PyPI check for 1 hour so we don't hit the network on
10
+ every launch.
11
+ - Two-launch model: launch N detects + downloads in the background;
12
+ launch N+1 reads the result file, prints "[meshcode] updated X → Y",
13
+ and the new code is already on disk.
14
+ - Safe in all the edge cases that bite Python CLI updaters:
15
+ * editable installs (dev mode) → skip
16
+ * pipx installs → use `pipx upgrade meshcode`
17
+ * MCP server subprocess → skip (we're inside an agent runtime)
18
+ * --no-update flag, MESHCODE_NO_UPDATE=1 env → skip
19
+ * offline / network error → silent skip
20
+ * concurrent runs → file lock so only one bg update at a time
21
+ """
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ import os
26
+ import subprocess
27
+ import sys
28
+ import time
29
+ from pathlib import Path
30
+ from typing import Optional, Tuple
31
+
32
+ PKG_NAME = "meshcode"
33
+ PYPI_URL = f"https://pypi.org/pypi/{PKG_NAME}/json"
34
+ CACHE_TTL_SEC = 3600 # 1 hour
35
+ NETWORK_TIMEOUT_SEC = 1.5
36
+
37
+ STATE_DIR = Path.home() / ".meshcode"
38
+ RESULT_PATH = STATE_DIR / ".update_result.json"
39
+ LOCK_PATH = STATE_DIR / ".update.lock"
40
+ LOG_PATH = STATE_DIR / "update.log"
41
+ LOCK_STALE_SEC = 600 # 10 min — if a lock is older, treat as crashed
42
+
43
+
44
+ # ============================================================
45
+ # Detection helpers — when NOT to auto-update
46
+ # ============================================================
47
+
48
+ def _current_version() -> str:
49
+ try:
50
+ from . import __version__
51
+ return __version__
52
+ except Exception:
53
+ return "0.0.0"
54
+
55
+
56
+ def is_editable_install() -> bool:
57
+ """True if meshcode is installed via `pip install -e .` (dev mode)."""
58
+ try:
59
+ import meshcode
60
+ path = os.path.realpath(meshcode.__file__)
61
+ return "site-packages" not in path and "dist-packages" not in path
62
+ except Exception:
63
+ return False
64
+
65
+
66
+ def is_user_site_install() -> bool:
67
+ """True if meshcode is installed in the per-user site (`pip install --user`).
68
+
69
+ Used to keep the upgrade in the same site so the user-PATH shim
70
+ (e.g. ~/Library/Python/3.X/bin/meshcode) stays valid. Without this,
71
+ `pip install -U` falls back to system-site and orphans the binary
72
+ the user's PATH points at.
73
+ """
74
+ try:
75
+ import site
76
+ import meshcode
77
+ path = os.path.realpath(meshcode.__file__)
78
+ user_site = os.path.realpath(site.getusersitepackages())
79
+ return path.startswith(user_site + os.sep)
80
+ except Exception:
81
+ return False
82
+
83
+
84
+ def is_inside_mcp_serve() -> bool:
85
+ """True if we're running inside the MCP subprocess of an agent client."""
86
+ if os.environ.get("MESHCODE_MCP_SERVE") == "1":
87
+ return True
88
+ argv0 = " ".join(sys.argv).lower()
89
+ return "meshcode_mcp" in argv0 or "meshcode.meshcode_mcp" in argv0
90
+
91
+
92
+ def is_pipx_install() -> bool:
93
+ """True if installed via pipx (we should use `pipx upgrade` instead)."""
94
+ exe = os.path.realpath(sys.executable)
95
+ return "/pipx/" in exe or "\\pipx\\" in exe
96
+
97
+
98
+ def update_disabled() -> bool:
99
+ """User explicitly opted out for this run / globally."""
100
+ if os.environ.get("MESHCODE_NO_UPDATE") == "1":
101
+ return True
102
+ if "--no-update" in sys.argv:
103
+ return True
104
+ return False
105
+
106
+
107
+ def _version_tuple(v: str) -> Tuple[int, ...]:
108
+ parts = []
109
+ for p in v.split("."):
110
+ digits = "".join(c for c in p if c.isdigit())
111
+ parts.append(int(digits) if digits else 0)
112
+ return tuple(parts)
113
+
114
+
115
+ def _is_newer(remote: str, local: str) -> bool:
116
+ try:
117
+ return _version_tuple(remote) > _version_tuple(local)
118
+ except Exception:
119
+ return False
120
+
121
+
122
+ # ============================================================
123
+ # Cache + result file (consumed by next launch)
124
+ # ============================================================
125
+
126
+ def _read_prefs() -> dict:
127
+ try:
128
+ from .preferences import load_prefs
129
+ return load_prefs()
130
+ except Exception:
131
+ return {}
132
+
133
+
134
+ def _write_prefs_kv(**kv) -> None:
135
+ try:
136
+ from .preferences import load_prefs, save_prefs
137
+ prefs = load_prefs()
138
+ prefs.update(kv)
139
+ save_prefs(prefs)
140
+ except Exception:
141
+ pass
142
+
143
+
144
+ def _cache_fresh() -> bool:
145
+ last = _read_prefs().get("last_update_check_at", 0)
146
+ return (time.time() - last) < CACHE_TTL_SEC
147
+
148
+
149
+ def _mark_checked(latest: Optional[str]) -> None:
150
+ _write_prefs_kv(
151
+ last_update_check_at=int(time.time()),
152
+ last_known_latest_version=latest or _current_version(),
153
+ )
154
+
155
+
156
+ def consume_pending_result() -> None:
157
+ """Read + delete .update_result.json, print outcome to stderr.
158
+
159
+ Called at the START of each `meshcode run` to surface what the
160
+ previous launch's background updater did.
161
+ """
162
+ if not RESULT_PATH.exists():
163
+ return
164
+ try:
165
+ data = json.loads(RESULT_PATH.read_text(encoding="utf-8"))
166
+ RESULT_PATH.unlink(missing_ok=True)
167
+ except Exception:
168
+ return
169
+ cur = _current_version()
170
+ new_v = data.get("version") or "?"
171
+ if data.get("ok"):
172
+ if _is_newer(new_v, cur):
173
+ print(f"[meshcode] updated {cur} → {new_v}. Restart your editor to load the new version.", file=sys.stderr)
174
+ else:
175
+ # update ran but pip didn't actually upgrade (already at latest)
176
+ pass
177
+ else:
178
+ err = data.get("error", "unknown error")
179
+ print(f"[meshcode] WARN: last auto-update failed: {err}", file=sys.stderr)
180
+
181
+
182
+ # ============================================================
183
+ # Network — PyPI version probe
184
+ # ============================================================
185
+
186
+ def fetch_latest_version(timeout: float = NETWORK_TIMEOUT_SEC) -> Optional[str]:
187
+ try:
188
+ import urllib.request
189
+ req = urllib.request.Request(PYPI_URL, headers={"User-Agent": f"meshcode/{_current_version()}"})
190
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
191
+ data = json.loads(resp.read().decode("utf-8"))
192
+ return data.get("info", {}).get("version")
193
+ except Exception:
194
+ return None
195
+
196
+
197
+ # ============================================================
198
+ # File lock — prevent concurrent bg updates
199
+ # ============================================================
200
+
201
+ def _acquire_lock() -> bool:
202
+ try:
203
+ if LOCK_PATH.exists():
204
+ age = time.time() - LOCK_PATH.stat().st_mtime
205
+ if age < LOCK_STALE_SEC:
206
+ return False
207
+ LOCK_PATH.unlink(missing_ok=True)
208
+ LOCK_PATH.parent.mkdir(parents=True, exist_ok=True)
209
+ LOCK_PATH.write_text(str(os.getpid()), encoding="utf-8")
210
+ return True
211
+ except Exception:
212
+ return False
213
+
214
+
215
+ # ============================================================
216
+ # Background update spawn
217
+ # ============================================================
218
+
219
+ _BG_RUNNER_PY = r'''
220
+ import json, os, subprocess, sys, time
221
+ from pathlib import Path
222
+
223
+ result = {"version": None, "ok": False, "error": None, "finished_at": None}
224
+ state_dir = Path.home() / ".meshcode"
225
+ result_path = state_dir / ".update_result.json"
226
+ lock_path = state_dir / ".update.lock"
227
+ log_path = state_dir / "update.log"
228
+ state_dir.mkdir(parents=True, exist_ok=True)
229
+
230
+ mode = sys.argv[1] if len(sys.argv) > 1 else "pip"
231
+ target_version = sys.argv[2] if len(sys.argv) > 2 else None
232
+ site_flag = sys.argv[3] if len(sys.argv) > 3 else "system"
233
+
234
+ try:
235
+ if mode == "pipx":
236
+ cmd = ["pipx", "upgrade", "meshcode"]
237
+ else:
238
+ cmd = [sys.executable, "-m", "pip", "install", "-U", "--disable-pip-version-check", "--quiet"]
239
+ # Keep upgrade in the same site as the current install so the
240
+ # user-PATH shim (~/Library/Python/X.Y/bin) doesn't get orphaned.
241
+ if site_flag == "user":
242
+ cmd.append("--user")
243
+ cmd.append("meshcode")
244
+ with open(log_path, "ab") as logf:
245
+ logf.write(f"\n=== {time.strftime('%Y-%m-%d %H:%M:%S')} bg update via {mode} ===\n".encode())
246
+ logf.flush()
247
+ proc = subprocess.run(cmd, stdout=logf, stderr=logf, timeout=180)
248
+ if proc.returncode == 0:
249
+ result["ok"] = True
250
+ result["version"] = target_version
251
+ else:
252
+ result["error"] = f"pip exit {proc.returncode}"
253
+ except subprocess.TimeoutExpired:
254
+ result["error"] = "pip install timed out after 180s"
255
+ except Exception as e:
256
+ result["error"] = str(e)
257
+ finally:
258
+ result["finished_at"] = int(time.time())
259
+ try:
260
+ result_path.write_text(json.dumps(result), encoding="utf-8")
261
+ except Exception:
262
+ pass
263
+ try:
264
+ lock_path.unlink()
265
+ except Exception:
266
+ pass
267
+ '''
268
+
269
+
270
+ def _spawn_background_updater(target_version: str) -> bool:
271
+ """Spawn a fully detached subprocess that runs the updater.
272
+
273
+ The parent (current `meshcode run`) returns immediately. The child
274
+ runs pip install in the background, writes the result to disk, and
275
+ exits. Next `meshcode run` consumes the result.
276
+ """
277
+ if not _acquire_lock():
278
+ return False
279
+
280
+ mode = "pipx" if is_pipx_install() else "pip"
281
+ site_flag = "user" if (mode == "pip" and is_user_site_install()) else "system"
282
+
283
+ # We pass the runner code via stdin so we don't need to ship a
284
+ # second .py file. The child reads it from sys.stdin and execs it.
285
+ runner = f"import sys; exec(sys.stdin.read())"
286
+ args = [sys.executable, "-c", runner, mode, target_version, site_flag]
287
+
288
+ try:
289
+ if sys.platform == "win32":
290
+ DETACHED_PROCESS = 0x00000008
291
+ CREATE_NEW_PROCESS_GROUP = 0x00000200
292
+ CREATE_NO_WINDOW = 0x08000000
293
+ flags = DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW
294
+ proc = subprocess.Popen(
295
+ args,
296
+ stdin=subprocess.PIPE,
297
+ stdout=subprocess.DEVNULL,
298
+ stderr=subprocess.DEVNULL,
299
+ creationflags=flags,
300
+ close_fds=True,
301
+ )
302
+ else:
303
+ proc = subprocess.Popen(
304
+ args,
305
+ stdin=subprocess.PIPE,
306
+ stdout=subprocess.DEVNULL,
307
+ stderr=subprocess.DEVNULL,
308
+ start_new_session=True,
309
+ close_fds=True,
310
+ )
311
+ if proc.stdin:
312
+ proc.stdin.write(_BG_RUNNER_PY.encode("utf-8"))
313
+ proc.stdin.close()
314
+ return True
315
+ except Exception:
316
+ try:
317
+ LOCK_PATH.unlink(missing_ok=True)
318
+ except Exception:
319
+ pass
320
+ return False
321
+
322
+
323
+ # ============================================================
324
+ # Public entrypoint — called from meshcode run
325
+ # ============================================================
326
+
327
+ def check_and_maybe_update(verbose: bool = False) -> None:
328
+ """Non-blocking. Call at the start of `meshcode run`.
329
+
330
+ Order of checks (any failure → silent return, never raise):
331
+ 1. consume any pending result from previous launch
332
+ 2. opt-out gates: --no-update, MESHCODE_NO_UPDATE=1
333
+ 3. context gates: editable install, MCP subprocess
334
+ 4. user preference: resolve_auto_update() (may prompt first time)
335
+ 5. cache TTL: skip if checked < 1h ago
336
+ 6. PyPI fetch (1.5s timeout)
337
+ 7. version compare; if newer → spawn background updater
338
+ """
339
+ try:
340
+ consume_pending_result()
341
+ except Exception:
342
+ pass
343
+
344
+ if update_disabled():
345
+ return
346
+ if is_inside_mcp_serve():
347
+ return
348
+ if is_editable_install():
349
+ return
350
+
351
+ try:
352
+ from .preferences import resolve_auto_update
353
+ if not resolve_auto_update():
354
+ return
355
+ except Exception:
356
+ return
357
+
358
+ if _cache_fresh():
359
+ return
360
+
361
+ latest = fetch_latest_version()
362
+ _mark_checked(latest)
363
+ if not latest:
364
+ return
365
+ if not _is_newer(latest, _current_version()):
366
+ return
367
+
368
+ started = _spawn_background_updater(latest)
369
+ if started and verbose:
370
+ print(f"[meshcode] downloading {latest} in background...", file=sys.stderr)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.25
3
+ Version: 2.11.27
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -217,7 +217,10 @@ tests/test_realtime_event_freshness.py
217
217
  tests/test_rls_cross_tenant.py
218
218
  tests/test_rpc_grants.py
219
219
  tests/test_rpc_migrations.py
220
+ tests/test_run_agent_dry_run.py
221
+ tests/test_run_agent_no_server_import.py
220
222
  tests/test_security_regressions.py
223
+ tests/test_self_update_user_site.py
221
224
  tests/test_sentinel.py
222
225
  tests/test_setup_path.py
223
226
  tests/test_sleep_signals.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meshcode"
7
- version = "2.11.25"
7
+ version = "2.11.27"
8
8
  description = "Real-time communication between AI agents — Supabase-backed CLI"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}