meshagent-cli 0.51.4__tar.gz → 0.51.6__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 (172) hide show
  1. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/PKG-INFO +17 -17
  2. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/containers.py +13 -4
  3. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/containers_test.py +32 -0
  4. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/image.py +159 -73
  5. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/image_test.py +69 -6
  6. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/routes.py +12 -0
  7. meshagent_cli-0.51.6/meshagent/cli/version.py +1 -0
  8. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent_cli.egg-info/PKG-INFO +17 -17
  9. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent_cli.egg-info/requires.txt +16 -16
  10. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/pyproject.toml +16 -16
  11. meshagent_cli-0.51.4/meshagent/cli/version.py +0 -1
  12. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/LICENSE +0 -0
  13. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/README.md +0 -0
  14. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/__init__.py +0 -0
  15. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/_filedrop.py +0 -0
  16. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/agent.py +0 -0
  17. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/agent_cli_options_test.py +0 -0
  18. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/agent_package_cli.py +0 -0
  19. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/agent_package_cli_test.py +0 -0
  20. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/agents.py +0 -0
  21. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/agents_test.py +0 -0
  22. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/ask.py +0 -0
  23. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/ask_test.py +0 -0
  24. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/async_typer.py +0 -0
  25. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/async_typer_test.py +0 -0
  26. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/auth.py +0 -0
  27. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/auth_async.py +0 -0
  28. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/auth_async_test.py +0 -0
  29. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/auth_test.py +0 -0
  30. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/call.py +0 -0
  31. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/chatbot.py +0 -0
  32. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/cli.py +0 -0
  33. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/cli_mcp.py +0 -0
  34. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/cli_test.py +0 -0
  35. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/common_options.py +0 -0
  36. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/config.py +0 -0
  37. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/config_test.py +0 -0
  38. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create.py +0 -0
  39. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/__init__.py +0 -0
  40. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/backend-agent/server.py +0 -0
  41. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/contact-form/server.py +0 -0
  42. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/slack-channel/channel.py +0 -0
  43. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/slack-channel/scripts/configure-slack.py +0 -0
  44. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/slack-channel/scripts/setup-slack-dev-route.py +0 -0
  45. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/slack-channel/server.py +0 -0
  46. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/task-queue-dashboard/server.py +0 -0
  47. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/telegram-channel/channel.py +0 -0
  48. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/telegram-channel/scripts/configure-telegram.py +0 -0
  49. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/telegram-channel/scripts/create-bot-token.py +0 -0
  50. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/telegram-channel/server.py +0 -0
  51. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/twilio-channel/channel.py +0 -0
  52. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/twilio-channel/server.py +0 -0
  53. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/webserver/server.py +0 -0
  54. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/whatsapp-channel/channel.py +0 -0
  55. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_project_templates/python/whatsapp-channel/server.py +0 -0
  56. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/create_test.py +0 -0
  57. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/dataset.py +0 -0
  58. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/dataset_test.py +0 -0
  59. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/developer.py +0 -0
  60. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/developer_test.py +0 -0
  61. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/doctor.py +0 -0
  62. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/doctor_dockerfiles/__init__.py +0 -0
  63. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/doctor_templates/__init__.py +0 -0
  64. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/doctor_test.py +0 -0
  65. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/feeds.py +0 -0
  66. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/feeds_test.py +0 -0
  67. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/helper.py +0 -0
  68. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/helper_test.py +0 -0
  69. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/helpers.py +0 -0
  70. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/host.py +0 -0
  71. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/iam.py +0 -0
  72. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/iam_test.py +0 -0
  73. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/launch.py +0 -0
  74. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/launch_test.py +0 -0
  75. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/llm.py +0 -0
  76. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/llm_test.py +0 -0
  77. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/local_settings.py +0 -0
  78. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/local_settings_test.py +0 -0
  79. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/mailbot.py +0 -0
  80. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/mailbot_test.py +0 -0
  81. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/mailboxes.py +0 -0
  82. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/mailboxes_test.py +0 -0
  83. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/meeting_transcriber.py +0 -0
  84. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/memory.py +0 -0
  85. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/memory_test.py +0 -0
  86. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/meshagent_images.py +0 -0
  87. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/meshagent_images_test.py +0 -0
  88. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/messaging.py +0 -0
  89. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/messaging_test.py +0 -0
  90. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/mounts.py +0 -0
  91. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/multi.py +0 -0
  92. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/oci_archive.py +0 -0
  93. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/oci_archive_test.py +0 -0
  94. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/participant_token.py +0 -0
  95. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/port.py +0 -0
  96. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/port_test.py +0 -0
  97. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/preamble_rules.py +0 -0
  98. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/preamble_rules_test.py +0 -0
  99. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/process.py +0 -0
  100. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/process_live_test.py +0 -0
  101. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/process_test.py +0 -0
  102. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/projects.py +0 -0
  103. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/projects_test.py +0 -0
  104. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/queue.py +0 -0
  105. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/queue_test.py +0 -0
  106. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/registry.py +0 -0
  107. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/registry_test.py +0 -0
  108. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/room.py +0 -0
  109. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/room_connect.py +0 -0
  110. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/room_connect_test.py +0 -0
  111. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/room_services.py +0 -0
  112. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/room_services_test.py +0 -0
  113. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/rooms.py +0 -0
  114. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/rooms_test.py +0 -0
  115. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/root_commands.py +0 -0
  116. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/root_commands_test.py +0 -0
  117. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/routes_test.py +0 -0
  118. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/sample_channel_http_test.py +0 -0
  119. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/sample_channel_live_e2e_test.py +0 -0
  120. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/scheduled_tasks.py +0 -0
  121. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/scheduled_tasks_test.py +0 -0
  122. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/secrets.py +0 -0
  123. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/secrets_test.py +0 -0
  124. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/service_accounts.py +0 -0
  125. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/service_accounts_test.py +0 -0
  126. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/services.py +0 -0
  127. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/services_test.py +0 -0
  128. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/sessions.py +0 -0
  129. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/sessions_test.py +0 -0
  130. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/sqlite.py +0 -0
  131. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/sqlite_test.py +0 -0
  132. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/storage.py +0 -0
  133. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/storage_test.py +0 -0
  134. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/subscriptions.py +0 -0
  135. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/sync.py +0 -0
  136. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/sync_test.py +0 -0
  137. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/task_runner.py +0 -0
  138. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/task_runner_test.py +0 -0
  139. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/test.py +0 -0
  140. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/testing.py +0 -0
  141. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/thread_inspect.py +0 -0
  142. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/thread_inspect_test.py +0 -0
  143. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/thread_sidebar.py +0 -0
  144. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tool_call_summary.py +0 -0
  145. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tool_call_summary_test.py +0 -0
  146. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tool_integrations.py +0 -0
  147. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tool_integrations_test.py +0 -0
  148. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/__init__.py +0 -0
  149. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/auth_switch.py +0 -0
  150. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/auth_switch_test.py +0 -0
  151. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/create.py +0 -0
  152. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/deploy_room.py +0 -0
  153. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/deploy_room_test.py +0 -0
  154. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/project_activate.py +0 -0
  155. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/project_activate_test.py +0 -0
  156. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/setup.py +0 -0
  157. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/setup_splash_frames.py +0 -0
  158. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/tui/setup_test.py +0 -0
  159. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/version_check.py +0 -0
  160. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/version_check_test.py +0 -0
  161. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/voicebot.py +0 -0
  162. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/volumes.py +0 -0
  163. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/volumes_test.py +0 -0
  164. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/webserver.py +0 -0
  165. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/webserver_test.py +0 -0
  166. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/worker.py +0 -0
  167. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent/cli/worker_test.py +0 -0
  168. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent_cli.egg-info/SOURCES.txt +0 -0
  169. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent_cli.egg-info/dependency_links.txt +0 -0
  170. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent_cli.egg-info/entry_points.txt +0 -0
  171. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/meshagent_cli.egg-info/top_level.txt +0 -0
  172. {meshagent_cli-0.51.4 → meshagent_cli-0.51.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshagent-cli
3
- Version: 0.51.4
3
+ Version: 0.51.6
4
4
  Summary: CLI for Meshagent
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Documentation, https://docs.meshagent.com
@@ -22,8 +22,8 @@ Requires-Dist: openpyxl~=3.1
22
22
  Requires-Dist: xlsxwriter~=3.2
23
23
  Requires-Dist: pathspec<2,>=1.0.3
24
24
  Requires-Dist: zstandard~=0.25.0
25
- Requires-Dist: meshagent-llm-proxy==0.51.4
26
- Requires-Dist: meshagent-codex==0.51.4
25
+ Requires-Dist: meshagent-llm-proxy==0.51.6
26
+ Requires-Dist: meshagent-codex==0.51.6
27
27
  Requires-Dist: rich~=14.3.0
28
28
  Requires-Dist: sounddevice~=0.5
29
29
  Requires-Dist: textual<9.0,>=8.2.3
@@ -32,23 +32,23 @@ Requires-Dist: prompt-toolkit~=3.0.52
32
32
  Requires-Dist: ascii-magic~=2.3
33
33
  Requires-Dist: pillow~=11.3.0
34
34
  Provides-Extra: all
35
- Requires-Dist: meshagent-agents[all]==0.51.4; extra == "all"
36
- Requires-Dist: meshagent-api[all]==0.51.4; extra == "all"
37
- Requires-Dist: meshagent-commoncrawl==0.51.4; extra == "all"
38
- Requires-Dist: meshagent-scrapy==0.51.4; extra == "all"
39
- Requires-Dist: meshagent-computers==0.51.4; extra == "all"
40
- Requires-Dist: meshagent-openai==0.51.4; extra == "all"
41
- Requires-Dist: meshagent-anthropic==0.51.4; extra == "all"
42
- Requires-Dist: meshagent-otel==0.51.4; extra == "all"
43
- Requires-Dist: meshagent-mcp==0.51.4; extra == "all"
44
- Requires-Dist: meshagent-tools==0.51.4; extra == "all"
35
+ Requires-Dist: meshagent-agents[all]==0.51.6; extra == "all"
36
+ Requires-Dist: meshagent-api[all]==0.51.6; extra == "all"
37
+ Requires-Dist: meshagent-commoncrawl==0.51.6; extra == "all"
38
+ Requires-Dist: meshagent-scrapy==0.51.6; extra == "all"
39
+ Requires-Dist: meshagent-computers==0.51.6; extra == "all"
40
+ Requires-Dist: meshagent-openai==0.51.6; extra == "all"
41
+ Requires-Dist: meshagent-anthropic==0.51.6; extra == "all"
42
+ Requires-Dist: meshagent-otel==0.51.6; extra == "all"
43
+ Requires-Dist: meshagent-mcp==0.51.6; extra == "all"
44
+ Requires-Dist: meshagent-tools==0.51.6; extra == "all"
45
45
  Requires-Dist: supabase-auth~=2.28.0; extra == "all"
46
46
  Requires-Dist: prompt-toolkit~=3.0.52; extra == "all"
47
47
  Provides-Extra: mcp-service
48
- Requires-Dist: meshagent-agents[all]==0.51.4; extra == "mcp-service"
49
- Requires-Dist: meshagent-api==0.51.4; extra == "mcp-service"
50
- Requires-Dist: meshagent-mcp==0.51.4; extra == "mcp-service"
51
- Requires-Dist: meshagent-tools==0.51.4; extra == "mcp-service"
48
+ Requires-Dist: meshagent-agents[all]==0.51.6; extra == "mcp-service"
49
+ Requires-Dist: meshagent-api==0.51.6; extra == "mcp-service"
50
+ Requires-Dist: meshagent-mcp==0.51.6; extra == "mcp-service"
51
+ Requires-Dist: meshagent-tools==0.51.6; extra == "mcp-service"
52
52
  Requires-Dist: supabase-auth~=2.28.0; extra == "mcp-service"
53
53
  Dynamic: license-file
54
54
 
@@ -365,9 +365,15 @@ async def _drain_stream_plain(stream, *, show_progress: bool = True):
365
365
 
366
366
  t1 = asyncio.create_task(_logs())
367
367
  t2 = asyncio.create_task(_prog())
368
+ completed = False
368
369
  try:
369
- return await stream
370
+ result = await stream
371
+ completed = True
372
+ return result
370
373
  finally:
374
+ if not completed:
375
+ t1.cancel()
376
+ t2.cancel()
371
377
  await asyncio.gather(t1, t2, return_exceptions=True)
372
378
 
373
379
 
@@ -511,8 +517,10 @@ async def _drain_stream_pretty(stream):
511
517
  t_logs = asyncio.create_task(_logs())
512
518
  t_prog = asyncio.create_task(_prog())
513
519
  t_ui = asyncio.create_task(_refresh())
520
+ completed = False
514
521
  try:
515
522
  result = await stream
523
+ completed = True
516
524
  return result
517
525
  finally:
518
526
  # Hide any still-visible tasks (e.g., indeterminate ones with total=None)
@@ -520,10 +528,11 @@ async def _drain_stream_pretty(stream):
520
528
  progress.update(tid, visible=False)
521
529
  live.update(render())
522
530
 
523
- for t in (t_logs, t_prog):
524
- await t
525
-
531
+ if not completed:
532
+ t_logs.cancel()
533
+ t_prog.cancel()
526
534
  t_ui.cancel()
535
+ await asyncio.gather(t_logs, t_prog, t_ui, return_exceptions=True)
527
536
 
528
537
 
529
538
  async def _with_client(
@@ -323,6 +323,38 @@ async def test_drain_stream_plain_strips_cri_log_prefixes(
323
323
  assert capsys.readouterr().out == "step 1\nstep 2\n"
324
324
 
325
325
 
326
+ @pytest.mark.asyncio
327
+ async def test_drain_stream_plain_cancels_consumers_when_stream_is_cancelled() -> None:
328
+ consumers_cancelled = 0
329
+
330
+ class _BlockingStream:
331
+ async def _consume(self):
332
+ nonlocal consumers_cancelled
333
+ try:
334
+ await asyncio.Future()
335
+ yield None
336
+ finally:
337
+ consumers_cancelled += 1
338
+
339
+ def logs(self):
340
+ return self._consume()
341
+
342
+ def progress(self):
343
+ return self._consume()
344
+
345
+ def __await__(self):
346
+ return asyncio.Future().__await__()
347
+
348
+ task = asyncio.create_task(containers._drain_stream_plain(_BlockingStream()))
349
+ await asyncio.sleep(0)
350
+ task.cancel()
351
+
352
+ with pytest.raises(asyncio.CancelledError):
353
+ await asyncio.wait_for(task, timeout=0.5)
354
+
355
+ assert consumers_cancelled == 2
356
+
357
+
326
358
  @pytest.mark.asyncio
327
359
  async def test_stream_build_job_logs_and_wait_for_exit_uses_build_log_stream(
328
360
  monkeypatch: pytest.MonkeyPatch,
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import contextlib
4
5
  import hashlib
5
6
  import json
6
7
  import os
@@ -12,7 +13,7 @@ import tempfile
12
13
  from collections.abc import AsyncIterator, Awaitable, Callable
13
14
  from dataclasses import dataclass, field
14
15
  from pathlib import Path, PurePosixPath
15
- from typing import Annotated, Literal, Optional, Protocol, TYPE_CHECKING
16
+ from typing import Annotated, Literal, Optional, Protocol, TYPE_CHECKING, TypeVar
16
17
  from urllib.parse import urlparse
17
18
 
18
19
  import typer
@@ -124,8 +125,8 @@ class _DeployDomainPromptHandler(Protocol):
124
125
 
125
126
  app = async_typer.AsyncTyper(help="Pack local directories as OCI images")
126
127
  _BUILD_CONTEXT_CHUNK_SIZE = 1024 * 1024
127
- _BUILD_WAIT_TIMEOUT_SECONDS = 600.0
128
128
  _CLIENT_CLOSE_TIMEOUT_SECONDS = 2.0
129
+ _OPERATION_CANCEL_TIMEOUT_SECONDS = 1.0
129
130
  _DEFAULT_CONTEXT_MOUNT_PATH = "/context"
130
131
  _DEFAULT_REPOSITORY_TOKEN_TTL_SECONDS = 3600
131
132
  _DEPLOY_CACHE_CLEANUP_TIMEOUT_SECONDS = 30.0
@@ -151,6 +152,7 @@ _PROJECT_KEY_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
151
152
  _COOKIE_VALIDATION_METHOD = "cookie"
152
153
  DeployValidationMode = Literal["default", "cookie", "none"]
153
154
  ContainerTemplateOption = Literal["agent", "none"]
155
+ _T = TypeVar("_T")
154
156
  _RESERVED_ROOM_SERVICE_PORTS_TEXT = ", ".join(
155
157
  str(port) for port in sorted(RESERVED_ROOM_SERVICE_PORTS)
156
158
  )
@@ -3758,6 +3760,7 @@ async def _wait_for_deployed_service_live(
3758
3760
  domain: str | None,
3759
3761
  liveness_path: str | None,
3760
3762
  queue_backed_route: bool = False,
3763
+ expected_restart_count: int = 0,
3761
3764
  status_handler: Callable[[str], Awaitable[None]] | None = None,
3762
3765
  log_handler: Callable[[str], Awaitable[None]] | None = None,
3763
3766
  ) -> None:
@@ -3825,7 +3828,10 @@ async def _wait_for_deployed_service_live(
3825
3828
  await asyncio.sleep(_DEPLOY_WAIT_POLL_INTERVAL_SECONDS)
3826
3829
  continue
3827
3830
 
3828
- if state.restart_count > 0:
3831
+ if (
3832
+ state.restart_count > expected_restart_count
3833
+ and state.last_exit_code not in (None, 0)
3834
+ ):
3829
3835
  if status_handler is None:
3830
3836
  _print_service_exited_before_live(
3831
3837
  service_name=service_name,
@@ -3860,23 +3866,17 @@ async def _wait_for_deployed_service_live(
3860
3866
  and container_id is not None
3861
3867
  and container_id != active_logs.container_id
3862
3868
  ):
3863
- if status_handler is None:
3864
- _print_service_exited_before_live(
3865
- service_name=service_name,
3866
- service_id=service_id,
3867
- exit_code=state.last_exit_code,
3868
- )
3869
- else:
3870
- exit_code_text = (
3871
- str(state.last_exit_code)
3872
- if state.last_exit_code is not None
3873
- else "unknown"
3874
- )
3875
- await status_handler(
3876
- "Service container changed before the service was live: "
3877
- f"{service_name} ({service_id}), exit code {exit_code_text}"
3878
- )
3879
- raise typer.Exit(code=1)
3869
+ await _stop_deploy_log_stream(active_logs=active_logs)
3870
+ await _emit_deploy_status(
3871
+ status_handler,
3872
+ rich_message=f"[cyan]Tailing container logs:[/] {container_id}",
3873
+ plain_message=f"Tailing container logs: {container_id}",
3874
+ )
3875
+ active_logs = _start_deploy_log_stream(
3876
+ client=client,
3877
+ container_id=container_id,
3878
+ log_handler=log_handler,
3879
+ )
3880
3880
 
3881
3881
  if container_id is None or state.state != "running":
3882
3882
  await asyncio.sleep(_DEPLOY_WAIT_POLL_INTERVAL_SECONDS)
@@ -3974,6 +3974,70 @@ async def _build_local_context_archive(
3974
3974
  raise
3975
3975
 
3976
3976
 
3977
+ def _consume_background_task_result(task: asyncio.Future[object]) -> None:
3978
+ if task.cancelled():
3979
+ return
3980
+ with contextlib.suppress(Exception):
3981
+ task.exception()
3982
+
3983
+
3984
+ async def _await_room_build_operation(
3985
+ *,
3986
+ client: RoomClient,
3987
+ operation: Awaitable[_T],
3988
+ operation_name: str,
3989
+ ) -> _T:
3990
+ loop = asyncio.get_running_loop()
3991
+ disconnected = loop.create_future()
3992
+
3993
+ def on_disconnected(*, reason: str | None = None) -> None:
3994
+ if not disconnected.done():
3995
+ disconnected.set_result(reason)
3996
+
3997
+ observes_disconnects = isinstance(client, RoomClient)
3998
+ if observes_disconnects:
3999
+ client.on("disconnected", on_disconnected)
4000
+ operation_task = asyncio.ensure_future(operation)
4001
+ try:
4002
+ done, _ = await asyncio.wait(
4003
+ {operation_task, disconnected},
4004
+ return_when=asyncio.FIRST_COMPLETED,
4005
+ )
4006
+ if operation_task in done:
4007
+ return await operation_task
4008
+ reason = disconnected.result()
4009
+ detail = f": {reason}" if reason else ""
4010
+ raise RuntimeError(
4011
+ f"room connection lost while {operation_name}{detail}; "
4012
+ "the in-flight operation cannot be resumed safely. Retry the deploy."
4013
+ )
4014
+ finally:
4015
+ if observes_disconnects:
4016
+ client.off("disconnected", on_disconnected)
4017
+ if not disconnected.done():
4018
+ disconnected.cancel()
4019
+ if not operation_task.done():
4020
+ operation_task.cancel()
4021
+ done, _ = await asyncio.wait(
4022
+ {operation_task}, timeout=_OPERATION_CANCEL_TIMEOUT_SECONDS
4023
+ )
4024
+ if operation_task not in done:
4025
+ operation_task.add_done_callback(_consume_background_task_result)
4026
+
4027
+
4028
+ async def _close_room_client(client: RoomClient) -> None:
4029
+ close_task = asyncio.create_task(client.__aexit__(None, None, None))
4030
+ done, _ = await asyncio.wait({close_task}, timeout=_CLIENT_CLOSE_TIMEOUT_SECONDS)
4031
+ if close_task in done:
4032
+ await close_task
4033
+ return
4034
+ close_task.cancel()
4035
+ close_task.add_done_callback(_consume_background_task_result)
4036
+ sys.stderr.write(
4037
+ "Room client did not close promptly after the build; abandoning cleanup.\n"
4038
+ )
4039
+
4040
+
3977
4041
  async def _run_image_pack_stage(
3978
4042
  *,
3979
4043
  resolved_project_id: str | None,
@@ -4024,36 +4088,38 @@ async def _run_image_pack_stage(
4024
4088
  )
4025
4089
  },
4026
4090
  )
4027
- build_id = await client.containers.build(
4028
- tags=[parsed_tag.value],
4029
- mount_path=_DEFAULT_CONTEXT_MOUNT_PATH,
4030
- context_path=_DEFAULT_CONTEXT_MOUNT_PATH,
4031
- dockerfile_path=_generated_pack_dockerfile_path(
4032
- mount_path=_DEFAULT_CONTEXT_MOUNT_PATH
4091
+ build_id = await _await_room_build_operation(
4092
+ client=client,
4093
+ operation=client.containers.build(
4094
+ tags=[parsed_tag.value],
4095
+ mount_path=_DEFAULT_CONTEXT_MOUNT_PATH,
4096
+ context_path=_DEFAULT_CONTEXT_MOUNT_PATH,
4097
+ dockerfile_path=_generated_pack_dockerfile_path(
4098
+ mount_path=_DEFAULT_CONTEXT_MOUNT_PATH
4099
+ ),
4100
+ optimize_image=True,
4101
+ private=False,
4102
+ credentials=registry_credentials,
4103
+ builder_name=_default_builder_name(client=client),
4104
+ chunks=_iter_file_chunks(archive_path),
4105
+ size=archive_size,
4033
4106
  ),
4034
- optimize_image=True,
4035
- private=False,
4036
- credentials=registry_credentials,
4037
- builder_name=_default_builder_name(client=client),
4038
- chunks=_iter_file_chunks(archive_path),
4039
- size=archive_size,
4107
+ operation_name="uploading the build context",
4108
+ )
4109
+ exit_code = await _await_room_build_operation(
4110
+ client=client,
4111
+ operation=_stream_build_job_logs_and_wait_for_exit(
4112
+ client=client,
4113
+ build_id=build_id,
4114
+ ),
4115
+ operation_name="waiting for the image build and publish",
4040
4116
  )
4041
- try:
4042
- exit_code = await asyncio.wait_for(
4043
- _stream_build_job_logs_and_wait_for_exit(
4044
- client=client,
4045
- build_id=build_id,
4046
- ),
4047
- timeout=_BUILD_WAIT_TIMEOUT_SECONDS,
4048
- )
4049
- except TimeoutError as exc:
4050
- raise RuntimeError("timed out waiting for image build") from exc
4051
4117
  if exit_code != 0:
4052
4118
  raise typer.Exit(code=exit_code)
4053
4119
  finally:
4054
4120
  if context_archive_temp_dir is not None:
4055
4121
  context_archive_temp_dir.cleanup()
4056
- await client.__aexit__(None, None, None)
4122
+ await _close_room_client(client)
4057
4123
  await account_client.close()
4058
4124
 
4059
4125
 
@@ -4134,46 +4200,53 @@ async def _run_image_build_stage(
4134
4200
  rich_message=f"[cyan]Uploading build context ({archive_size_text})...[/cyan]",
4135
4201
  plain_message=f"Uploading build context ({archive_size_text})...",
4136
4202
  )
4137
- build_id = await client.containers.build(
4138
- tags=tags,
4139
- mount_path=build_inputs.pack_spec.mount_path,
4140
- context_path=build_inputs.context_path,
4141
- dockerfile_path=build_inputs.dockerfile_path,
4142
- optimize_image=optimize,
4143
- private=private,
4144
- credentials=credentials,
4145
- builder_name=resolved_builder_name,
4146
- chunks=_iter_file_chunks_with_progress(
4147
- path=archive_path,
4203
+ build_id = await _await_room_build_operation(
4204
+ client=client,
4205
+ operation=client.containers.build(
4206
+ tags=tags,
4207
+ mount_path=build_inputs.pack_spec.mount_path,
4208
+ context_path=build_inputs.context_path,
4209
+ dockerfile_path=build_inputs.dockerfile_path,
4210
+ optimize_image=optimize,
4211
+ private=private,
4212
+ credentials=credentials,
4213
+ builder_name=resolved_builder_name,
4214
+ chunks=_iter_file_chunks_with_progress(
4215
+ path=archive_path,
4216
+ size=archive_size,
4217
+ status_handler=status_handler,
4218
+ ),
4148
4219
  size=archive_size,
4149
- status_handler=status_handler,
4150
4220
  ),
4151
- size=archive_size,
4221
+ operation_name="uploading the build context",
4152
4222
  )
4153
4223
  await _emit_deploy_status(
4154
4224
  status_handler,
4155
4225
  rich_message="[cyan]Starting image build...[/cyan]",
4156
4226
  plain_message="Starting image build...",
4157
4227
  )
4158
- try:
4159
- if log_handler is None:
4160
- exit_code = await asyncio.wait_for(
4228
+ if log_handler is None:
4229
+ exit_code = await _await_room_build_operation(
4230
+ client=client,
4231
+ operation=(
4161
4232
  _stream_build_job_logs_and_wait_for_exit(
4162
4233
  client=client, build_id=build_id
4163
- ),
4164
- timeout=_BUILD_WAIT_TIMEOUT_SECONDS,
4165
- )
4166
- else:
4167
- exit_code = await asyncio.wait_for(
4234
+ )
4235
+ ),
4236
+ operation_name="waiting for the image build and publish",
4237
+ )
4238
+ else:
4239
+ exit_code = await _await_room_build_operation(
4240
+ client=client,
4241
+ operation=(
4168
4242
  _stream_build_job_logs_and_wait_for_exit_tui(
4169
4243
  client=client,
4170
4244
  build_id=build_id,
4171
4245
  log_handler=log_handler,
4172
- ),
4173
- timeout=_BUILD_WAIT_TIMEOUT_SECONDS,
4174
- )
4175
- except TimeoutError as exc:
4176
- raise RuntimeError("timed out waiting for image build") from exc
4246
+ )
4247
+ ),
4248
+ operation_name="waiting for the image build and publish",
4249
+ )
4177
4250
  if exit_code != 0:
4178
4251
  await _emit_deploy_status(
4179
4252
  status_handler,
@@ -4181,10 +4254,14 @@ async def _run_image_build_stage(
4181
4254
  plain_message=f"Image build failed: exit code {exit_code}",
4182
4255
  )
4183
4256
  raise typer.Exit(code=exit_code)
4184
- published_image = await _resolve_completed_build_image(
4257
+ published_image = await _await_room_build_operation(
4185
4258
  client=client,
4186
- build_id=build_id,
4187
- parsed_tag=parsed_tag,
4259
+ operation=_resolve_completed_build_image(
4260
+ client=client,
4261
+ build_id=build_id,
4262
+ parsed_tag=parsed_tag,
4263
+ ),
4264
+ operation_name="resolving the published image",
4188
4265
  )
4189
4266
  await _emit_deploy_status(
4190
4267
  status_handler,
@@ -4200,7 +4277,7 @@ async def _run_image_build_stage(
4200
4277
  finally:
4201
4278
  if context_archive_temp_dir is not None:
4202
4279
  context_archive_temp_dir.cleanup()
4203
- await client.__aexit__(None, None, None)
4280
+ await _close_room_client(client)
4204
4281
  await account_client.close()
4205
4282
 
4206
4283
 
@@ -5165,6 +5242,14 @@ async def deploy_image(
5165
5242
  if previous_runtime_state is not None
5166
5243
  else None
5167
5244
  )
5245
+ previous_restart_count = (
5246
+ previous_runtime_state.restart_count
5247
+ if previous_runtime_state is not None
5248
+ else 0
5249
+ )
5250
+ expected_restart_count = previous_restart_count + (
5251
+ 0 if deploy_result.created else 1
5252
+ )
5168
5253
  liveness_path = (
5169
5254
  _resolve_domain_liveness_path(
5170
5255
  service_spec=deploy_plan.spec,
@@ -5191,6 +5276,7 @@ async def deploy_image(
5191
5276
  domain=domain,
5192
5277
  liveness_path=liveness_path,
5193
5278
  queue_backed_route=queue_backed_route,
5279
+ expected_restart_count=expected_restart_count,
5194
5280
  status_handler=status_handler,
5195
5281
  log_handler=log_handler,
5196
5282
  ),
@@ -83,6 +83,58 @@ def _fake_published_build_image(
83
83
  )
84
84
 
85
85
 
86
+ @pytest.mark.asyncio
87
+ async def test_room_build_operation_fails_immediately_on_disconnect(
88
+ monkeypatch: pytest.MonkeyPatch,
89
+ ) -> None:
90
+ class _FakeRoomClient:
91
+ def __init__(self) -> None:
92
+ self.handlers: dict[str, list[object]] = {}
93
+
94
+ def on(self, event_name: str, handler) -> None:
95
+ self.handlers.setdefault(event_name, []).append(handler)
96
+
97
+ def off(self, event_name: str, handler) -> None:
98
+ self.handlers[event_name].remove(handler)
99
+
100
+ def disconnect(self, reason: str) -> None:
101
+ for handler in list(self.handlers.get("disconnected", [])):
102
+ handler(reason=reason)
103
+
104
+ operation_cancelled = asyncio.Event()
105
+
106
+ async def operation() -> None:
107
+ try:
108
+ await asyncio.Future()
109
+ finally:
110
+ operation_cancelled.set()
111
+
112
+ client = _FakeRoomClient()
113
+ monkeypatch.setattr(image, "RoomClient", _FakeRoomClient)
114
+ task = asyncio.create_task(
115
+ image._await_room_build_operation(
116
+ client=client,
117
+ operation=operation(),
118
+ operation_name="waiting for the image build and publish",
119
+ )
120
+ )
121
+ await asyncio.sleep(0)
122
+ client.disconnect("websocket closed with code 1006")
123
+
124
+ with pytest.raises(
125
+ RuntimeError,
126
+ match=(
127
+ "room connection lost while waiting for the image build and publish: "
128
+ "websocket closed with code 1006; the in-flight operation cannot be "
129
+ "resumed safely"
130
+ ),
131
+ ):
132
+ await asyncio.wait_for(task, timeout=0.5)
133
+
134
+ assert operation_cancelled.is_set()
135
+ assert client.handlers["disconnected"] == []
136
+
137
+
86
138
  def _route_for_service(
87
139
  *, domain: str, room_name: str, port: str, service_id: str
88
140
  ) -> Route:
@@ -2133,6 +2185,13 @@ async def test_run_image_build_stage_requests_repository_token_and_prepends_regi
2133
2185
  class _FakeRoomClient:
2134
2186
  def __init__(self) -> None:
2135
2187
  self.containers = _FakeContainersClient()
2188
+ self.handlers: dict[str, list[object]] = {}
2189
+
2190
+ def on(self, event_name: str, handler) -> None:
2191
+ self.handlers.setdefault(event_name, []).append(handler)
2192
+
2193
+ def off(self, event_name: str, handler) -> None:
2194
+ self.handlers[event_name].remove(handler)
2136
2195
 
2137
2196
  async def __aexit__(self, exc_type, exc, tb) -> None:
2138
2197
  del exc_type, exc, tb
@@ -2431,7 +2490,8 @@ async def test_wait_for_deployed_service_live_streams_logs_and_checks_service_li
2431
2490
  ServiceRuntimeState(
2432
2491
  service_id="service-1",
2433
2492
  state="running",
2434
- container_id="container-1",
2493
+ container_id="container-2",
2494
+ restart_count=3,
2435
2495
  status=ServiceRuntimeStatus(
2436
2496
  ports=[
2437
2497
  ServicePortRuntimeState(
@@ -2445,7 +2505,8 @@ async def test_wait_for_deployed_service_live_streams_logs_and_checks_service_li
2445
2505
  ServiceRuntimeState(
2446
2506
  service_id="service-1",
2447
2507
  state="running",
2448
- container_id="container-1",
2508
+ container_id="container-2",
2509
+ restart_count=3,
2449
2510
  status=ServiceRuntimeStatus(
2450
2511
  ports=[
2451
2512
  ServicePortRuntimeState(
@@ -2457,7 +2518,6 @@ async def test_wait_for_deployed_service_live_streams_logs_and_checks_service_li
2457
2518
  ),
2458
2519
  ),
2459
2520
  ]
2460
- fake_active_logs = SimpleNamespace(container_id="container-1")
2461
2521
 
2462
2522
  class _FakeServices:
2463
2523
  def __init__(self, runtime_states: list[ServiceRuntimeState]) -> None:
@@ -2479,7 +2539,7 @@ async def test_wait_for_deployed_service_live_streams_logs_and_checks_service_li
2479
2539
  def _fake_start_deploy_log_stream(*, client, container_id: str, log_handler=None):
2480
2540
  del client, log_handler
2481
2541
  captured["started_logs"].append(container_id)
2482
- return fake_active_logs
2542
+ return SimpleNamespace(container_id=container_id)
2483
2543
 
2484
2544
  async def _fake_stop_deploy_log_stream(*, active_logs) -> None:
2485
2545
  captured["stopped_logs"].append(active_logs)
@@ -2508,8 +2568,11 @@ async def test_wait_for_deployed_service_live_streams_logs_and_checks_service_li
2508
2568
  liveness_path="/ready",
2509
2569
  )
2510
2570
 
2511
- assert captured["started_logs"] == ["container-1"]
2512
- assert captured["stopped_logs"] == [fake_active_logs]
2571
+ assert captured["started_logs"] == ["container-1", "container-2"]
2572
+ assert [active.container_id for active in captured["stopped_logs"]] == [
2573
+ "container-1",
2574
+ "container-2",
2575
+ ]
2513
2576
  assert any("Service liveness is ready" in message for message in captured["prints"])
2514
2577
 
2515
2578
 
@@ -460,6 +460,11 @@ async def route_update(
460
460
  if current_content is not None
461
461
  else ""
462
462
  ),
463
+ notFound=(
464
+ current_content.notFound
465
+ if current_content is not None
466
+ else None
467
+ ),
463
468
  cors=(
464
469
  parsed_cors
465
470
  if parsed_cors is not None
@@ -512,6 +517,13 @@ async def route_update(
512
517
  current_path.pathType if current_path is not None else "prefix"
513
518
  ),
514
519
  "stripPrefix": strip_prefix,
520
+ **(
521
+ {"unavailable": current_path.unavailable}
522
+ if current_path is not None
523
+ and target.get("targetPort") is not None
524
+ and current_path.unavailable is not None
525
+ else {}
526
+ ),
515
527
  **target,
516
528
  },
517
529
  *[item.model_dump(mode="python") for item in route.spec.paths[1:]],
@@ -0,0 +1 @@
1
+ __version__ = "0.51.6"