hud-python 0.4.56__tar.gz → 0.4.57__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.

Potentially problematic release.


This version of hud-python might be problematic. Click here for more details.

Files changed (304) hide show
  1. {hud_python-0.4.56 → hud_python-0.4.57}/PKG-INFO +1 -1
  2. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/dev.py +24 -4
  3. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/flows/dev.py +6 -17
  4. hud_python-0.4.57/hud/cli/tests/test_convert.py +367 -0
  5. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/version_check.py +7 -6
  6. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/base.py +28 -2
  7. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/fastmcp.py +2 -2
  8. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/mcp_use.py +2 -2
  9. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/tests/test_protocol.py +9 -3
  10. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/server.py +59 -0
  11. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/test_version.py +1 -1
  12. {hud_python-0.4.56 → hud_python-0.4.57}/hud/version.py +1 -1
  13. {hud_python-0.4.56 → hud_python-0.4.57}/pyproject.toml +1 -1
  14. {hud_python-0.4.56 → hud_python-0.4.57}/.gitignore +0 -0
  15. {hud_python-0.4.56 → hud_python-0.4.57}/LICENSE +0 -0
  16. {hud_python-0.4.56 → hud_python-0.4.57}/README.md +0 -0
  17. {hud_python-0.4.56 → hud_python-0.4.57}/environments/README.md +0 -0
  18. {hud_python-0.4.56 → hud_python-0.4.57}/environments/blank/README.md +0 -0
  19. {hud_python-0.4.56 → hud_python-0.4.57}/environments/blank/environment/README.md +0 -0
  20. {hud_python-0.4.56 → hud_python-0.4.57}/environments/blank/environment/pyproject.toml +0 -0
  21. {hud_python-0.4.56 → hud_python-0.4.57}/environments/blank/server/README.md +0 -0
  22. {hud_python-0.4.56 → hud_python-0.4.57}/environments/blank/server/pyproject.toml +0 -0
  23. {hud_python-0.4.56 → hud_python-0.4.57}/environments/browser/README.md +0 -0
  24. {hud_python-0.4.56 → hud_python-0.4.57}/environments/browser/environment/2048/README.md +0 -0
  25. {hud_python-0.4.56 → hud_python-0.4.57}/environments/browser/environment/2048/backend/pyproject.toml +0 -0
  26. {hud_python-0.4.56 → hud_python-0.4.57}/environments/browser/environment/README.md +0 -0
  27. {hud_python-0.4.56 → hud_python-0.4.57}/environments/browser/environment/pyproject.toml +0 -0
  28. {hud_python-0.4.56 → hud_python-0.4.57}/environments/browser/environment/todo/README.md +0 -0
  29. {hud_python-0.4.56 → hud_python-0.4.57}/environments/browser/environment/todo/backend/pyproject.toml +0 -0
  30. {hud_python-0.4.56 → hud_python-0.4.57}/environments/browser/pyproject.toml +0 -0
  31. {hud_python-0.4.56 → hud_python-0.4.57}/environments/browser/server/pyproject.toml +0 -0
  32. {hud_python-0.4.56 → hud_python-0.4.57}/environments/deepresearch/README.md +0 -0
  33. {hud_python-0.4.56 → hud_python-0.4.57}/environments/deepresearch/environment/pyproject.toml +0 -0
  34. {hud_python-0.4.56 → hud_python-0.4.57}/environments/deepresearch/pyproject.toml +0 -0
  35. {hud_python-0.4.56 → hud_python-0.4.57}/environments/deepresearch/server/pyproject.toml +0 -0
  36. {hud_python-0.4.56 → hud_python-0.4.57}/environments/remote_browser/README.md +0 -0
  37. {hud_python-0.4.56 → hud_python-0.4.57}/environments/remote_browser/pyproject.toml +0 -0
  38. {hud_python-0.4.56 → hud_python-0.4.57}/environments/remote_browser/src/hud_controller/providers/README.md +0 -0
  39. {hud_python-0.4.56 → hud_python-0.4.57}/environments/rubrics/README.md +0 -0
  40. {hud_python-0.4.56 → hud_python-0.4.57}/environments/rubrics/environment/pyproject.toml +0 -0
  41. {hud_python-0.4.56 → hud_python-0.4.57}/environments/rubrics/pyproject.toml +0 -0
  42. {hud_python-0.4.56 → hud_python-0.4.57}/environments/rubrics/server/pyproject.toml +0 -0
  43. {hud_python-0.4.56 → hud_python-0.4.57}/environments/text_2048/README.md +0 -0
  44. {hud_python-0.4.56 → hud_python-0.4.57}/environments/text_2048/pyproject.toml +0 -0
  45. {hud_python-0.4.56 → hud_python-0.4.57}/examples/README.md +0 -0
  46. {hud_python-0.4.56 → hud_python-0.4.57}/hud/__init__.py +0 -0
  47. {hud_python-0.4.56 → hud_python-0.4.57}/hud/__main__.py +0 -0
  48. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/__init__.py +0 -0
  49. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/base.py +0 -0
  50. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/claude.py +0 -0
  51. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/grounded_openai.py +0 -0
  52. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/langchain.py +0 -0
  53. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/lite_llm.py +0 -0
  54. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/misc/__init__.py +0 -0
  55. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/misc/integration_test_agent.py +0 -0
  56. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/misc/response_agent.py +0 -0
  57. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/openai.py +0 -0
  58. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/openai_chat_generic.py +0 -0
  59. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/tests/__init__.py +0 -0
  60. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/tests/test_base.py +0 -0
  61. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/tests/test_base_runtime.py +0 -0
  62. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/tests/test_claude.py +0 -0
  63. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/tests/test_client.py +0 -0
  64. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/tests/test_grounded_openai_agent.py +0 -0
  65. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/tests/test_openai.py +0 -0
  66. {hud_python-0.4.56 → hud_python-0.4.57}/hud/agents/utils.py +0 -0
  67. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/__init__.py +0 -0
  68. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/__main__.py +0 -0
  69. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/analyze.py +0 -0
  70. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/build.py +0 -0
  71. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/clone.py +0 -0
  72. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/debug.py +0 -0
  73. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/eval.py +0 -0
  74. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/flows/__init__.py +0 -0
  75. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/flows/tasks.py +0 -0
  76. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/get.py +0 -0
  77. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/init.py +0 -0
  78. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/list_func.py +0 -0
  79. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/pull.py +0 -0
  80. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/push.py +0 -0
  81. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/remove.py +0 -0
  82. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/__init__.py +0 -0
  83. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/celebrate.py +0 -0
  84. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/config.py +0 -0
  85. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/display.py +0 -0
  86. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/gpu.py +0 -0
  87. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/gpu_utils.py +0 -0
  88. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/local_runner.py +0 -0
  89. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/presets.py +0 -0
  90. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/remote_runner.py +0 -0
  91. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/rl_api.py +0 -0
  92. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/viewer.py +0 -0
  93. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/vllm.py +0 -0
  94. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/rl/wait_utils.py +0 -0
  95. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/__init__.py +0 -0
  96. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_analyze.py +0 -0
  97. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_analyze_metadata.py +0 -0
  98. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_analyze_module.py +0 -0
  99. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_build.py +0 -0
  100. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_build_failure.py +0 -0
  101. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_build_module.py +0 -0
  102. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_cli_init.py +0 -0
  103. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_cli_main.py +0 -0
  104. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_cli_more_wrappers.py +0 -0
  105. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_cli_root.py +0 -0
  106. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_clone.py +0 -0
  107. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_cursor.py +0 -0
  108. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_debug.py +0 -0
  109. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_eval.py +0 -0
  110. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_list_func.py +0 -0
  111. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_main_module.py +0 -0
  112. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_mcp_server.py +0 -0
  113. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_pull.py +0 -0
  114. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_push.py +0 -0
  115. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_push_happy.py +0 -0
  116. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_push_wrapper.py +0 -0
  117. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_registry.py +0 -0
  118. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/tests/test_utils.py +0 -0
  119. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/__init__.py +0 -0
  120. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/config.py +0 -0
  121. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/cursor.py +0 -0
  122. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/docker.py +0 -0
  123. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/env_check.py +0 -0
  124. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/environment.py +0 -0
  125. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/interactive.py +0 -0
  126. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/local_runner.py +0 -0
  127. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/logging.py +0 -0
  128. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/metadata.py +0 -0
  129. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/package_runner.py +0 -0
  130. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/registry.py +0 -0
  131. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/remote_runner.py +0 -0
  132. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/runner.py +0 -0
  133. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/server.py +0 -0
  134. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/source_hash.py +0 -0
  135. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tasks.py +0 -0
  136. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/__init__.py +0 -0
  137. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_config.py +0 -0
  138. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_docker.py +0 -0
  139. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_docker_hints.py +0 -0
  140. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_env_check.py +0 -0
  141. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_environment.py +0 -0
  142. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_interactive_module.py +0 -0
  143. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_local_runner.py +0 -0
  144. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_logging_utils.py +0 -0
  145. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_metadata.py +0 -0
  146. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_package_runner.py +0 -0
  147. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_registry_utils.py +0 -0
  148. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_remote_runner.py +0 -0
  149. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_runner_modules.py +0 -0
  150. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_source_hash.py +0 -0
  151. {hud_python-0.4.56 → hud_python-0.4.57}/hud/cli/utils/tests/test_tasks.py +0 -0
  152. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/README.md +0 -0
  153. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/__init__.py +0 -0
  154. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/tests/__init__.py +0 -0
  155. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/tests/test_client_integration.py +0 -0
  156. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/tests/test_fastmcp.py +0 -0
  157. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/tests/test_mcp_use_retry.py +0 -0
  158. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/utils/__init__.py +0 -0
  159. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/utils/mcp_use_retry.py +0 -0
  160. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/utils/retry.py +0 -0
  161. {hud_python-0.4.56 → hud_python-0.4.57}/hud/clients/utils/retry_transport.py +0 -0
  162. {hud_python-0.4.56 → hud_python-0.4.57}/hud/datasets/__init__.py +0 -0
  163. {hud_python-0.4.56 → hud_python-0.4.57}/hud/datasets/parallel.py +0 -0
  164. {hud_python-0.4.56 → hud_python-0.4.57}/hud/datasets/runner.py +0 -0
  165. {hud_python-0.4.56 → hud_python-0.4.57}/hud/datasets/tests/__init__.py +0 -0
  166. {hud_python-0.4.56 → hud_python-0.4.57}/hud/datasets/tests/test_runner.py +0 -0
  167. {hud_python-0.4.56 → hud_python-0.4.57}/hud/datasets/tests/test_utils.py +0 -0
  168. {hud_python-0.4.56 → hud_python-0.4.57}/hud/datasets/utils.py +0 -0
  169. {hud_python-0.4.56 → hud_python-0.4.57}/hud/misc/__init__.py +0 -0
  170. {hud_python-0.4.56 → hud_python-0.4.57}/hud/misc/claude_plays_pokemon.py +0 -0
  171. {hud_python-0.4.56 → hud_python-0.4.57}/hud/native/__init__.py +0 -0
  172. {hud_python-0.4.56 → hud_python-0.4.57}/hud/native/comparator.py +0 -0
  173. {hud_python-0.4.56 → hud_python-0.4.57}/hud/native/tests/__init__.py +0 -0
  174. {hud_python-0.4.56 → hud_python-0.4.57}/hud/native/tests/test_comparator.py +0 -0
  175. {hud_python-0.4.56 → hud_python-0.4.57}/hud/native/tests/test_native_init.py +0 -0
  176. {hud_python-0.4.56 → hud_python-0.4.57}/hud/otel/__init__.py +0 -0
  177. {hud_python-0.4.56 → hud_python-0.4.57}/hud/otel/collector.py +0 -0
  178. {hud_python-0.4.56 → hud_python-0.4.57}/hud/otel/config.py +0 -0
  179. {hud_python-0.4.56 → hud_python-0.4.57}/hud/otel/context.py +0 -0
  180. {hud_python-0.4.56 → hud_python-0.4.57}/hud/otel/exporters.py +0 -0
  181. {hud_python-0.4.56 → hud_python-0.4.57}/hud/otel/instrumentation.py +0 -0
  182. {hud_python-0.4.56 → hud_python-0.4.57}/hud/otel/processors.py +0 -0
  183. {hud_python-0.4.56 → hud_python-0.4.57}/hud/otel/tests/__init__.py +0 -0
  184. {hud_python-0.4.56 → hud_python-0.4.57}/hud/otel/tests/test_instrumentation.py +0 -0
  185. {hud_python-0.4.56 → hud_python-0.4.57}/hud/otel/tests/test_processors.py +0 -0
  186. {hud_python-0.4.56 → hud_python-0.4.57}/hud/py.typed +0 -0
  187. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/README.md +0 -0
  188. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/__init__.py +0 -0
  189. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/actor.py +0 -0
  190. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/buffer.py +0 -0
  191. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/chat_template.jinja +0 -0
  192. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/config.py +0 -0
  193. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/distributed.py +0 -0
  194. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/learner.py +0 -0
  195. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/tests/__init__.py +0 -0
  196. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/tests/test_learner.py +0 -0
  197. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/train.py +0 -0
  198. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/types.py +0 -0
  199. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/utils/start_vllm_server.sh +0 -0
  200. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/utils.py +0 -0
  201. {hud_python-0.4.56 → hud_python-0.4.57}/hud/rl/vllm_adapter.py +0 -0
  202. {hud_python-0.4.56 → hud_python-0.4.57}/hud/samples/__init__.py +0 -0
  203. {hud_python-0.4.56 → hud_python-0.4.57}/hud/samples/browser.py +0 -0
  204. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/__init__.py +0 -0
  205. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/context.py +0 -0
  206. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/helper/__init__.py +0 -0
  207. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/low_level.py +0 -0
  208. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/router.py +0 -0
  209. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/tests/__init__.py +0 -0
  210. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/tests/test_add_tool.py +0 -0
  211. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/tests/test_context.py +0 -0
  212. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/tests/test_mcp_server_handlers.py +0 -0
  213. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/tests/test_mcp_server_integration.py +0 -0
  214. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/tests/test_mcp_server_more.py +0 -0
  215. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/tests/test_run_wrapper.py +0 -0
  216. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/tests/test_server_extra.py +0 -0
  217. {hud_python-0.4.56 → hud_python-0.4.57}/hud/server/tests/test_sigterm_runner.py +0 -0
  218. {hud_python-0.4.56 → hud_python-0.4.57}/hud/settings.py +0 -0
  219. {hud_python-0.4.56 → hud_python-0.4.57}/hud/shared/__init__.py +0 -0
  220. {hud_python-0.4.56 → hud_python-0.4.57}/hud/shared/exceptions.py +0 -0
  221. {hud_python-0.4.56 → hud_python-0.4.57}/hud/shared/hints.py +0 -0
  222. {hud_python-0.4.56 → hud_python-0.4.57}/hud/shared/requests.py +0 -0
  223. {hud_python-0.4.56 → hud_python-0.4.57}/hud/shared/tests/__init__.py +0 -0
  224. {hud_python-0.4.56 → hud_python-0.4.57}/hud/shared/tests/test_exceptions.py +0 -0
  225. {hud_python-0.4.56 → hud_python-0.4.57}/hud/shared/tests/test_hints.py +0 -0
  226. {hud_python-0.4.56 → hud_python-0.4.57}/hud/shared/tests/test_requests.py +0 -0
  227. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/__init__.py +0 -0
  228. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/async_context.py +0 -0
  229. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/instrument.py +0 -0
  230. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/job.py +0 -0
  231. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/replay.py +0 -0
  232. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/tests/__init__.py +0 -0
  233. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/tests/test_async_context.py +0 -0
  234. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/tests/test_instrument.py +0 -0
  235. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/tests/test_job.py +0 -0
  236. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/tests/test_replay.py +0 -0
  237. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/tests/test_trace.py +0 -0
  238. {hud_python-0.4.56 → hud_python-0.4.57}/hud/telemetry/trace.py +0 -0
  239. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/__init__.py +0 -0
  240. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/base.py +0 -0
  241. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/bash.py +0 -0
  242. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/computer/__init__.py +0 -0
  243. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/computer/anthropic.py +0 -0
  244. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/computer/hud.py +0 -0
  245. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/computer/openai.py +0 -0
  246. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/computer/qwen.py +0 -0
  247. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/computer/settings.py +0 -0
  248. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/edit.py +0 -0
  249. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/executors/__init__.py +0 -0
  250. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/executors/base.py +0 -0
  251. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/executors/pyautogui.py +0 -0
  252. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/executors/tests/__init__.py +0 -0
  253. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/executors/tests/test_base_executor.py +0 -0
  254. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/executors/tests/test_pyautogui_executor.py +0 -0
  255. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/executors/xdo.py +0 -0
  256. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/grounding/__init__.py +0 -0
  257. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/grounding/config.py +0 -0
  258. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/grounding/grounded_tool.py +0 -0
  259. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/grounding/grounder.py +0 -0
  260. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/grounding/tests/__init__.py +0 -0
  261. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/grounding/tests/test_grounded_tool.py +0 -0
  262. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/playwright.py +0 -0
  263. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/response.py +0 -0
  264. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/submit.py +0 -0
  265. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/__init__.py +0 -0
  266. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_base.py +0 -0
  267. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_bash.py +0 -0
  268. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_bash_extended.py +0 -0
  269. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_computer.py +0 -0
  270. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_computer_actions.py +0 -0
  271. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_edit.py +0 -0
  272. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_init.py +0 -0
  273. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_playwright_tool.py +0 -0
  274. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_response.py +0 -0
  275. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_submit.py +0 -0
  276. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_tools.py +0 -0
  277. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_tools_init.py +0 -0
  278. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_types.py +0 -0
  279. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/tests/test_utils.py +0 -0
  280. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/types.py +0 -0
  281. {hud_python-0.4.56 → hud_python-0.4.57}/hud/tools/utils.py +0 -0
  282. {hud_python-0.4.56 → hud_python-0.4.57}/hud/types.py +0 -0
  283. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/__init__.py +0 -0
  284. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/agent_factories.py +0 -0
  285. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/async_utils.py +0 -0
  286. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/group_eval.py +0 -0
  287. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/hud_console.py +0 -0
  288. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/mcp.py +0 -0
  289. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/pretty_errors.py +0 -0
  290. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/progress.py +0 -0
  291. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/task_tracking.py +0 -0
  292. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tasks.py +0 -0
  293. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/telemetry.py +0 -0
  294. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/__init__.py +0 -0
  295. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/test_agent_factories.py +0 -0
  296. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/test_async_utils.py +0 -0
  297. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/test_init.py +0 -0
  298. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/test_mcp.py +0 -0
  299. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/test_pretty_errors.py +0 -0
  300. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/test_progress.py +0 -0
  301. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/test_tasks.py +0 -0
  302. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/test_telemetry.py +0 -0
  303. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tests/test_tool_shorthand.py +0 -0
  304. {hud_python-0.4.56 → hud_python-0.4.57}/hud/utils/tool_shorthand.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hud-python
3
- Version: 0.4.56
3
+ Version: 0.4.57
4
4
  Summary: SDK for the HUD platform.
5
5
  Project-URL: Homepage, https://github.com/hud-evals/hud-python
6
6
  Project-URL: Bug Tracker, https://github.com/hud-evals/hud-python/issues
@@ -237,7 +237,7 @@ async def run_mcp_module(
237
237
 
238
238
  from hud.cli.flows.dev import create_dynamic_trace
239
239
 
240
- live_trace_url = await create_dynamic_trace(
240
+ _, live_trace_url = await create_dynamic_trace(
241
241
  mcp_config=local_mcp_config,
242
242
  build_status=False,
243
243
  environment_name=mcp_server.name or "mcp-server",
@@ -608,7 +608,7 @@ def run_docker_dev_server(
608
608
  "headers": {},
609
609
  }
610
610
  }
611
- live_trace_url = _asy.run(
611
+ _, live_trace_url = _asy.run(
612
612
  create_dynamic_trace(
613
613
  mcp_config=local_mcp_config,
614
614
  build_status=True,
@@ -661,13 +661,33 @@ def run_docker_dev_server(
661
661
  # Create and run proxy with HUD helpers
662
662
  async def run_proxy() -> None:
663
663
  from fastmcp import FastMCP
664
+ from fastmcp.server.proxy import ProxyClient
664
665
 
665
- # Create FastMCP proxy to Docker stdio
666
- fastmcp_proxy = FastMCP.as_proxy(mcp_config, name="HUD Docker Dev Proxy")
666
+ # Create ProxyClient without custom log handler since we capture Docker logs directly
667
+ proxy_client = ProxyClient(mcp_config, name="HUD Docker Dev Proxy")
668
+
669
+ # Extract container name from docker args and store for logs endpoint
670
+ docker_cmd = mcp_config["docker"]["args"]
671
+ container_name = None
672
+ for i, arg in enumerate(docker_cmd):
673
+ if arg == "--name" and i + 1 < len(docker_cmd):
674
+ container_name = docker_cmd[i + 1]
675
+ break
676
+
677
+ if container_name:
678
+ # Store container name for logs endpoint to use
679
+ os.environ["_HUD_DEV_DOCKER_CONTAINER"] = container_name
680
+ hud_console.debug(f"Docker container: {container_name}")
681
+
682
+ # Create FastMCP proxy using the ProxyClient
683
+ fastmcp_proxy = FastMCP.as_proxy(proxy_client)
667
684
 
668
685
  # Wrap in MCPServer to get /docs and REST wrappers
669
686
  proxy = MCPServer(name="HUD Docker Dev Proxy")
670
687
 
688
+ # Enable logs endpoint on HTTP server
689
+ os.environ["_HUD_DEV_LOGS_PROVIDER"] = "enabled"
690
+
671
691
  # Import all tools from the FastMCP proxy
672
692
  await proxy.import_server(fastmcp_proxy)
673
693
 
@@ -18,7 +18,7 @@ async def create_dynamic_trace(
18
18
  mcp_config: dict[str, dict[str, Any]],
19
19
  build_status: bool,
20
20
  environment_name: str,
21
- ) -> str | None:
21
+ ) -> tuple[str | None, str | None]:
22
22
  """
23
23
  Create a dynamic trace for HUD dev sessions when running in HTTP mode.
24
24
 
@@ -43,27 +43,16 @@ async def create_dynamic_trace(
43
43
  api_key = settings.api_key
44
44
  if not api_key:
45
45
  logger.warning("Skipping dynamic trace creation; missing HUD_API_KEY")
46
- return None
46
+ return None, None
47
47
 
48
48
  try:
49
49
  resp = await make_request("POST", url=url, json=payload, api_key=api_key)
50
50
  # New API returns an id; construct the URL as https://hud.so/trace/{id}
51
- trace_id = None
52
- if isinstance(resp, dict):
53
- trace_id = resp.get("id")
54
- if trace_id is None:
55
- data = resp.get("data", {}) or {}
56
- if isinstance(data, dict):
57
- trace_id = data.get("id")
58
- # Backcompat: if url is provided directly
59
- if not trace_id:
60
- direct_url = resp.get("url") or (resp.get("data", {}) or {}).get("url")
61
- if isinstance(direct_url, str) and direct_url:
62
- return direct_url
51
+ trace_id = resp.get("id")
63
52
 
64
53
  if isinstance(trace_id, str) and trace_id:
65
- return f"https://hud.so/trace/{trace_id}"
66
- return None
54
+ return trace_id, f"https://hud.so/trace/{trace_id}"
55
+ return None, None
67
56
  except Exception as e:
68
57
  # Do not interrupt dev flow
69
58
  try:
@@ -71,7 +60,7 @@ async def create_dynamic_trace(
71
60
  logger.warning("Failed to create dynamic dev trace: %s | payload=%s", e, preview)
72
61
  except Exception:
73
62
  logger.warning("Failed to create dynamic dev trace: %s", e)
74
- return None
63
+ return None, None
75
64
 
76
65
 
77
66
  def show_dev_ui(
@@ -0,0 +1,367 @@
1
+ """Tests for the convert command."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from unittest.mock import patch
6
+
7
+ import pytest
8
+ import typer
9
+
10
+ from hud.cli.flows.tasks import convert_tasks_to_remote
11
+ from hud.types import Task
12
+
13
+
14
+ class TestConvertCommand:
15
+ """Test the convert command functionality."""
16
+
17
+ @pytest.fixture
18
+ def temp_tasks_file(self, tmp_path):
19
+ """Create a temporary tasks file."""
20
+ tasks = [
21
+ {
22
+ "prompt": "Test task 1",
23
+ "mcp_config": {
24
+ "local": {
25
+ "command": "docker",
26
+ "args": ["run", "--rm", "-i", "test-image:latest"],
27
+ }
28
+ },
29
+ }
30
+ ]
31
+ tasks_file = tmp_path / "tasks.json"
32
+ tasks_file.write_text(json.dumps(tasks))
33
+ return tasks_file
34
+
35
+ @pytest.fixture
36
+ def mock_env_dir(self, tmp_path):
37
+ """Create a mock environment directory with lock file."""
38
+ env_dir = tmp_path / "env"
39
+ env_dir.mkdir()
40
+
41
+ # Create lock file
42
+ lock_data = {
43
+ "images": {
44
+ "remote": "registry.hud.so/test-org/test-env:v1.0.0",
45
+ "local": "test-env:latest",
46
+ }
47
+ }
48
+ lock_file = env_dir / "hud.lock.yaml"
49
+ import yaml
50
+
51
+ lock_file.write_text(yaml.dump(lock_data))
52
+
53
+ return env_dir
54
+
55
+ @patch("hud.cli.flows.tasks._derive_remote_image")
56
+ @patch("hud.cli.flows.tasks._ensure_pushed")
57
+ @patch("hud.cli.flows.tasks.find_environment_dir")
58
+ @patch("hud.cli.flows.tasks.load_tasks")
59
+ @patch("hud.settings.settings")
60
+ def test_convert_tasks_basic(
61
+ self,
62
+ mock_settings,
63
+ mock_load_tasks,
64
+ mock_find_env,
65
+ mock_ensure_pushed,
66
+ mock_derive_remote,
67
+ temp_tasks_file,
68
+ mock_env_dir,
69
+ ):
70
+ """Test basic task conversion from local to remote."""
71
+ # Setup mocks
72
+ mock_settings.api_key = "test-api-key"
73
+ mock_find_env.return_value = mock_env_dir
74
+
75
+ # Mock the push check to return updated lock data
76
+ mock_ensure_pushed.return_value = {
77
+ "images": {
78
+ "remote": "registry.hud.so/test-org/test-env:v1.0.0",
79
+ "local": "test-env:v1.0.0",
80
+ }
81
+ }
82
+
83
+ # Mock derive remote image
84
+ mock_derive_remote.return_value = "registry.hud.so/test-org/test-env:v1.0.0"
85
+
86
+ task = Task(
87
+ prompt="Test task",
88
+ mcp_config={
89
+ "local": {"command": "docker", "args": ["run", "--rm", "-i", "test-image:latest"]}
90
+ },
91
+ )
92
+ raw_task = {
93
+ "prompt": "Test task",
94
+ "mcp_config": {
95
+ "local": {"command": "docker", "args": ["run", "--rm", "-i", "test-image:latest"]}
96
+ },
97
+ }
98
+
99
+ mock_load_tasks.side_effect = [[task], [raw_task]]
100
+
101
+ # Run conversion
102
+ result_path = convert_tasks_to_remote(str(temp_tasks_file))
103
+
104
+ # Check result
105
+ assert result_path.endswith("remote_tasks.json")
106
+ assert Path(result_path).exists()
107
+
108
+ # Verify converted content
109
+ with open(result_path) as f:
110
+ converted_tasks = json.load(f)
111
+
112
+ assert len(converted_tasks) == 1
113
+ assert "hud" in converted_tasks[0]["mcp_config"]
114
+ assert converted_tasks[0]["mcp_config"]["hud"]["url"] == "https://mcp.hud.so/v3/mcp"
115
+
116
+ @patch("hud.settings.settings")
117
+ def test_convert_missing_api_key(self, mock_settings, temp_tasks_file):
118
+ """Test that conversion fails without API key."""
119
+ mock_settings.api_key = ""
120
+
121
+ with pytest.raises(typer.Exit):
122
+ convert_tasks_to_remote(str(temp_tasks_file))
123
+
124
+ @patch("hud.cli.flows.tasks.find_environment_dir")
125
+ @patch("hud.cli.flows.tasks.load_tasks")
126
+ @patch("hud.settings.settings")
127
+ def test_convert_already_remote(
128
+ self, mock_settings, mock_load_tasks, mock_find_env, temp_tasks_file
129
+ ):
130
+ """Test that already remote tasks are not converted again."""
131
+ mock_settings.api_key = "test-api-key"
132
+ mock_find_env.return_value = None # No env dir needed for remote tasks
133
+
134
+ # Create task that's already remote
135
+ task = Task(
136
+ prompt="Test task",
137
+ mcp_config={
138
+ "remote": {
139
+ "url": "https://mcp.hud.so",
140
+ "headers": {"Mcp-Image": "registry.hud.so/test/image:v1"},
141
+ }
142
+ },
143
+ )
144
+
145
+ mock_load_tasks.return_value = [task]
146
+
147
+ # Should return original path without modification
148
+ result_path = convert_tasks_to_remote(str(temp_tasks_file))
149
+ assert result_path == str(temp_tasks_file)
150
+
151
+ @patch("hud.cli.flows.tasks.find_environment_dir")
152
+ @patch("hud.cli.flows.tasks.load_tasks")
153
+ @patch("hud.settings.settings")
154
+ def test_convert_no_environment(
155
+ self, mock_settings, mock_load_tasks, mock_find_env, temp_tasks_file
156
+ ):
157
+ """Test that conversion fails when no environment is found."""
158
+ mock_settings.api_key = "test-api-key"
159
+ mock_find_env.return_value = None
160
+
161
+ task = Task(
162
+ prompt="Test task",
163
+ mcp_config={
164
+ "local": {"command": "docker", "args": ["run", "--rm", "-i", "test-image:latest"]}
165
+ },
166
+ )
167
+
168
+ mock_load_tasks.return_value = [task]
169
+
170
+ with pytest.raises(typer.Exit):
171
+ convert_tasks_to_remote(str(temp_tasks_file))
172
+
173
+ @patch("hud.utils.hud_console.hud_console.confirm")
174
+ @patch("hud.cli.flows.tasks._derive_remote_image")
175
+ @patch("hud.cli.flows.tasks._ensure_pushed")
176
+ @patch("hud.cli.flows.tasks.find_environment_dir")
177
+ @patch("hud.cli.flows.tasks.load_tasks")
178
+ @patch("hud.settings.settings")
179
+ def test_convert_with_env_vars(
180
+ self,
181
+ mock_settings,
182
+ mock_load_tasks,
183
+ mock_find_env,
184
+ mock_ensure_pushed,
185
+ mock_derive_remote,
186
+ mock_confirm,
187
+ temp_tasks_file,
188
+ mock_env_dir,
189
+ ):
190
+ """Test conversion includes environment variables as headers."""
191
+ mock_settings.api_key = "test-api-key"
192
+ mock_find_env.return_value = mock_env_dir
193
+ mock_confirm.return_value = True # Always confirm in tests
194
+
195
+ # Mock the push check to return updated lock data
196
+ mock_ensure_pushed.return_value = {
197
+ "images": {
198
+ "remote": "registry.hud.so/test-org/test-env:v1.0.0",
199
+ "local": "test-env:v1.0.0",
200
+ }
201
+ }
202
+
203
+ # Mock derive remote image
204
+ mock_derive_remote.return_value = "registry.hud.so/test-org/test-env:v1.0.0"
205
+
206
+ # Add .env file with API keys
207
+ env_file = mock_env_dir / ".env"
208
+ env_file.write_text("OPENAI_API_KEY=sk-test123\nANTHROPIC_API_KEY=sk-ant456")
209
+
210
+ task = Task(
211
+ prompt="Test task",
212
+ mcp_config={
213
+ "local": {
214
+ "command": "docker",
215
+ "args": ["run", "--rm", "-i", "-e", "OPENAI_API_KEY", "test-image:latest"],
216
+ }
217
+ },
218
+ )
219
+ raw_task = task.model_dump()
220
+
221
+ mock_load_tasks.side_effect = [[task], [raw_task]]
222
+
223
+ # Run conversion
224
+ result_path = convert_tasks_to_remote(str(temp_tasks_file))
225
+
226
+ # Verify headers include env vars
227
+ with open(result_path) as f:
228
+ converted_tasks = json.load(f)
229
+
230
+ headers = converted_tasks[0]["mcp_config"]["hud"]["headers"]
231
+ assert "Env-Openai-Api-Key" in headers
232
+ assert headers["Env-Openai-Api-Key"] == "${OPENAI_API_KEY}"
233
+
234
+
235
+ class TestConvertHelperFunctions:
236
+ """Test helper functions used by convert command."""
237
+
238
+ def test_env_var_to_header_key(self):
239
+ """Test environment variable name conversion to header format."""
240
+ from hud.cli.flows.tasks import _env_var_to_header_key
241
+
242
+ assert _env_var_to_header_key("OPENAI_API_KEY") == "Env-Openai-Api-Key"
243
+ assert _env_var_to_header_key("ANTHROPIC_API_KEY") == "Env-Anthropic-Api-Key"
244
+ assert _env_var_to_header_key("SIMPLE") == "Env-Simple"
245
+ assert _env_var_to_header_key("MULTIPLE_WORD_VAR") == "Env-Multiple-Word-Var"
246
+
247
+ def test_extract_dotenv_api_key_vars(self):
248
+ """Test extraction of API-like variables from .env file."""
249
+ # Create test env directory with .env file
250
+ import tempfile
251
+
252
+ from hud.cli.flows.tasks import _extract_dotenv_api_key_vars
253
+
254
+ with tempfile.TemporaryDirectory() as tmpdir:
255
+ env_dir = Path(tmpdir)
256
+ env_file = env_dir / ".env"
257
+ env_file.write_text("""
258
+ # Test .env file
259
+ OPENAI_API_KEY=sk-test123
260
+ ANTHROPIC_API_KEY=sk-ant456
261
+ SOME_TOKEN=abc123
262
+ CLIENT_SECRET=secret789
263
+ USER_PASSWORD=pass123
264
+ REGULAR_VAR=not_included
265
+ HUD_API_URL=https://api.hud.so
266
+ """)
267
+
268
+ result = _extract_dotenv_api_key_vars(env_dir)
269
+
270
+ # Should include only API-like variables
271
+ assert "OPENAI_API_KEY" in result
272
+ assert "ANTHROPIC_API_KEY" in result
273
+ assert "SOME_TOKEN" in result
274
+ assert "CLIENT_SECRET" in result
275
+ assert "USER_PASSWORD" in result
276
+ assert "REGULAR_VAR" not in result
277
+ assert "HUD_API_URL" in result # API in name, so it's included
278
+
279
+ def test_is_remote_url(self):
280
+ """Test remote URL detection."""
281
+ from hud.cli.flows.tasks import _is_remote_url
282
+
283
+ # This function matches URLs with domain names (not localhost or IPs)
284
+ assert _is_remote_url("https://mcp.hud.so")
285
+ assert _is_remote_url("http://mcp.hud.so")
286
+ assert _is_remote_url("https://mcp.hud.so/some/path")
287
+ assert _is_remote_url("https://example.com") # Also matches
288
+ assert not _is_remote_url("http://localhost:8000") # localhost doesn't match
289
+ assert not _is_remote_url("file:///path/to/file") # file:// doesn't match
290
+
291
+ def test_extract_env_vars_from_docker_args(self):
292
+ """Test extraction of environment variables from docker arguments."""
293
+ from hud.cli.flows.tasks import _extract_env_vars_from_docker_args
294
+
295
+ # Test with various docker arg formats
296
+ args = [
297
+ "run",
298
+ "--rm",
299
+ "-i",
300
+ "-e",
301
+ "VAR1",
302
+ "-e",
303
+ "VAR2=value",
304
+ "--env",
305
+ "VAR3",
306
+ "--env=VAR4",
307
+ # Note: -eFOO compact form is not supported by the implementation
308
+ "--env-file",
309
+ ".env",
310
+ "-p",
311
+ "8080:80",
312
+ ]
313
+
314
+ result = _extract_env_vars_from_docker_args(args)
315
+
316
+ assert "VAR1" in result
317
+ assert "VAR2" in result
318
+ assert "VAR3" in result
319
+ assert "VAR4" in result
320
+ # FOO is not extracted because -eFOO compact form is not supported
321
+ assert len(result) == 4
322
+
323
+ def test_derive_remote_image(self):
324
+ """Test deriving remote image from lock data."""
325
+ from hud.cli.flows.tasks import _derive_remote_image
326
+
327
+ # The function derives remote image from images.local, not images.remote
328
+ lock_data = {"images": {"local": "test-env:v1.0.0"}}
329
+ result = _derive_remote_image(lock_data)
330
+ assert result == "test-env:v1.0.0"
331
+
332
+ # Test fallback to legacy format
333
+ lock_data = {
334
+ "image": "test-org/test-env:v1.0.0",
335
+ }
336
+ result = _derive_remote_image(lock_data)
337
+ assert result == "test-org/test-env:v1.0.0"
338
+
339
+ def test_extract_vars_from_task_configs(self):
340
+ """Test extraction of env vars from task configurations."""
341
+ from hud.cli.flows.tasks import _extract_vars_from_task_configs
342
+
343
+ raw_tasks = [
344
+ {
345
+ "prompt": "Task 1",
346
+ "mcp_config": {
347
+ "local": {"command": "docker", "args": ["run", "-e", "API_KEY1", "image1"]}
348
+ },
349
+ },
350
+ {
351
+ "prompt": "Task 2",
352
+ "mcp_config": {
353
+ "local": {
354
+ "command": "docker",
355
+ "args": ["run", "-e", "API_KEY2", "--env", "API_KEY3", "image2"],
356
+ }
357
+ },
358
+ },
359
+ {"prompt": "Task 3", "mcp_config": {"remote": {"url": "https://mcp.hud.so"}}},
360
+ ]
361
+
362
+ result = _extract_vars_from_task_configs(raw_tasks)
363
+
364
+ assert "API_KEY1" in result
365
+ assert "API_KEY2" in result
366
+ assert "API_KEY3" in result
367
+ assert len(result) == 3
@@ -17,6 +17,7 @@ from __future__ import annotations
17
17
 
18
18
  import contextlib
19
19
  import json
20
+ import logging
20
21
  import os
21
22
  import time
22
23
  from pathlib import Path
@@ -27,6 +28,9 @@ from packaging import version
27
28
 
28
29
  from hud.utils.hud_console import HUDConsole
29
30
 
31
+ # Logger for version checking
32
+ logger = logging.getLogger(__name__)
33
+
30
34
  # Cache location for version check data
31
35
  CACHE_DIR = Path.home() / ".hud" / ".cache"
32
36
  VERSION_CACHE_FILE = CACHE_DIR / "version_check.json"
@@ -218,7 +222,7 @@ def display_update_prompt(console: HUDConsole | None = None) -> None:
218
222
  console: HUDConsole instance for output. If None, creates a new one.
219
223
  """
220
224
  if console is None:
221
- console = HUDConsole()
225
+ console = HUDConsole(logger=logger)
222
226
 
223
227
  try:
224
228
  info = check_for_updates()
@@ -231,11 +235,8 @@ def display_update_prompt(console: HUDConsole | None = None) -> None:
231
235
  f" Run: [bold yellow]uv tool upgrade hud-python[/bold yellow] to update"
232
236
  )
233
237
 
234
- # Display as a subtle but noticeable panel
235
- console._stdout_console.print(
236
- f"\n[yellow]{update_msg}[/yellow]\n",
237
- highlight=False,
238
- )
238
+ # Display using console info
239
+ console.info(f"[yellow]{update_msg}[/yellow]")
239
240
  except Exception: # noqa: S110
240
241
  # Never let version checking disrupt the user's workflow
241
242
  pass
@@ -104,6 +104,7 @@ class BaseHUDClient(AgentMCPClient):
104
104
 
105
105
  self._initialized = False
106
106
  self._telemetry_data = {} # Initialize telemetry data
107
+ self._cached_resources: list[types.Resource] = [] # Cache for resources
107
108
 
108
109
  if self.verbose:
109
110
  self._setup_verbose_logging()
@@ -170,6 +171,7 @@ class BaseHUDClient(AgentMCPClient):
170
171
  if self._initialized:
171
172
  await self._disconnect()
172
173
  self._initialized = False
174
+ self._cached_resources.clear()
173
175
  hud_console.info("Environment Shutdown completed")
174
176
  else:
175
177
  hud_console.debug("Client was not initialized, skipping disconnect")
@@ -211,9 +213,22 @@ class BaseHUDClient(AgentMCPClient):
211
213
  """List all available tools."""
212
214
  raise NotImplementedError
213
215
 
214
- @abstractmethod
215
216
  async def list_resources(self) -> list[types.Resource]:
216
- """List all available resources."""
217
+ """List all available resources.
218
+
219
+ Uses cached resources if available, otherwise fetches from the server.
220
+
221
+ Returns:
222
+ List of available resources.
223
+ """
224
+ # If cache is empty, populate it
225
+ if not self._cached_resources:
226
+ self._cached_resources = await self._list_resources_impl()
227
+ return self._cached_resources
228
+
229
+ @abstractmethod
230
+ async def _list_resources_impl(self) -> list[types.Resource]:
231
+ """Implementation-specific resource listing. Subclasses must implement this."""
217
232
  raise NotImplementedError
218
233
 
219
234
  @abstractmethod
@@ -270,6 +285,17 @@ class BaseHUDClient(AgentMCPClient):
270
285
  async def _fetch_telemetry(self) -> None:
271
286
  """Common telemetry fetching for all hud clients."""
272
287
  try:
288
+ # Get resources (will use cache if available, otherwise fetch)
289
+ resources = await self.list_resources()
290
+ telemetry_available = any(
291
+ str(resource.uri) == "telemetry://live" for resource in resources
292
+ )
293
+
294
+ if not telemetry_available:
295
+ if self.verbose:
296
+ hud_console.debug("Telemetry resource not available from server")
297
+ return
298
+
273
299
  # Try to read telemetry resource directly
274
300
  result = await self.read_resource("telemetry://live")
275
301
  if result and result.contents:
@@ -143,8 +143,8 @@ class FastMCPHUDClient(BaseHUDClient):
143
143
  structuredContent=result.structured_content,
144
144
  )
145
145
 
146
- async def list_resources(self) -> list[types.Resource]:
147
- """List all available resources."""
146
+ async def _list_resources_impl(self) -> list[types.Resource]:
147
+ """Implementation of resource listing for FastMCP client."""
148
148
  if self._client is None:
149
149
  raise ValueError("Client is not connected, call initialize() first")
150
150
  return await self._client.list_resources()
@@ -243,8 +243,8 @@ class MCPUseHUDClient(BaseHUDClient):
243
243
  structuredContent=result.structuredContent,
244
244
  )
245
245
 
246
- async def list_resources(self) -> list[types.Resource]:
247
- """List all available resources."""
246
+ async def _list_resources_impl(self) -> list[types.Resource]:
247
+ """Implementation of resource listing for MCP-use client."""
248
248
  if self._client is None or not self._sessions:
249
249
  raise ValueError("Client is not connected, call initialize() first")
250
250
 
@@ -35,9 +35,15 @@ class MockClient(BaseHUDClient):
35
35
  raise RuntimeError("Not connected")
36
36
  return self._mock_tools
37
37
 
38
- async def list_resources(self) -> list[types.Resource]:
39
- """Minimal list_resources for protocol satisfaction in tests."""
40
- return []
38
+ async def _list_resources_impl(self) -> list[types.Resource]:
39
+ """Minimal resource listing implementation for tests."""
40
+ from pydantic import AnyUrl
41
+
42
+ return [
43
+ types.Resource(
44
+ uri=AnyUrl("telemetry://live"), name="telemetry", description="Live telemetry data"
45
+ )
46
+ ]
41
47
 
42
48
  async def _call_tool(self, tool_call: MCPToolCall) -> MCPToolResult:
43
49
  if tool_call.name == "test_tool":