meshcode 2.11.10__tar.gz → 2.11.12__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 (223) hide show
  1. {meshcode-2.11.10 → meshcode-2.11.12}/PKG-INFO +1 -1
  2. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/__init__.py +1 -1
  3. meshcode-2.11.12/meshcode/__main__.py +42 -0
  4. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/comms_v4.py +173 -52
  5. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/server.py +25 -2
  6. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/run_agent.py +10 -0
  7. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode.egg-info/PKG-INFO +1 -1
  8. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode.egg-info/SOURCES.txt +4 -0
  9. {meshcode-2.11.10 → meshcode-2.11.12}/pyproject.toml +1 -1
  10. meshcode-2.11.12/tests/test_color_truecolor.py +83 -0
  11. meshcode-2.11.12/tests/test_init_device_code.py +68 -0
  12. meshcode-2.11.12/tests/test_setup_path.py +66 -0
  13. {meshcode-2.11.10 → meshcode-2.11.12}/README.md +0 -0
  14. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/_stop_hook_template.py +0 -0
  15. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/ascii_art.py +0 -0
  16. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/cli.py +0 -0
  17. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/compat.py +0 -0
  18. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/daemon.py +0 -0
  19. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/doctor.py +0 -0
  20. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/error_hints.py +0 -0
  21. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/exceptions.py +0 -0
  22. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/invites.py +0 -0
  23. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/launcher.py +0 -0
  24. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/launcher_install.py +0 -0
  25. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/__init__.py +0 -0
  26. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/__main__.py +0 -0
  27. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/backend.py +0 -0
  28. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/realtime.py +0 -0
  29. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
  30. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/test_backend.py +0 -0
  31. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  32. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  33. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  34. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  35. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  36. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/preferences.py +0 -0
  37. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/protocol_handler.py +0 -0
  38. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/protocol_v2.py +0 -0
  39. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/quickstart.py +0 -0
  40. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/scripts/check_secrets.py +0 -0
  41. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/scripts/race_rate_harness.py +0 -0
  42. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/secrets.py +0 -0
  43. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/self_update.py +0 -0
  44. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/setup_clients.py +0 -0
  45. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/supervisor.py +0 -0
  46. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode/upload.py +0 -0
  47. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/comms_v4.py +0 -0
  48. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/__init__.py +0 -0
  49. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/ascii_art.py +0 -0
  50. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/cli.py +0 -0
  51. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/comms_v4.py +0 -0
  52. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/compat.py +0 -0
  53. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/error_hints.py +0 -0
  54. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/exceptions.py +0 -0
  55. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/invites.py +0 -0
  56. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/launcher.py +0 -0
  57. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/launcher_install.py +0 -0
  58. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  59. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  60. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -0
  61. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  62. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -0
  63. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  64. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  65. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  66. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/preferences.py +0 -0
  67. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/protocol_v2.py +0 -0
  68. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/quickstart.py +0 -0
  69. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/run_agent.py +0 -0
  70. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/secrets.py +0 -0
  71. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/self_update.py +0 -0
  72. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/setup_clients.py +0 -0
  73. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/supervisor.py +0 -0
  74. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/meshcode/upload.py +0 -0
  75. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/scripts/sentinel.py +0 -0
  76. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_core.py +0 -0
  77. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_cross_agent_messaging.py +0 -0
  78. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_esc_deaf_state.py +0 -0
  79. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_exceptions.py +0 -0
  80. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_mark_read_batch.py +0 -0
  81. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_migration_integrity.py +0 -0
  82. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_realtime_event_freshness.py +0 -0
  83. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_rls_cross_tenant.py +0 -0
  84. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -0
  85. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_security_regressions.py +0 -0
  86. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_sentinel.py +0 -0
  87. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-backend-wt/tests/test_status_enum_coverage.py +0 -0
  88. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -0
  89. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -0
  90. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -0
  91. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -0
  92. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -0
  93. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -0
  94. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -0
  95. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -0
  96. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -0
  97. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -0
  98. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -0
  99. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -0
  100. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -0
  101. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -0
  102. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -0
  103. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -0
  104. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -0
  105. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  106. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -0
  107. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -0
  108. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -0
  109. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -0
  110. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -0
  111. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -0
  112. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -0
  113. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -0
  114. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -0
  115. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/comms_v4.py +0 -0
  116. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/__init__.py +0 -0
  117. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/ascii_art.py +0 -0
  118. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/cli.py +0 -0
  119. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/comms_v4.py +0 -0
  120. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/compat.py +0 -0
  121. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/error_hints.py +0 -0
  122. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/exceptions.py +0 -0
  123. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/invites.py +0 -0
  124. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/launcher.py +0 -0
  125. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/launcher_install.py +0 -0
  126. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  127. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  128. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -0
  129. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  130. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -0
  131. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  132. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  133. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  134. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/preferences.py +0 -0
  135. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/protocol_v2.py +0 -0
  136. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/quickstart.py +0 -0
  137. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/run_agent.py +0 -0
  138. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/secrets.py +0 -0
  139. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/self_update.py +0 -0
  140. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/setup_clients.py +0 -0
  141. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/supervisor.py +0 -0
  142. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/meshcode/upload.py +0 -0
  143. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/scripts/sentinel.py +0 -0
  144. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_core.py +0 -0
  145. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -0
  146. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -0
  147. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_exceptions.py +0 -0
  148. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -0
  149. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_migration_integrity.py +0 -0
  150. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -0
  151. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -0
  152. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -0
  153. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_security_regressions.py +0 -0
  154. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_sentinel.py +0 -0
  155. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -0
  156. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/comms_v4.py +0 -0
  157. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/__init__.py +0 -0
  158. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/ascii_art.py +0 -0
  159. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/cli.py +0 -0
  160. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/comms_v4.py +0 -0
  161. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/compat.py +0 -0
  162. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/error_hints.py +0 -0
  163. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/exceptions.py +0 -0
  164. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/invites.py +0 -0
  165. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/launcher.py +0 -0
  166. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/launcher_install.py +0 -0
  167. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  168. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  169. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -0
  170. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  171. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -0
  172. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  173. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  174. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  175. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/preferences.py +0 -0
  176. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -0
  177. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/quickstart.py +0 -0
  178. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/run_agent.py +0 -0
  179. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/secrets.py +0 -0
  180. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/self_update.py +0 -0
  181. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/setup_clients.py +0 -0
  182. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/supervisor.py +0 -0
  183. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/meshcode/upload.py +0 -0
  184. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/scripts/sentinel.py +0 -0
  185. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_core.py +0 -0
  186. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -0
  187. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -0
  188. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_exceptions.py +0 -0
  189. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -0
  190. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -0
  191. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -0
  192. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -0
  193. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -0
  194. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_security_regressions.py +0 -0
  195. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_sentinel.py +0 -0
  196. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -0
  197. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode.egg-info/dependency_links.txt +0 -0
  198. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode.egg-info/entry_points.txt +0 -0
  199. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode.egg-info/requires.txt +0 -0
  200. {meshcode-2.11.10 → meshcode-2.11.12}/meshcode.egg-info/top_level.txt +0 -0
  201. {meshcode-2.11.10 → meshcode-2.11.12}/setup.cfg +0 -0
  202. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_auto_update_hardening.py +0 -0
  203. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_boot_bug_regression.py +0 -0
  204. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_core.py +0 -0
  205. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_cross_agent_messaging.py +0 -0
  206. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_doctor.py +0 -0
  207. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_epistemic_v1_python_sdk.py +0 -0
  208. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  209. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_esc_deaf_state.py +0 -0
  210. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_exceptions.py +0 -0
  211. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_install_guard.py +0 -0
  212. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_lease_sigterm_release.py +0 -0
  213. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_mark_read_batch.py +0 -0
  214. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_migration_integrity.py +0 -0
  215. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_realtime_event_freshness.py +0 -0
  216. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_rls_cross_tenant.py +0 -0
  217. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_rpc_migrations.py +0 -0
  218. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_security_regressions.py +0 -0
  219. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_sentinel.py +0 -0
  220. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_sleep_signals.py +0 -0
  221. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_status_enum_coverage.py +0 -0
  222. {meshcode-2.11.10 → meshcode-2.11.12}/tests/test_stay_on_loop_hook.py +0 -0
  223. {meshcode-2.11.10 → meshcode-2.11.12}/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.10
3
+ Version: 2.11.12
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.10"
2
+ __version__ = "2.11.12"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -0,0 +1,42 @@
1
+ """Helpful error when user invokes `python -m meshcode` because the
2
+ console-script isn't on PATH yet (B5 friction).
3
+
4
+ Also dispatches `python -m meshcode setup-path` to the real CLI so the
5
+ user can fix PATH without needing the `meshcode` binary on PATH first.
6
+ """
7
+ import os
8
+ import sys
9
+
10
+
11
+ def main():
12
+ # If the user explicitly asked to set up PATH, delegate to the real CLI.
13
+ if len(sys.argv) > 1 and sys.argv[1] == "setup-path":
14
+ from meshcode import comms_v4 as _cv # noqa: F401
15
+ sys.argv[0] = "meshcode"
16
+ # comms_v4 runs main() at module load via `if __name__ == "__main__"`.
17
+ # Re-exec the body by importing as __main__ would require executing
18
+ # the module again; simpler: invoke via runpy.
19
+ import runpy
20
+ runpy.run_module("meshcode.comms_v4", run_name="__main__", alter_sys=True)
21
+ return
22
+
23
+ # Otherwise: the user is here because `meshcode` isn't on PATH.
24
+ print()
25
+ print(" meshcode is installed but the `meshcode` command isn't on your PATH.")
26
+ print()
27
+ print(" Quick fix:")
28
+ print(f" {sys.executable} -m meshcode setup-path")
29
+ print()
30
+ print(" Or manually:")
31
+ v = sys.version_info
32
+ if sys.platform == "darwin":
33
+ print(f' export PATH="$HOME/Library/Python/{v.major}.{v.minor}/bin:$PATH"')
34
+ else:
35
+ print(' export PATH="$(python3 -m site --user-base)/bin:$PATH"')
36
+ print()
37
+ print(" Then re-run: meshcode --help")
38
+ sys.exit(1)
39
+
40
+
41
+ if __name__ == "__main__":
42
+ main()
@@ -2777,26 +2777,108 @@ if __name__ == "__main__":
2777
2777
  sys.exit(1)
2778
2778
  print()
2779
2779
 
2780
- # B3: wrap pairing RPC so SSL / network errors abort cleanly (no
2781
- # silent fall-through to API-key paste — friction #7).
2780
+ # B3: wrap any pairing RPC so SSL / network errors abort cleanly with
2781
+ # an actionable message (no silent fall-through, friction #7).
2782
+ def _pairing_rpc(fn, args):
2783
+ try:
2784
+ return sb_rpc(fn, args)
2785
+ except Exception as _e:
2786
+ _msg = str(_e).lower()
2787
+ if "certificate" in _msg or "ssl" in _msg:
2788
+ print()
2789
+ print(" [meshcode] SSL certificate verification failed.")
2790
+ print(" [meshcode] This usually means your Python install is missing root certs.")
2791
+ _v = sys.version_info
2792
+ print(f" [meshcode] Fix: run /Applications/Python\\ {_v.major}.{_v.minor}/Install\\ Certificates.command")
2793
+ print(f" [meshcode] (Replace {_v.major}.{_v.minor} with your actual installed version.)")
2794
+ print(f" [meshcode] Alternative: pip install --upgrade certifi")
2795
+ else:
2796
+ print()
2797
+ print(f" [meshcode] Pairing service unreachable: {_e}")
2798
+ print(f" [meshcode] Check your network, then re-run `meshcode init`.")
2799
+ sys.exit(1)
2800
+
2801
+ # B6.2 device-code OAuth (preferred): generates a code, opens browser
2802
+ # to /auth/device?code=…, user clicks Approve, CLI receives a scoped
2803
+ # token. Token is per-device, revocable from dashboard. Replaces the
2804
+ # legacy api-key paste path (which left long-lived secrets in
2805
+ # terminal history — Samuel friction).
2806
+ import hashlib as _hashlib
2807
+ import platform as _platform
2782
2808
  try:
2783
- pairing = sb_rpc("mc_create_pairing_session", {})
2784
- except Exception as _init_e:
2785
- _msg = str(_init_e).lower()
2786
- if "certificate" in _msg or "ssl" in _msg:
2809
+ _hn = _platform.node() or "unknown"
2810
+ _device_fp = _hashlib.sha256(
2811
+ f"{_platform.system()}|{_hn}|{_platform.machine()}".encode()
2812
+ ).hexdigest()[:32]
2813
+ except Exception:
2814
+ _device_fp = _hashlib.sha256(b"meshcode-anon-device").hexdigest()[:32]
2815
+ _device_name = f"{_platform.node() or 'device'} ({_platform.system()})"
2816
+
2817
+ b6_resp = _pairing_rpc("mc_device_code_create", {
2818
+ "p_device_fingerprint": _device_fp,
2819
+ "p_device_name": _device_name,
2820
+ })
2821
+ if isinstance(b6_resp, dict) and b6_resp.get("ok") and b6_resp.get("code"):
2822
+ code = b6_resp["code"]
2823
+ expires_in = int(b6_resp.get("expires_in_s") or 600)
2824
+ url = f"https://meshcode.io/auth/device?code={code}"
2825
+ print(f" Step 1: Confirm the code in your browser:")
2826
+ print()
2827
+ print(f" ┌─────────────────┐")
2828
+ print(f" │ {code:^11} │")
2829
+ print(f" └─────────────────┘")
2830
+ print()
2831
+ print(f" Opening {url} ...")
2832
+ print(f" (If browser doesn't open: visit https://meshcode.io/auth/device)")
2833
+ print()
2834
+ try:
2835
+ webbrowser.open(url)
2836
+ except Exception:
2837
+ pass
2838
+ print(" Waiting for approval", end="", flush=True)
2839
+ token = None
2840
+ deadline = _t.time() + expires_in
2841
+ last_status = "pending"
2842
+ while _t.time() < deadline:
2843
+ _t.sleep(2)
2844
+ print(".", end="", flush=True)
2845
+ poll = _pairing_rpc("mc_device_code_poll", {
2846
+ "p_code": code,
2847
+ "p_device_fingerprint": _device_fp,
2848
+ })
2849
+ if not (isinstance(poll, dict)):
2850
+ continue
2851
+ if poll.get("status") == "approved" and poll.get("token"):
2852
+ token = poll["token"]
2853
+ break
2854
+ if poll.get("status") in ("expired", "consumed", "rejected") or poll.get("error") not in (None, "rate_limited"):
2855
+ last_status = poll.get("status") or poll.get("error")
2856
+ break
2857
+ print()
2858
+ if token:
2859
+ login(token)
2787
2860
  print()
2788
- print(" [meshcode] SSL certificate verification failed.")
2789
- print(" [meshcode] This usually means your Python install is missing root certs.")
2790
- _v = sys.version_info
2791
- print(f" [meshcode] Fix: run /Applications/Python\\ {_v.major}.{_v.minor}/Install\\ Certificates.command")
2792
- print(f" [meshcode] (Replace {_v.major}.{_v.minor} with your actual installed version.)")
2793
- print(f" [meshcode] Alternative: pip install --upgrade certifi")
2794
- else:
2861
+ print(" Paired successfully device added to your account.")
2862
+ print(" Step 2: Create your first meshwork")
2863
+ print(" Run: meshcode go <agent-name> --project <meshwork-name>")
2795
2864
  print()
2796
- print(f" [meshcode] Pairing service unreachable: {_init_e}")
2797
- print(f" [meshcode] Check your network, then re-run `meshcode init`.")
2865
+ print(" Example:")
2866
+ print(" meshcode go backend --project my-app")
2867
+ print()
2868
+ sys.exit(0)
2869
+ print()
2870
+ print(f" Pairing did not complete ({last_status}). Re-run: meshcode init")
2798
2871
  sys.exit(1)
2799
- if pairing and isinstance(pairing, dict) and pairing.get("ok"):
2872
+
2873
+ # Legacy fallback: if mc_device_code_create returned a function-not-found
2874
+ # error (B6.1 mig 326 not yet applied on this Supabase project), drop
2875
+ # back to the older mc_create_pairing_session flow. Manual API-key paste
2876
+ # has been retired (security — friction #6).
2877
+ _legacy_b6_err = ""
2878
+ if isinstance(b6_resp, dict):
2879
+ _legacy_b6_err = str(b6_resp.get("error") or "")
2880
+ pairing = _pairing_rpc("mc_create_pairing_session", {})
2881
+ if isinstance(pairing, dict) and pairing.get("ok"):
2800
2882
  session_id = pairing["session_id"]
2801
2883
  code = pairing["code"]
2802
2884
  url = pairing["url"]
@@ -2811,11 +2893,11 @@ if __name__ == "__main__":
2811
2893
  pass
2812
2894
  print(" Waiting for approval", end="", flush=True)
2813
2895
  api_key = None
2814
- for _ in range(150): # 150 * 2s = 5 minutes
2896
+ for _ in range(150):
2815
2897
  _t.sleep(2)
2816
2898
  print(".", end="", flush=True)
2817
- poll = sb_rpc("mc_poll_pairing_session", {"p_session_id": session_id})
2818
- if poll and isinstance(poll, dict):
2899
+ poll = _pairing_rpc("mc_poll_pairing_session", {"p_session_id": session_id})
2900
+ if isinstance(poll, dict):
2819
2901
  if poll.get("status") == "approved" and poll.get("api_key"):
2820
2902
  api_key = poll["api_key"]
2821
2903
  break
@@ -2823,52 +2905,28 @@ if __name__ == "__main__":
2823
2905
  break
2824
2906
  print()
2825
2907
  if api_key:
2826
- print()
2827
2908
  login(api_key)
2828
2909
  print()
2829
2910
  print(" Step 2: Create your first meshwork")
2830
2911
  print(" Run: meshcode go <agent-name> --project <meshwork-name>")
2831
2912
  print()
2832
- print(" Example:")
2833
- print(" meshcode go backend --project my-app")
2834
- print()
2835
2913
  sys.exit(0)
2836
- else:
2837
- print(" Pairing timed out or was not approved.")
2838
- print()
2839
-
2840
- # Fallback: manual API key paste
2841
- print(" Step 1: Create your account at meshcode.io")
2842
- try:
2843
- webbrowser.open("https://meshcode.io")
2844
- except Exception:
2845
- print(" Visit: https://meshcode.io")
2846
- print(" Step 2: Copy your API key from Settings")
2847
- print()
2848
- try:
2849
- api_key = input(" Paste your API key (mc_...): ").strip()
2850
- except (EOFError, KeyboardInterrupt):
2851
- api_key = ""
2852
2914
  print()
2853
- if api_key:
2854
- login(api_key)
2855
- print()
2856
- print(" Step 3: Create your first meshwork")
2857
- print(" Run: meshcode go <agent-name> --project <meshwork-name>")
2858
- print()
2859
- print(" Example:")
2860
- print(" meshcode go backend --project my-app")
2861
- print()
2862
- else:
2863
- print(" [meshcode] No API key provided. Run: meshcode login <api_key>")
2915
+ print(f" Pairing did not complete. Re-run: meshcode init")
2864
2916
  sys.exit(1)
2865
- sys.exit(0)
2917
+
2918
+ # Both paths failed.
2919
+ print()
2920
+ print(f" [meshcode] Pairing unavailable: device-code ({_legacy_b6_err or 'unknown'}); "
2921
+ f"legacy pairing ({pairing.get('error') if isinstance(pairing, dict) else 'unknown'}).")
2922
+ print(f" [meshcode] Re-run `meshcode init` later or contact support.")
2923
+ sys.exit(1)
2866
2924
 
2867
2925
  # Auth guard: commands that talk to Supabase need a valid API key.
2868
2926
  # doctor, help, version, login, prefs, launcher don't need auth.
2869
2927
  _NO_AUTH_CMDS = {"doctor", "compat", "upgrade", "help", "--help", "-h", "login",
2870
2928
  "init", "prefs", "launcher", "--version", "-V", "version",
2871
- "whoami", "profiles", "scan"}
2929
+ "whoami", "profiles", "scan", "setup-path"}
2872
2930
  if cmd not in _NO_AUTH_CMDS:
2873
2931
  _cli_key = _load_api_key_for_cli()
2874
2932
  if not _cli_key:
@@ -3479,6 +3537,69 @@ if __name__ == "__main__":
3479
3537
  print(" meshcode prefs reset")
3480
3538
  sys.exit(1)
3481
3539
 
3540
+ elif cmd == "setup-path":
3541
+ # B5: append Python user-bin to user's shell rc file (idempotent).
3542
+ # Friction #8 fix: after `pip install --user meshcode`, `meshcode`
3543
+ # isn't on PATH; this gives the user a one-command remediation.
3544
+ import platform as _pl
3545
+ import subprocess as _sp
3546
+ _v = sys.version_info
3547
+ if _pl.system() == "Darwin":
3548
+ user_bin = os.path.expanduser(f"~/Library/Python/{_v.major}.{_v.minor}/bin")
3549
+ else:
3550
+ try:
3551
+ user_bin = _sp.check_output(
3552
+ [sys.executable, "-m", "site", "--user-base"],
3553
+ text=True, timeout=5,
3554
+ ).strip() + "/bin"
3555
+ except Exception:
3556
+ user_bin = os.path.expanduser("~/.local/bin")
3557
+
3558
+ shell = os.environ.get("SHELL", "")
3559
+ if "zsh" in shell:
3560
+ rc = os.path.expanduser("~/.zshrc")
3561
+ fish = False
3562
+ elif "fish" in shell:
3563
+ rc = os.path.expanduser("~/.config/fish/config.fish")
3564
+ fish = True
3565
+ else:
3566
+ rc = os.path.expanduser("~/.bashrc")
3567
+ fish = False
3568
+
3569
+ marker = "# meshcode: PATH for user-installed Python binaries"
3570
+ line = (f"set -gx PATH {user_bin} $PATH" if fish
3571
+ else f'export PATH="{user_bin}:$PATH"')
3572
+
3573
+ existing = ""
3574
+ try:
3575
+ if os.path.exists(rc):
3576
+ with open(rc) as f:
3577
+ existing = f.read()
3578
+ except Exception:
3579
+ pass
3580
+
3581
+ if user_bin in existing:
3582
+ print(f"[meshcode] {rc} already exports {user_bin} — nothing to do.")
3583
+ sys.exit(0)
3584
+
3585
+ try:
3586
+ os.makedirs(os.path.dirname(rc), exist_ok=True)
3587
+ with open(rc, "a") as f:
3588
+ f.write(f"\n{marker}\n{line}\n")
3589
+ print(f"[meshcode] Appended PATH export to {rc}")
3590
+ print(f"[meshcode] Open a new terminal OR run: source {rc}")
3591
+ print(f"[meshcode] Then: meshcode --help")
3592
+ sys.exit(0)
3593
+ except PermissionError:
3594
+ print(f"[meshcode] ERROR: cannot write {rc} (permission denied).")
3595
+ print(f"[meshcode] Manually add this line to your shell rc:")
3596
+ print(f" {line}")
3597
+ sys.exit(1)
3598
+ except Exception as _spe:
3599
+ print(f"[meshcode] ERROR: {_spe}")
3600
+ print(f"[meshcode] Manually add: {line}")
3601
+ sys.exit(1)
3602
+
3482
3603
  elif cmd == "upgrade":
3483
3604
  # meshcode upgrade — interactive foreground upgrade
3484
3605
  import importlib
@@ -45,9 +45,21 @@ _ANSI_RGB = [
45
45
  (255, 85, 85), (85, 255, 255), (255, 85, 255), (85, 255, 85), (85, 85, 255),
46
46
  ]
47
47
 
48
+ # BUG-AUDIO-COLOR fix (task e552412a, 2026-05-19): use 24-bit truecolor
49
+ # escapes when the terminal supports them so every terminal renders the
50
+ # same agent color (4-bit palette indices are remapped by terminal themes,
51
+ # producing the cross-machine mismatch Samuel saw).
52
+ _TRUECOLOR_OK = os.environ.get("COLORTERM", "").lower() in ("truecolor", "24bit")
53
+
48
54
 
49
55
  def _hex_to_ansi(hex_color: str) -> str:
50
- """Map hex color to nearest ANSI code."""
56
+ """Return ANSI escape for a hex color.
57
+
58
+ On truecolor-capable terminals returns 24-bit truecolor (\\e[38;2;R;G;Bm) so
59
+ the color bypasses palette remapping. Falls back to nearest 4-bit ANSI
60
+ palette index when COLORTERM is not truecolor.
61
+ Returns None if hex is malformed.
62
+ """
51
63
  hx = hex_color.strip().lstrip("#")
52
64
  if len(hx) != 6:
53
65
  return None
@@ -55,6 +67,9 @@ def _hex_to_ansi(hex_color: str) -> str:
55
67
  r, g, b = int(hx[0:2], 16), int(hx[2:4], 16), int(hx[4:6], 16)
56
68
  except ValueError:
57
69
  return None
70
+ if _TRUECOLOR_OK:
71
+ return f"\033[38;2;{r};{g};{b}m"
72
+ # Fallback: map to nearest 4-bit ANSI palette index
58
73
  best_i, best_d = 0, float("inf")
59
74
  for i, (ar, ag, ab) in enumerate(_ANSI_RGB):
60
75
  d = (r - ar) ** 2 + (g - ag) ** 2 + (b - ab) ** 2
@@ -65,10 +80,18 @@ def _hex_to_ansi(hex_color: str) -> str:
65
80
 
66
81
 
67
82
  def _agent_color(name: str) -> str:
68
- """ANSI color from profile (dashboard) or MD5 hash fallback."""
83
+ """Profile-canonical color if cached, else a deterministic per-name color.
84
+ Never random — same name -> same color on every machine."""
69
85
  if _CACHED_AGENT_COLOR:
70
86
  return _CACHED_AGENT_COLOR
71
87
  h = int(_hashlib.md5(name.encode()).hexdigest()[:8], 16)
88
+ if _TRUECOLOR_OK:
89
+ # Pseudo-random but deterministic 24-bit color from the hash. Clamp
90
+ # mid-range so the color is readable on both dark and light terminals.
91
+ r = max(70, min(220, (h >> 16) & 0xFF))
92
+ g = max(70, min(220, (h >> 8) & 0xFF))
93
+ b = max(70, min(220, h & 0xFF))
94
+ return f"\033[38;2;{r};{g};{b}m"
72
95
  return _ANSI_COLORS[h % len(_ANSI_COLORS)]
73
96
 
74
97
 
@@ -846,6 +846,16 @@ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str
846
846
  except Exception:
847
847
  pass # Soft-fail — boot must proceed regardless.
848
848
 
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
858
+
849
859
  # ── Welcome banner with unique ASCII art + personality ──────────
850
860
  try:
851
861
  from .ascii_art import generate_art, render_welcome
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.10
3
+ Version: 2.11.12
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,6 +1,7 @@
1
1
  README.md
2
2
  pyproject.toml
3
3
  meshcode/__init__.py
4
+ meshcode/__main__.py
4
5
  meshcode/_stop_hook_template.py
5
6
  meshcode/ascii_art.py
6
7
  meshcode/cli.py
@@ -195,6 +196,7 @@ meshcode/scripts/check_secrets.py
195
196
  meshcode/scripts/race_rate_harness.py
196
197
  tests/test_auto_update_hardening.py
197
198
  tests/test_boot_bug_regression.py
199
+ tests/test_color_truecolor.py
198
200
  tests/test_core.py
199
201
  tests/test_cross_agent_messaging.py
200
202
  tests/test_doctor.py
@@ -202,6 +204,7 @@ tests/test_epistemic_v1_python_sdk.py
202
204
  tests/test_epistemic_v1_stop_conditions.py
203
205
  tests/test_esc_deaf_state.py
204
206
  tests/test_exceptions.py
207
+ tests/test_init_device_code.py
205
208
  tests/test_install_guard.py
206
209
  tests/test_lease_sigterm_release.py
207
210
  tests/test_mark_read_batch.py
@@ -211,6 +214,7 @@ tests/test_rls_cross_tenant.py
211
214
  tests/test_rpc_migrations.py
212
215
  tests/test_security_regressions.py
213
216
  tests/test_sentinel.py
217
+ tests/test_setup_path.py
214
218
  tests/test_sleep_signals.py
215
219
  tests/test_status_enum_coverage.py
216
220
  tests/test_stay_on_loop_hook.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meshcode"
7
- version = "2.11.10"
7
+ version = "2.11.12"
8
8
  description = "Real-time communication between AI agents — Supabase-backed CLI"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -0,0 +1,83 @@
1
+ """Tests for BUG-AUDIO-COLOR (task e552412a) — truecolor agent colors."""
2
+ import os
3
+
4
+ import pytest
5
+
6
+
7
+ @pytest.fixture(autouse=True)
8
+ def _set_required_env(monkeypatch):
9
+ """server.py requires these env vars at import."""
10
+ monkeypatch.setenv("MESHCODE_PROJECT", "test")
11
+ monkeypatch.setenv("MESHCODE_AGENT", "test")
12
+ monkeypatch.setenv("MESHCODE_PROJECT_ID", "00000000-0000-0000-0000-000000000000")
13
+ yield
14
+
15
+
16
+ def test_hex_to_ansi_truecolor_format(monkeypatch):
17
+ from meshcode.meshcode_mcp import server as srv
18
+ monkeypatch.setattr(srv, "_TRUECOLOR_OK", True)
19
+ assert srv._hex_to_ansi("#E53935") == "\033[38;2;229;57;53m"
20
+ assert srv._hex_to_ansi("E53935") == "\033[38;2;229;57;53m"
21
+
22
+
23
+ def test_hex_to_ansi_4bit_fallback(monkeypatch):
24
+ from meshcode.meshcode_mcp import server as srv
25
+ monkeypatch.setattr(srv, "_TRUECOLOR_OK", False)
26
+ result = srv._hex_to_ansi("#E53935")
27
+ # 4-bit ANSI escape, one of the palette entries
28
+ assert result in srv._ANSI_COLORS
29
+ assert result.startswith("\033[")
30
+
31
+
32
+ def test_hex_to_ansi_malformed_returns_none(monkeypatch):
33
+ from meshcode.meshcode_mcp import server as srv
34
+ monkeypatch.setattr(srv, "_TRUECOLOR_OK", True)
35
+ assert srv._hex_to_ansi("not-hex") is None
36
+ assert srv._hex_to_ansi("#zzzzzz") is None
37
+
38
+
39
+ def test_agent_color_deterministic_truecolor(monkeypatch):
40
+ """Same name → same color across calls in truecolor mode."""
41
+ from meshcode.meshcode_mcp import server as srv
42
+ monkeypatch.setattr(srv, "_CACHED_AGENT_COLOR", None)
43
+ monkeypatch.setattr(srv, "_TRUECOLOR_OK", True)
44
+ a = srv._agent_color("commander")
45
+ b = srv._agent_color("commander")
46
+ assert a == b
47
+ assert a.startswith("\033[38;2;") # truecolor
48
+
49
+
50
+ def test_agent_color_deterministic_4bit(monkeypatch):
51
+ from meshcode.meshcode_mcp import server as srv
52
+ monkeypatch.setattr(srv, "_CACHED_AGENT_COLOR", None)
53
+ monkeypatch.setattr(srv, "_TRUECOLOR_OK", False)
54
+ a = srv._agent_color("commander")
55
+ b = srv._agent_color("commander")
56
+ assert a == b
57
+ assert a in srv._ANSI_COLORS
58
+
59
+
60
+ def test_agent_color_cached_overrides(monkeypatch):
61
+ """If _CACHED_AGENT_COLOR is set (from profile fetch), use that."""
62
+ from meshcode.meshcode_mcp import server as srv
63
+ monkeypatch.setattr(srv, "_CACHED_AGENT_COLOR", "\033[38;2;100;200;50m")
64
+ monkeypatch.setattr(srv, "_TRUECOLOR_OK", True)
65
+ assert srv._agent_color("any-name") == "\033[38;2;100;200;50m"
66
+
67
+
68
+ def test_agent_color_clamped_to_mid_range(monkeypatch):
69
+ """Truecolor RGB components clamped to [70, 220] for readability on
70
+ both dark and light terminals."""
71
+ from meshcode.meshcode_mcp import server as srv
72
+ monkeypatch.setattr(srv, "_CACHED_AGENT_COLOR", None)
73
+ monkeypatch.setattr(srv, "_TRUECOLOR_OK", True)
74
+ # Try many names to exercise the clamp
75
+ for name in ("a", "bbbb", "cccccc", "dddddddd", "eeeeeeeeee"):
76
+ c = srv._agent_color(name)
77
+ # Parse the \033[38;2;R;G;Bm
78
+ body = c.lstrip("\033[").rstrip("m")
79
+ parts = body.split(";")
80
+ r, g, b = int(parts[2]), int(parts[3]), int(parts[4])
81
+ assert 70 <= r <= 220, f"R={r} out of range for {name}"
82
+ assert 70 <= g <= 220, f"G={g} out of range for {name}"
83
+ assert 70 <= b <= 220, f"B={b} out of range for {name}"
@@ -0,0 +1,68 @@
1
+ """Tests for B6.2 — device-code OAuth flow in `meshcode init`.
2
+
3
+ The full init() flow is tightly coupled to comms_v4's main() dispatch and
4
+ has side effects (sys.exit, webbrowser, input, supabase). Rather than spin
5
+ up subprocesses with monkey-patches that don't survive runpy re-imports,
6
+ this module verifies the *contracts* and the small helpers — the integration
7
+ is exercised by the live RPC handshake in production.
8
+ """
9
+ import hashlib
10
+ import platform
11
+
12
+
13
+ def test_device_fingerprint_is_stable():
14
+ """Same machine -> same fingerprint across calls (init can't pair if it
15
+ drifts mid-session)."""
16
+ def _fp():
17
+ try:
18
+ hn = platform.node() or "unknown"
19
+ return hashlib.sha256(
20
+ f"{platform.system()}|{hn}|{platform.machine()}".encode()
21
+ ).hexdigest()[:32]
22
+ except Exception:
23
+ return hashlib.sha256(b"meshcode-anon-device").hexdigest()[:32]
24
+
25
+ a = _fp()
26
+ b = _fp()
27
+ assert a == b
28
+ assert len(a) == 32
29
+ # Hex string
30
+ assert all(c in "0123456789abcdef" for c in a)
31
+
32
+
33
+ def test_init_branch_has_no_apikey_paste_prompt():
34
+ """Source-level invariant: comms_v4.py 'cmd == init' block must not call
35
+ builtins.input() for the API key (security regression sentinel)."""
36
+ import pathlib
37
+ src = pathlib.Path("meshcode/comms_v4.py").read_text()
38
+ init_idx = src.find('if cmd == "init":')
39
+ assert init_idx >= 0
40
+ # Find the next top-level `elif cmd ==` or `if cmd not in _NO_AUTH_CMDS`
41
+ end_idx = min(
42
+ i for i in (
43
+ src.find("if cmd not in _NO_AUTH_CMDS:", init_idx + 1),
44
+ src.find("elif cmd == ", init_idx + 1),
45
+ ) if i > 0
46
+ )
47
+ init_block = src[init_idx:end_idx]
48
+ # Must not contain input("Paste your API key") or similar paste prompts
49
+ paste_prompts = ['Paste your API key', 'input(" Paste']
50
+ for p in paste_prompts:
51
+ assert p not in init_block, (
52
+ f"init block must not contain {p!r} — B6.2 security goal "
53
+ "(no terminal paste of long-lived secrets)"
54
+ )
55
+
56
+
57
+ def test_init_branch_calls_device_code_first():
58
+ """Source-level: device-code RPC must be called BEFORE legacy pairing."""
59
+ import pathlib
60
+ src = pathlib.Path("meshcode/comms_v4.py").read_text()
61
+ init_idx = src.find('if cmd == "init":')
62
+ end_idx = src.find("if cmd not in _NO_AUTH_CMDS:", init_idx)
63
+ init_block = src[init_idx:end_idx]
64
+ dc_idx = init_block.find("mc_device_code_create")
65
+ legacy_idx = init_block.find("mc_create_pairing_session")
66
+ assert dc_idx > 0, "init must call mc_device_code_create"
67
+ assert legacy_idx > 0, "init must keep mc_create_pairing_session fallback"
68
+ assert dc_idx < legacy_idx, "device-code must be tried before legacy"