hud-python 0.4.22__py3-none-any.whl → 0.4.23__py3-none-any.whl

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 (48) hide show
  1. hud/agents/base.py +37 -39
  2. hud/agents/grounded_openai.py +3 -1
  3. hud/agents/misc/response_agent.py +3 -2
  4. hud/agents/openai.py +2 -2
  5. hud/agents/openai_chat_generic.py +3 -1
  6. hud/cli/__init__.py +34 -24
  7. hud/cli/analyze.py +27 -26
  8. hud/cli/build.py +50 -46
  9. hud/cli/debug.py +7 -7
  10. hud/cli/dev.py +107 -99
  11. hud/cli/eval.py +31 -29
  12. hud/cli/hf.py +53 -53
  13. hud/cli/init.py +28 -28
  14. hud/cli/list_func.py +22 -22
  15. hud/cli/pull.py +36 -36
  16. hud/cli/push.py +76 -74
  17. hud/cli/remove.py +42 -40
  18. hud/cli/rl/__init__.py +2 -2
  19. hud/cli/rl/init.py +41 -41
  20. hud/cli/rl/pod.py +97 -91
  21. hud/cli/rl/ssh.py +42 -40
  22. hud/cli/rl/train.py +75 -73
  23. hud/cli/rl/utils.py +10 -10
  24. hud/cli/tests/test_analyze.py +1 -1
  25. hud/cli/tests/test_analyze_metadata.py +2 -2
  26. hud/cli/tests/test_pull.py +45 -45
  27. hud/cli/tests/test_push.py +31 -29
  28. hud/cli/tests/test_registry.py +15 -15
  29. hud/cli/utils/environment.py +11 -11
  30. hud/cli/utils/interactive.py +17 -17
  31. hud/cli/utils/logging.py +12 -12
  32. hud/cli/utils/metadata.py +12 -12
  33. hud/cli/utils/registry.py +5 -5
  34. hud/cli/utils/runner.py +23 -23
  35. hud/cli/utils/server.py +16 -16
  36. hud/shared/hints.py +7 -7
  37. hud/tools/grounding/grounder.py +2 -1
  38. hud/types.py +4 -4
  39. hud/utils/__init__.py +3 -3
  40. hud/utils/{design.py → hud_console.py} +39 -33
  41. hud/utils/pretty_errors.py +6 -6
  42. hud/utils/tests/test_version.py +1 -1
  43. hud/version.py +1 -1
  44. {hud_python-0.4.22.dist-info → hud_python-0.4.23.dist-info}/METADATA +3 -1
  45. {hud_python-0.4.22.dist-info → hud_python-0.4.23.dist-info}/RECORD +48 -48
  46. {hud_python-0.4.22.dist-info → hud_python-0.4.23.dist-info}/WHEEL +0 -0
  47. {hud_python-0.4.22.dist-info → hud_python-0.4.23.dist-info}/entry_points.txt +0 -0
  48. {hud_python-0.4.22.dist-info → hud_python-0.4.23.dist-info}/licenses/LICENSE +0 -0
@@ -110,24 +110,24 @@ class TestGetDockerImageLabels:
110
110
  class TestPushEnvironment:
111
111
  """Test the main push_environment function."""
112
112
 
113
- @mock.patch("hud.cli.push.HUDDesign")
114
- def test_push_no_lock_file(self, mock_design_class, tmp_path):
113
+ @mock.patch("hud.cli.push.HUDConsole")
114
+ def test_push_no_lock_file(self, mock_hud_console_class, tmp_path):
115
115
  """Test pushing when no lock file exists."""
116
- mock_design = mock.Mock()
117
- mock_design_class.return_value = mock_design
116
+ mock_hud_console = mock.Mock()
117
+ mock_hud_console_class.return_value = mock_hud_console
118
118
 
119
119
  with pytest.raises(typer.Exit) as exc_info:
120
120
  push_environment(str(tmp_path))
121
121
 
122
122
  assert exc_info.value.exit_code == 1
123
- mock_design.error.assert_called()
123
+ mock_hud_console.error.assert_called()
124
124
 
125
- @mock.patch("hud.cli.push.HUDDesign")
125
+ @mock.patch("hud.cli.push.HUDConsole")
126
126
  @mock.patch("hud.settings.settings")
127
- def test_push_no_api_key(self, mock_settings, mock_design_class, tmp_path):
127
+ def test_push_no_api_key(self, mock_settings, mock_hud_console_class, tmp_path):
128
128
  """Test pushing without API key."""
129
- mock_design = mock.Mock()
130
- mock_design_class.return_value = mock_design
129
+ mock_hud_console = mock.Mock()
130
+ mock_hud_console_class.return_value = mock_hud_console
131
131
  mock_settings.api_key = None
132
132
 
133
133
  # Create lock file
@@ -144,10 +144,10 @@ class TestPushEnvironment:
144
144
  @mock.patch("subprocess.run")
145
145
  @mock.patch("hud.cli.push.get_docker_username")
146
146
  @mock.patch("hud.settings.settings")
147
- @mock.patch("hud.cli.push.HUDDesign")
147
+ @mock.patch("hud.cli.push.HUDConsole")
148
148
  def test_push_auto_detect_username(
149
149
  self,
150
- mock_design_class,
150
+ mock_hud_console_class,
151
151
  mock_settings,
152
152
  mock_get_username,
153
153
  mock_run,
@@ -157,8 +157,8 @@ class TestPushEnvironment:
157
157
  ):
158
158
  """Test auto-detecting Docker username and pushing."""
159
159
  # Setup mocks
160
- mock_design = mock.Mock()
161
- mock_design_class.return_value = mock_design
160
+ mock_hud_console = mock.Mock()
161
+ mock_hud_console_class.return_value = mock_hud_console
162
162
  mock_settings.api_key = "test-key"
163
163
  mock_settings.hud_telemetry_url = "https://api.hud.test"
164
164
  mock_get_username.return_value = "testuser"
@@ -206,11 +206,11 @@ class TestPushEnvironment:
206
206
 
207
207
  @mock.patch("subprocess.run")
208
208
  @mock.patch("hud.settings.settings")
209
- @mock.patch("hud.cli.push.HUDDesign")
210
- def test_push_explicit_image(self, mock_design_class, mock_settings, mock_run, tmp_path):
209
+ @mock.patch("hud.cli.push.HUDConsole")
210
+ def test_push_explicit_image(self, mock_hud_console_class, mock_settings, mock_run, tmp_path):
211
211
  """Test pushing with explicit image name."""
212
- mock_design = mock.Mock()
213
- mock_design_class.return_value = mock_design
212
+ mock_hud_console = mock.Mock()
213
+ mock_hud_console_class.return_value = mock_hud_console
214
214
  mock_settings.api_key = "test-key"
215
215
 
216
216
  # Create lock file
@@ -227,11 +227,13 @@ class TestPushEnvironment:
227
227
  @mock.patch("subprocess.Popen")
228
228
  @mock.patch("subprocess.run")
229
229
  @mock.patch("hud.settings.settings")
230
- @mock.patch("hud.cli.push.HUDDesign")
231
- def test_push_with_tag(self, mock_design_class, mock_settings, mock_run, mock_popen, tmp_path):
230
+ @mock.patch("hud.cli.push.HUDConsole")
231
+ def test_push_with_tag(
232
+ self, mock_hud_console_class, mock_settings, mock_run, mock_popen, tmp_path
233
+ ):
232
234
  """Test pushing with explicit tag."""
233
- mock_design = mock.Mock()
234
- mock_design_class.return_value = mock_design
235
+ mock_hud_console = mock.Mock()
236
+ mock_hud_console_class.return_value = mock_hud_console
235
237
  mock_settings.api_key = "test-key"
236
238
 
237
239
  # Create lock file
@@ -269,11 +271,11 @@ class TestPushEnvironment:
269
271
  assert "user/test:v2.0" in tag_call[0][0][0]
270
272
 
271
273
  @mock.patch("subprocess.Popen")
272
- @mock.patch("hud.cli.push.HUDDesign")
273
- def test_push_docker_failure(self, mock_design_class, mock_popen):
274
+ @mock.patch("hud.cli.push.HUDConsole")
275
+ def test_push_docker_failure(self, mock_hud_console_class, mock_popen):
274
276
  """Test handling Docker push failure."""
275
- mock_design = mock.Mock()
276
- mock_design_class.return_value = mock_design
277
+ mock_hud_console = mock.Mock()
278
+ mock_hud_console_class.return_value = mock_hud_console
277
279
 
278
280
  # Mock docker push failure
279
281
  mock_process = mock.Mock()
@@ -293,13 +295,13 @@ class TestPushEnvironment:
293
295
  @mock.patch("hud.cli.push.get_docker_image_labels")
294
296
  @mock.patch("subprocess.run")
295
297
  @mock.patch("hud.settings.settings")
296
- @mock.patch("hud.cli.push.HUDDesign")
298
+ @mock.patch("hud.cli.push.HUDConsole")
297
299
  def test_push_with_labels(
298
- self, mock_design_class, mock_settings, mock_run, mock_get_labels, tmp_path
300
+ self, mock_hud_console_class, mock_settings, mock_run, mock_get_labels, tmp_path
299
301
  ):
300
302
  """Test pushing with image labels."""
301
- mock_design = mock.Mock()
302
- mock_design_class.return_value = mock_design
303
+ mock_hud_console = mock.Mock()
304
+ mock_hud_console_class.return_value = mock_hud_console
303
305
  mock_settings.api_key = "test-key"
304
306
 
305
307
  # Create lock file
@@ -118,11 +118,11 @@ class TestExtractNameAndTag:
118
118
  class TestSaveToRegistry:
119
119
  """Test saving to local registry."""
120
120
 
121
- @mock.patch("hud.cli.utils.registry.HUDDesign")
122
- def test_save_success(self, mock_design_class, tmp_path):
121
+ @mock.patch("hud.cli.utils.registry.HUDConsole")
122
+ def test_save_success(self, mock_hud_console_class, tmp_path):
123
123
  """Test successful save to registry."""
124
- mock_design = mock.Mock()
125
- mock_design_class.return_value = mock_design
124
+ mock_hud_console = mock.Mock()
125
+ mock_hud_console_class.return_value = mock_hud_console
126
126
 
127
127
  # Mock home directory
128
128
  with mock.patch("pathlib.Path.home", return_value=tmp_path):
@@ -142,13 +142,13 @@ class TestSaveToRegistry:
142
142
  # Verify directory structure
143
143
  assert result.parent.name == "abc123def456"
144
144
 
145
- mock_design.success.assert_called_once()
145
+ mock_hud_console.success.assert_called_once()
146
146
 
147
- @mock.patch("hud.cli.utils.registry.HUDDesign")
148
- def test_save_verbose(self, mock_design_class, tmp_path):
147
+ @mock.patch("hud.cli.utils.registry.HUDConsole")
148
+ def test_save_verbose(self, mock_hud_console_class, tmp_path):
149
149
  """Test save with verbose output."""
150
- mock_design = mock.Mock()
151
- mock_design_class.return_value = mock_design
150
+ mock_hud_console = mock.Mock()
151
+ mock_hud_console_class.return_value = mock_hud_console
152
152
 
153
153
  with mock.patch("pathlib.Path.home", return_value=tmp_path):
154
154
  lock_data = {"image": "test:v1"}
@@ -157,13 +157,13 @@ class TestSaveToRegistry:
157
157
 
158
158
  assert result is not None
159
159
  # Should show verbose info
160
- assert mock_design.info.call_count >= 1
160
+ assert mock_hud_console.info.call_count >= 1
161
161
 
162
- @mock.patch("hud.cli.utils.registry.HUDDesign")
163
- def test_save_failure(self, mock_design_class):
162
+ @mock.patch("hud.cli.utils.registry.HUDConsole")
163
+ def test_save_failure(self, mock_hud_console_class):
164
164
  """Test handling save failure."""
165
- mock_design = mock.Mock()
166
- mock_design_class.return_value = mock_design
165
+ mock_hud_console = mock.Mock()
166
+ mock_hud_console_class.return_value = mock_hud_console
167
167
 
168
168
  # Mock file operations to fail
169
169
  with (
@@ -175,7 +175,7 @@ class TestSaveToRegistry:
175
175
  result = save_to_registry(lock_data, "test:latest", verbose=True)
176
176
 
177
177
  assert result is None
178
- mock_design.warning.assert_called_once()
178
+ mock_hud_console.warning.assert_called_once()
179
179
 
180
180
 
181
181
  class TestLoadFromRegistry:
@@ -7,9 +7,9 @@ from pathlib import Path
7
7
 
8
8
  import toml
9
9
 
10
- from hud.utils.design import HUDDesign
10
+ from hud.utils.hud_console import HUDConsole
11
11
 
12
- design = HUDDesign()
12
+ hud_console = HUDConsole()
13
13
 
14
14
 
15
15
  def get_image_name(directory: str | Path, image_override: str | None = None) -> tuple[str, str]:
@@ -31,7 +31,7 @@ def get_image_name(directory: str | Path, image_override: str | None = None) ->
31
31
  if config.get("tool", {}).get("hud", {}).get("image"):
32
32
  return config["tool"]["hud"]["image"], "cache"
33
33
  except Exception:
34
- design.error("Error loading pyproject.toml")
34
+ hud_console.error("Error loading pyproject.toml")
35
35
 
36
36
  # Auto-generate with :dev tag
37
37
  dir_path = Path(directory).resolve() # Get absolute path first
@@ -65,10 +65,10 @@ def update_pyproject_toml(directory: str | Path, image_name: str, silent: bool =
65
65
  toml.dump(config, f)
66
66
 
67
67
  if not silent:
68
- design.success(f"Updated pyproject.toml with image: {image_name}")
68
+ hud_console.success(f"Updated pyproject.toml with image: {image_name}")
69
69
  except Exception as e:
70
70
  if not silent:
71
- design.warning(f"Could not update pyproject.toml: {e}")
71
+ hud_console.warning(f"Could not update pyproject.toml: {e}")
72
72
 
73
73
 
74
74
  def build_environment(directory: str | Path, image_name: str, no_cache: bool = False) -> bool:
@@ -82,20 +82,20 @@ def build_environment(directory: str | Path, image_name: str, no_cache: bool = F
82
82
  build_cmd.append("--no-cache")
83
83
  build_cmd.append(str(directory))
84
84
 
85
- design.info(f"🔨 Building image: {image_name}{' (no cache)' if no_cache else ''}")
86
- design.info("") # Empty line before Docker output
85
+ hud_console.info(f"🔨 Building image: {image_name}{' (no cache)' if no_cache else ''}")
86
+ hud_console.info("") # Empty line before Docker output
87
87
 
88
88
  # Just run Docker build directly - it has its own nice live display
89
89
  result = subprocess.run(build_cmd) # noqa: S603
90
90
 
91
91
  if result.returncode == 0:
92
- design.info("") # Empty line after Docker output
93
- design.success(f"Build successful! Image: {image_name}")
92
+ hud_console.info("") # Empty line after Docker output
93
+ hud_console.success(f"Build successful! Image: {image_name}")
94
94
  # Update pyproject.toml (silently since we already showed success)
95
95
  update_pyproject_toml(directory, image_name, silent=True)
96
96
  return True
97
97
  else:
98
- design.error("Build failed!")
98
+ hud_console.error("Build failed!")
99
99
  return False
100
100
 
101
101
 
@@ -127,7 +127,7 @@ def is_environment_directory(path: str | Path) -> bool:
127
127
 
128
128
  # Must have pyproject.toml
129
129
  if not (dir_path / "pyproject.toml").exists():
130
- design.error("pyproject.toml not found")
130
+ hud_console.error("pyproject.toml not found")
131
131
  return False
132
132
 
133
133
  return True
@@ -14,7 +14,7 @@ from rich.syntax import Syntax
14
14
  from rich.tree import Tree
15
15
 
16
16
  from hud.clients import MCPClient
17
- from hud.utils.design import HUDDesign
17
+ from hud.utils.hud_console import HUDConsole
18
18
 
19
19
  console = Console()
20
20
 
@@ -33,7 +33,7 @@ class InteractiveMCPTester:
33
33
  self.verbose = verbose
34
34
  self.client: MCPClient | None = None
35
35
  self.tools: list[Any] = []
36
- self.design = HUDDesign()
36
+ self.console = HUDConsole()
37
37
 
38
38
  async def connect(self) -> bool:
39
39
  """Connect to the MCP server."""
@@ -53,7 +53,7 @@ class InteractiveMCPTester:
53
53
 
54
54
  return True
55
55
  except Exception as e:
56
- self.design.error(f"Failed to connect: {e}")
56
+ self.console.error(f"Failed to connect: {e}")
57
57
  return False
58
58
 
59
59
  async def disconnect(self) -> None:
@@ -361,7 +361,7 @@ class InteractiveMCPTester:
361
361
 
362
362
  async def run(self) -> None:
363
363
  """Run the interactive testing loop."""
364
- self.design.header("Interactive MCP Tester")
364
+ self.console.header("Interactive MCP Tester")
365
365
 
366
366
  # Connect to server
367
367
  console.print(f"[cyan]Connecting to {self.server_url}...[/cyan]")
@@ -397,19 +397,19 @@ class InteractiveMCPTester:
397
397
  await self.disconnect()
398
398
 
399
399
  # Show next steps tutorial
400
- self.design.section_title("Next Steps")
401
- self.design.info("🏗️ Ready to test with real agents? Run:")
402
- self.design.info(" [cyan]hud build[/cyan]")
403
- self.design.info("")
404
- self.design.info("This will:")
405
- self.design.info(" 1. Build your environment image")
406
- self.design.info(" 2. Generate a hud.lock.yaml file")
407
- self.design.info(" 3. Prepare it for testing with agents")
408
- self.design.info("")
409
- self.design.info("Then you can:")
410
- self.design.info(" • Test locally: [cyan]hud run <image>[/cyan]")
411
- self.design.info(" • Push to registry: [cyan]hud push --image <registry/name>[/cyan]")
412
- self.design.info(" • Use with agents via the lock file")
400
+ self.console.section_title("Next Steps")
401
+ self.console.info("🏗️ Ready to test with real agents? Run:")
402
+ self.console.info(" [cyan]hud build[/cyan]")
403
+ self.console.info("")
404
+ self.console.info("This will:")
405
+ self.console.info(" 1. Build your environment image")
406
+ self.console.info(" 2. Generate a hud.lock.yaml file")
407
+ self.console.info(" 3. Prepare it for testing with agents")
408
+ self.console.info("")
409
+ self.console.info("Then you can:")
410
+ self.console.info(" • Test locally: [cyan]hud run <image>[/cyan]")
411
+ self.console.info(" • Push to registry: [cyan]hud push --image <registry/name>[/cyan]")
412
+ self.console.info(" • Use with agents via the lock file")
413
413
 
414
414
  console.print("\n[dim]Happy testing! 🎉[/dim]")
415
415
 
hud/cli/utils/logging.py CHANGED
@@ -15,18 +15,18 @@ if sys.platform == "win32":
15
15
 
16
16
 
17
17
  class Colors:
18
- """ANSI color codes for terminal output."""
19
-
20
- HEADER = "\033[95m"
21
- BLUE = "\033[94m"
22
- CYAN = "\033[96m"
23
- GREEN = "\033[92m"
24
- YELLOW = "\033[93m"
25
- GOLD = "\033[33m"
26
- RED = "\033[91m"
27
- GRAY = "\033[90m"
28
- ENDC = "\033[0m"
29
- BOLD = "\033[1m"
18
+ """ANSI color codes for terminal output - optimized for both light and dark modes."""
19
+
20
+ HEADER = "\033[95m" # Light magenta
21
+ BLUE = "\033[94m" # Light blue
22
+ CYAN = "\033[96m" # Light cyan
23
+ GREEN = "\033[92m" # Light green
24
+ YELLOW = "\033[93m" # Light yellow
25
+ GOLD = "\033[33m" # Gold/orange
26
+ RED = "\033[91m" # Light red
27
+ GRAY = "\033[37m" # Light gray
28
+ ENDC = "\033[0m" # Reset
29
+ BOLD = "\033[1m" # Bold
30
30
 
31
31
 
32
32
  class CaptureLogger:
hud/cli/utils/metadata.py CHANGED
@@ -10,7 +10,7 @@ from rich.console import Console
10
10
  from rich.progress import Progress, SpinnerColumn, TextColumn
11
11
 
12
12
  from hud.settings import settings
13
- from hud.utils.design import HUDDesign
13
+ from hud.utils.hud_console import HUDConsole
14
14
 
15
15
  from .registry import (
16
16
  extract_digest_from_image,
@@ -19,7 +19,7 @@ from .registry import (
19
19
  )
20
20
 
21
21
  console = Console()
22
- design = HUDDesign()
22
+ hud_console = HUDConsole()
23
23
 
24
24
 
25
25
  def fetch_lock_from_registry(reference: str) -> dict | None:
@@ -81,7 +81,7 @@ def check_local_cache(reference: str) -> dict | None:
81
81
  if ref_base in img_base or img_base in ref_base:
82
82
  return lock_data
83
83
  except Exception:
84
- design.error("Error loading lock file")
84
+ hud_console.error("Error loading lock file")
85
85
 
86
86
  return None
87
87
 
@@ -92,9 +92,9 @@ async def analyze_from_metadata(reference: str, output_format: str, verbose: boo
92
92
 
93
93
  from hud.cli.analyze import display_interactive, display_markdown
94
94
 
95
- design.header("MCP Environment Analysis", icon="🔍")
96
- design.info(f"Looking up: {reference}")
97
- design.info("")
95
+ hud_console.header("MCP Environment Analysis", icon="🔍")
96
+ hud_console.info(f"Looking up: {reference}")
97
+ hud_console.info("")
98
98
 
99
99
  lock_data = None
100
100
  source = None
@@ -155,7 +155,7 @@ async def analyze_from_metadata(reference: str, output_format: str, verbose: boo
155
155
  progress.update(task, description="[red]✗ Not found[/red]")
156
156
 
157
157
  if not lock_data:
158
- design.error("Environment metadata not found")
158
+ hud_console.error("Environment metadata not found")
159
159
  console.print("\n[yellow]This environment hasn't been analyzed yet.[/yellow]")
160
160
  console.print("\nOptions:")
161
161
  console.print(f" 1. Pull it first: [cyan]hud pull {reference}[/cyan]")
@@ -205,16 +205,16 @@ async def analyze_from_metadata(reference: str, output_format: str, verbose: boo
205
205
  )
206
206
 
207
207
  # Display results
208
- design.info("")
208
+ hud_console.info("")
209
209
  if source == "local":
210
- design.dim_info("Source:", "Local cache")
210
+ hud_console.dim_info("Source:", "Local cache")
211
211
  else:
212
- design.dim_info("Source:", "HUD registry")
212
+ hud_console.dim_info("Source:", "HUD registry")
213
213
 
214
214
  if "image" in analysis:
215
- design.dim_info("Image:", analysis["image"])
215
+ hud_console.dim_info("Image:", analysis["image"])
216
216
 
217
- design.info("")
217
+ hud_console.info("")
218
218
 
219
219
  # Display results based on format
220
220
  if output_format == "json":
hud/cli/utils/registry.py CHANGED
@@ -7,7 +7,7 @@ from typing import Any
7
7
 
8
8
  import yaml
9
9
 
10
- from hud.utils.design import HUDDesign
10
+ from hud.utils.hud_console import HUDConsole
11
11
 
12
12
 
13
13
  def get_registry_dir() -> Path:
@@ -84,7 +84,7 @@ def save_to_registry(
84
84
  Returns:
85
85
  Path to the saved lock file, or None if save failed
86
86
  """
87
- design = HUDDesign()
87
+ hud_console = HUDConsole()
88
88
 
89
89
  try:
90
90
  # Extract digest for registry storage
@@ -98,14 +98,14 @@ def save_to_registry(
98
98
  with open(local_lock_path, "w") as f:
99
99
  yaml.dump(lock_data, f, default_flow_style=False, sort_keys=False)
100
100
 
101
- design.success(f"Added to local registry: {digest}")
101
+ hud_console.success(f"Added to local registry: {digest}")
102
102
  if verbose:
103
- design.info(f"Registry location: {local_lock_path}")
103
+ hud_console.info(f"Registry location: {local_lock_path}")
104
104
 
105
105
  return local_lock_path
106
106
  except Exception as e:
107
107
  if verbose:
108
- design.warning(f"Failed to save to registry: {e}")
108
+ hud_console.warning(f"Failed to save to registry: {e}")
109
109
  return None
110
110
 
111
111
 
hud/cli/utils/runner.py CHANGED
@@ -6,7 +6,7 @@ import asyncio
6
6
  import subprocess
7
7
  import sys
8
8
 
9
- from hud.utils.design import HUDDesign
9
+ from hud.utils.hud_console import HUDConsole
10
10
 
11
11
  from .logging import find_free_port
12
12
  from .server import MCPServerManager, run_server_with_interactive
@@ -14,29 +14,29 @@ from .server import MCPServerManager, run_server_with_interactive
14
14
 
15
15
  def run_stdio_server(image: str, docker_args: list[str], verbose: bool) -> None:
16
16
  """Run Docker image as stdio MCP server (direct passthrough)."""
17
- design = HUDDesign() # Use stderr for stdio mode
17
+ hud_console = HUDConsole() # Use stderr for stdio mode
18
18
 
19
19
  # Build docker command
20
20
  docker_cmd = ["docker", "run", "--rm", "-i", *docker_args, image]
21
21
 
22
22
  if verbose:
23
- design.info(f"🐳 Running: {' '.join(docker_cmd)}")
23
+ hud_console.info(f"🐳 Running: {' '.join(docker_cmd)}")
24
24
 
25
25
  # Run docker directly with stdio passthrough
26
26
  try:
27
27
  result = subprocess.run(docker_cmd, stdin=sys.stdin) # noqa: S603
28
28
  sys.exit(result.returncode)
29
29
  except KeyboardInterrupt:
30
- design.info("\n👋 Shutting down...")
30
+ hud_console.info("\n👋 Shutting down...")
31
31
  sys.exit(0)
32
32
  except Exception as e:
33
- design.error(f"Error: {e}")
33
+ hud_console.error(f"Error: {e}")
34
34
  sys.exit(1)
35
35
 
36
36
 
37
37
  async def run_http_server(image: str, docker_args: list[str], port: int, verbose: bool) -> None:
38
38
  """Run Docker image as HTTP MCP server (proxy mode)."""
39
- design = HUDDesign()
39
+ hud_console = HUDConsole()
40
40
 
41
41
  # Create server manager
42
42
  server_manager = MCPServerManager(image, docker_args)
@@ -44,11 +44,11 @@ async def run_http_server(image: str, docker_args: list[str], port: int, verbose
44
44
  # Find available port
45
45
  actual_port = find_free_port(port)
46
46
  if actual_port is None:
47
- design.error(f"No available ports found starting from {port}")
47
+ hud_console.error(f"No available ports found starting from {port}")
48
48
  return
49
49
 
50
50
  if actual_port != port:
51
- design.warning(f"Port {port} in use, using port {actual_port} instead")
51
+ hud_console.warning(f"Port {port} in use, using port {actual_port} instead")
52
52
 
53
53
  # Clean up any existing container
54
54
  server_manager.cleanup_container()
@@ -63,21 +63,21 @@ async def run_http_server(image: str, docker_args: list[str], port: int, verbose
63
63
  proxy = server_manager.create_proxy(config)
64
64
 
65
65
  # Show header
66
- design.info("") # Empty line
67
- design.header("HUD MCP Server", icon="🌐")
66
+ hud_console.info("") # Empty line
67
+ hud_console.header("HUD MCP Server", icon="🌐")
68
68
 
69
69
  # Show configuration
70
- design.section_title("Server Information")
71
- design.info(f"Port: {actual_port}")
72
- design.info(f"URL: http://localhost:{actual_port}/mcp")
73
- design.info(f"Container: {server_manager.container_name}")
74
- design.info("")
75
- design.progress_message("Press Ctrl+C to stop")
70
+ hud_console.section_title("Server Information")
71
+ hud_console.info(f"Port: {actual_port}")
72
+ hud_console.info(f"URL: http://localhost:{actual_port}/mcp")
73
+ hud_console.info(f"Container: {server_manager.container_name}")
74
+ hud_console.info("")
75
+ hud_console.progress_message("Press Ctrl+C to stop")
76
76
 
77
77
  try:
78
78
  await server_manager.run_http_server(proxy, actual_port, verbose)
79
79
  except KeyboardInterrupt:
80
- design.info("\n👋 Shutting down...")
80
+ hud_console.info("\n👋 Shutting down...")
81
81
  finally:
82
82
  # Clean up container
83
83
  server_manager.cleanup_container()
@@ -105,8 +105,8 @@ def run_mcp_server(
105
105
  """Run Docker image as MCP server with specified transport."""
106
106
  if transport == "stdio":
107
107
  if interactive:
108
- design = HUDDesign()
109
- design.error("Interactive mode requires HTTP transport")
108
+ hud_console = HUDConsole()
109
+ hud_console.error("Interactive mode requires HTTP transport")
110
110
  sys.exit(1)
111
111
  run_stdio_server(image, docker_args, verbose)
112
112
  elif transport == "http":
@@ -126,9 +126,9 @@ def run_mcp_server(
126
126
  "Application shutdown complete",
127
127
  ]
128
128
  ):
129
- design = HUDDesign()
130
- design.error(f"Unexpected error: {e}")
129
+ hud_console = HUDConsole()
130
+ hud_console.error(f"Unexpected error: {e}")
131
131
  else:
132
- design = HUDDesign()
133
- design.error(f"Unknown transport: {transport}")
132
+ hud_console = HUDConsole()
133
+ hud_console.error(f"Unknown transport: {transport}")
134
134
  sys.exit(1)
hud/cli/utils/server.py CHANGED
@@ -7,7 +7,7 @@ from typing import Any
7
7
 
8
8
  from fastmcp import FastMCP
9
9
 
10
- from hud.utils.design import HUDDesign
10
+ from hud.utils.hud_console import HUDConsole
11
11
 
12
12
  from .docker import generate_container_name, remove_container
13
13
 
@@ -24,7 +24,7 @@ class MCPServerManager:
24
24
  """
25
25
  self.image = image
26
26
  self.docker_args = docker_args or []
27
- self.design = HUDDesign()
27
+ self.console = HUDConsole()
28
28
  self.container_name = self._generate_container_name()
29
29
 
30
30
  def _generate_container_name(self) -> str:
@@ -155,7 +155,7 @@ class MCPServerManager:
155
155
  pass # Normal cancellation
156
156
  except Exception as e:
157
157
  if verbose:
158
- self.design.error(f"Server error: {e}")
158
+ self.console.error(f"Server error: {e}")
159
159
  raise
160
160
 
161
161
 
@@ -174,16 +174,16 @@ async def run_server_with_interactive(
174
174
  from .interactive import run_interactive_mode
175
175
  from .logging import find_free_port
176
176
 
177
- design = HUDDesign()
177
+ hud_console = HUDConsole()
178
178
 
179
179
  # Find available port
180
180
  actual_port = find_free_port(port)
181
181
  if actual_port is None:
182
- design.error(f"No available ports found starting from {port}")
182
+ hud_console.error(f"No available ports found starting from {port}")
183
183
  return
184
184
 
185
185
  if actual_port != port:
186
- design.warning(f"Port {port} in use, using port {actual_port} instead")
186
+ hud_console.warning(f"Port {port} in use, using port {actual_port} instead")
187
187
 
188
188
  # Clean up any existing container
189
189
  server_manager.cleanup_container()
@@ -198,16 +198,16 @@ async def run_server_with_interactive(
198
198
  proxy = server_manager.create_proxy(config, f"HUD Interactive - {server_manager.image}")
199
199
 
200
200
  # Show header
201
- design.info("") # Empty line
202
- design.header("HUD MCP Server - Interactive Mode", icon="🎮")
201
+ hud_console.info("") # Empty line
202
+ hud_console.header("HUD MCP Server - Interactive Mode", icon="🎮")
203
203
 
204
204
  # Show configuration
205
- design.section_title("Server Information")
206
- design.info(f"Image: {server_manager.image}")
207
- design.info(f"Port: {actual_port}")
208
- design.info(f"URL: http://localhost:{actual_port}/mcp")
209
- design.info(f"Container: {server_manager.container_name}")
210
- design.info("")
205
+ hud_console.section_title("Server Information")
206
+ hud_console.info(f"Image: {server_manager.image}")
207
+ hud_console.info(f"Port: {actual_port}")
208
+ hud_console.info(f"URL: http://localhost:{actual_port}/mcp")
209
+ hud_console.info(f"Container: {server_manager.container_name}")
210
+ hud_console.info("")
211
211
 
212
212
  # Create event to signal server is ready
213
213
  server_ready = asyncio.Event()
@@ -236,7 +236,7 @@ async def run_server_with_interactive(
236
236
  await run_interactive_mode(server_url, verbose=verbose)
237
237
 
238
238
  except KeyboardInterrupt:
239
- design.info("\n👋 Shutting down...")
239
+ hud_console.info("\n👋 Shutting down...")
240
240
  finally:
241
241
  # Cancel server task
242
242
  if server_task and not server_task.done():
@@ -244,7 +244,7 @@ async def run_server_with_interactive(
244
244
  try:
245
245
  await server_task
246
246
  except asyncio.CancelledError:
247
- design.error("Server task cancelled")
247
+ hud_console.error("Server task cancelled")
248
248
 
249
249
  # Clean up container
250
250
  server_manager.cleanup_container()