hud-python 0.4.1__py3-none-any.whl ā 0.4.3__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.
- hud/__init__.py +22 -22
- hud/agents/__init__.py +13 -15
- hud/agents/base.py +599 -599
- hud/agents/claude.py +373 -373
- hud/agents/langchain.py +261 -250
- hud/agents/misc/__init__.py +7 -7
- hud/agents/misc/response_agent.py +82 -80
- hud/agents/openai.py +352 -352
- hud/agents/openai_chat_generic.py +154 -154
- hud/agents/tests/__init__.py +1 -1
- hud/agents/tests/test_base.py +742 -742
- hud/agents/tests/test_claude.py +324 -324
- hud/agents/tests/test_client.py +363 -363
- hud/agents/tests/test_openai.py +237 -237
- hud/cli/__init__.py +617 -617
- hud/cli/__main__.py +8 -8
- hud/cli/analyze.py +371 -371
- hud/cli/analyze_metadata.py +230 -230
- hud/cli/build.py +498 -427
- hud/cli/clone.py +185 -185
- hud/cli/cursor.py +92 -92
- hud/cli/debug.py +392 -392
- hud/cli/docker_utils.py +83 -83
- hud/cli/init.py +280 -281
- hud/cli/interactive.py +353 -353
- hud/cli/mcp_server.py +764 -756
- hud/cli/pull.py +330 -336
- hud/cli/push.py +404 -370
- hud/cli/remote_runner.py +311 -311
- hud/cli/runner.py +160 -160
- hud/cli/tests/__init__.py +3 -3
- hud/cli/tests/test_analyze.py +284 -284
- hud/cli/tests/test_cli_init.py +265 -265
- hud/cli/tests/test_cli_main.py +27 -27
- hud/cli/tests/test_clone.py +142 -142
- hud/cli/tests/test_cursor.py +253 -253
- hud/cli/tests/test_debug.py +453 -453
- hud/cli/tests/test_mcp_server.py +139 -139
- hud/cli/tests/test_utils.py +388 -388
- hud/cli/utils.py +263 -263
- hud/clients/README.md +143 -143
- hud/clients/__init__.py +16 -16
- hud/clients/base.py +378 -379
- hud/clients/fastmcp.py +222 -222
- hud/clients/mcp_use.py +298 -278
- hud/clients/tests/__init__.py +1 -1
- hud/clients/tests/test_client_integration.py +111 -111
- hud/clients/tests/test_fastmcp.py +342 -342
- hud/clients/tests/test_protocol.py +188 -188
- hud/clients/utils/__init__.py +1 -1
- hud/clients/utils/retry_transport.py +160 -160
- hud/datasets.py +327 -322
- hud/misc/__init__.py +1 -1
- hud/misc/claude_plays_pokemon.py +292 -292
- hud/otel/__init__.py +35 -35
- hud/otel/collector.py +142 -142
- hud/otel/config.py +164 -164
- hud/otel/context.py +536 -536
- hud/otel/exporters.py +366 -366
- hud/otel/instrumentation.py +97 -97
- hud/otel/processors.py +118 -118
- hud/otel/tests/__init__.py +1 -1
- hud/otel/tests/test_processors.py +197 -197
- hud/server/__init__.py +5 -5
- hud/server/context.py +114 -114
- hud/server/helper/__init__.py +5 -5
- hud/server/low_level.py +132 -132
- hud/server/server.py +170 -166
- hud/server/tests/__init__.py +3 -3
- hud/settings.py +73 -73
- hud/shared/__init__.py +5 -5
- hud/shared/exceptions.py +180 -180
- hud/shared/requests.py +264 -264
- hud/shared/tests/test_exceptions.py +157 -157
- hud/shared/tests/test_requests.py +275 -275
- hud/telemetry/__init__.py +25 -25
- hud/telemetry/instrument.py +379 -379
- hud/telemetry/job.py +309 -309
- hud/telemetry/replay.py +74 -74
- hud/telemetry/trace.py +83 -83
- hud/tools/__init__.py +33 -33
- hud/tools/base.py +365 -365
- hud/tools/bash.py +161 -161
- hud/tools/computer/__init__.py +15 -15
- hud/tools/computer/anthropic.py +437 -437
- hud/tools/computer/hud.py +376 -376
- hud/tools/computer/openai.py +295 -295
- hud/tools/computer/settings.py +82 -82
- hud/tools/edit.py +314 -314
- hud/tools/executors/__init__.py +30 -30
- hud/tools/executors/base.py +539 -539
- hud/tools/executors/pyautogui.py +621 -621
- hud/tools/executors/tests/__init__.py +1 -1
- hud/tools/executors/tests/test_base_executor.py +338 -338
- hud/tools/executors/tests/test_pyautogui_executor.py +165 -165
- hud/tools/executors/xdo.py +511 -511
- hud/tools/playwright.py +412 -412
- hud/tools/tests/__init__.py +3 -3
- hud/tools/tests/test_base.py +282 -282
- hud/tools/tests/test_bash.py +158 -158
- hud/tools/tests/test_bash_extended.py +197 -197
- hud/tools/tests/test_computer.py +425 -425
- hud/tools/tests/test_computer_actions.py +34 -34
- hud/tools/tests/test_edit.py +259 -259
- hud/tools/tests/test_init.py +27 -27
- hud/tools/tests/test_playwright_tool.py +183 -183
- hud/tools/tests/test_tools.py +145 -145
- hud/tools/tests/test_utils.py +156 -156
- hud/tools/types.py +72 -72
- hud/tools/utils.py +50 -50
- hud/types.py +136 -136
- hud/utils/__init__.py +10 -10
- hud/utils/async_utils.py +65 -65
- hud/utils/design.py +236 -168
- hud/utils/mcp.py +55 -55
- hud/utils/progress.py +149 -149
- hud/utils/telemetry.py +66 -66
- hud/utils/tests/test_async_utils.py +173 -173
- hud/utils/tests/test_init.py +17 -17
- hud/utils/tests/test_progress.py +261 -261
- hud/utils/tests/test_telemetry.py +82 -82
- hud/utils/tests/test_version.py +8 -8
- hud/version.py +7 -7
- {hud_python-0.4.1.dist-info ā hud_python-0.4.3.dist-info}/METADATA +10 -8
- hud_python-0.4.3.dist-info/RECORD +131 -0
- {hud_python-0.4.1.dist-info ā hud_python-0.4.3.dist-info}/licenses/LICENSE +21 -21
- hud/agents/art.py +0 -101
- hud_python-0.4.1.dist-info/RECORD +0 -132
- {hud_python-0.4.1.dist-info ā hud_python-0.4.3.dist-info}/WHEEL +0 -0
- {hud_python-0.4.1.dist-info ā hud_python-0.4.3.dist-info}/entry_points.txt +0 -0
hud/cli/runner.py
CHANGED
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
"""Run Docker images as MCP servers."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import asyncio
|
|
6
|
-
import subprocess
|
|
7
|
-
import sys
|
|
8
|
-
|
|
9
|
-
import click
|
|
10
|
-
from fastmcp import FastMCP
|
|
11
|
-
|
|
12
|
-
from hud.utils.design import HUDDesign
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def run_stdio_server(image: str, docker_args: list[str], verbose: bool) -> None:
|
|
16
|
-
"""Run Docker image as stdio MCP server (direct passthrough)."""
|
|
17
|
-
design = HUDDesign(stderr=True) # Use stderr for stdio mode
|
|
18
|
-
|
|
19
|
-
# Build docker command
|
|
20
|
-
docker_cmd = ["docker", "run", "--rm", "-i", *docker_args, image]
|
|
21
|
-
|
|
22
|
-
if verbose:
|
|
23
|
-
design.info(f"š³ Running: {' '.join(docker_cmd)}")
|
|
24
|
-
|
|
25
|
-
# Run docker directly with stdio passthrough
|
|
26
|
-
try:
|
|
27
|
-
result = subprocess.run(docker_cmd, stdin=sys.stdin) # noqa: S603
|
|
28
|
-
sys.exit(result.returncode)
|
|
29
|
-
except KeyboardInterrupt:
|
|
30
|
-
design.info("\nš Shutting down...")
|
|
31
|
-
sys.exit(0)
|
|
32
|
-
except Exception as e:
|
|
33
|
-
design.error(f"Error: {e}")
|
|
34
|
-
sys.exit(1)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
async def run_http_server(image: str, docker_args: list[str], port: int, verbose: bool) -> None:
|
|
38
|
-
"""Run Docker image as HTTP MCP server (proxy mode)."""
|
|
39
|
-
from .utils import find_free_port
|
|
40
|
-
|
|
41
|
-
design = HUDDesign()
|
|
42
|
-
|
|
43
|
-
# Find available port
|
|
44
|
-
actual_port = find_free_port(port)
|
|
45
|
-
if actual_port is None:
|
|
46
|
-
design.error(f"No available ports found starting from {port}")
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
if actual_port != port:
|
|
50
|
-
design.warning(f"Port {port} in use, using port {actual_port} instead")
|
|
51
|
-
|
|
52
|
-
# Generate container name
|
|
53
|
-
container_name = f"run-{image.replace(':', '-').replace('/', '-')}"
|
|
54
|
-
|
|
55
|
-
# Remove any existing container with the same name
|
|
56
|
-
try:
|
|
57
|
-
subprocess.run( # noqa: ASYNC221, S603
|
|
58
|
-
["docker", "rm", "-f", container_name], # noqa: S607
|
|
59
|
-
stdout=subprocess.DEVNULL,
|
|
60
|
-
stderr=subprocess.DEVNULL,
|
|
61
|
-
check=False, # Don't raise error if container doesn't exist
|
|
62
|
-
)
|
|
63
|
-
except Exception:
|
|
64
|
-
click.echo(f"Failed to remove existing container {container_name}", err=True)
|
|
65
|
-
|
|
66
|
-
# Build docker command for stdio container
|
|
67
|
-
docker_cmd = (
|
|
68
|
-
*[
|
|
69
|
-
"docker",
|
|
70
|
-
"run",
|
|
71
|
-
"--rm",
|
|
72
|
-
"-i",
|
|
73
|
-
"--name",
|
|
74
|
-
container_name,
|
|
75
|
-
],
|
|
76
|
-
docker_args,
|
|
77
|
-
[image],
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
# Create MCP config for stdio transport
|
|
81
|
-
config = {
|
|
82
|
-
"mcpServers": {
|
|
83
|
-
"default": {
|
|
84
|
-
"command": docker_cmd[0],
|
|
85
|
-
"args": docker_cmd[1:] if len(docker_cmd) > 1 else [],
|
|
86
|
-
# transport defaults to stdio
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
# Set up logging suppression
|
|
92
|
-
import logging
|
|
93
|
-
import os
|
|
94
|
-
|
|
95
|
-
os.environ["FASTMCP_DISABLE_BANNER"] = "1"
|
|
96
|
-
|
|
97
|
-
if not verbose:
|
|
98
|
-
logging.getLogger("fastmcp").setLevel(logging.ERROR)
|
|
99
|
-
logging.getLogger("mcp").setLevel(logging.ERROR)
|
|
100
|
-
logging.getLogger("uvicorn").setLevel(logging.ERROR)
|
|
101
|
-
logging.getLogger("uvicorn.access").setLevel(logging.ERROR)
|
|
102
|
-
logging.getLogger("uvicorn.error").setLevel(logging.ERROR)
|
|
103
|
-
|
|
104
|
-
import warnings
|
|
105
|
-
|
|
106
|
-
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
107
|
-
|
|
108
|
-
# Create HTTP proxy
|
|
109
|
-
proxy = FastMCP.as_proxy(config, name=f"HUD Run - {image}")
|
|
110
|
-
|
|
111
|
-
# Show header
|
|
112
|
-
design.info("") # Empty line
|
|
113
|
-
design.header("HUD MCP Server", icon="š")
|
|
114
|
-
|
|
115
|
-
# Show configuration
|
|
116
|
-
design.section_title("Server Information")
|
|
117
|
-
design.info(f"Port: {actual_port}")
|
|
118
|
-
design.info(f"URL: http://localhost:{actual_port}/mcp")
|
|
119
|
-
design.info(f"Container: {container_name}")
|
|
120
|
-
design.info("")
|
|
121
|
-
design.progress_message("Press Ctrl+C to stop")
|
|
122
|
-
|
|
123
|
-
try:
|
|
124
|
-
await proxy.run_async(
|
|
125
|
-
transport="http",
|
|
126
|
-
host="0.0.0.0", # noqa: S104
|
|
127
|
-
port=actual_port,
|
|
128
|
-
path="/mcp",
|
|
129
|
-
log_level="error" if not verbose else "info",
|
|
130
|
-
show_banner=False,
|
|
131
|
-
)
|
|
132
|
-
except KeyboardInterrupt:
|
|
133
|
-
design.info("\nš Shutting down...")
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def run_mcp_server(
|
|
137
|
-
image: str, docker_args: list[str], transport: str, port: int, verbose: bool
|
|
138
|
-
) -> None:
|
|
139
|
-
"""Run Docker image as MCP server with specified transport."""
|
|
140
|
-
if transport == "stdio":
|
|
141
|
-
run_stdio_server(image, docker_args, verbose)
|
|
142
|
-
elif transport == "http":
|
|
143
|
-
try:
|
|
144
|
-
asyncio.run(run_http_server(image, docker_args, port, verbose))
|
|
145
|
-
except Exception as e:
|
|
146
|
-
# Suppress the graceful shutdown errors
|
|
147
|
-
if not any(
|
|
148
|
-
x in str(e)
|
|
149
|
-
for x in [
|
|
150
|
-
"timeout graceful shutdown exceeded",
|
|
151
|
-
"Cancel 0 running task(s)",
|
|
152
|
-
"Application shutdown complete",
|
|
153
|
-
]
|
|
154
|
-
):
|
|
155
|
-
design = HUDDesign()
|
|
156
|
-
design.error(f"Unexpected error: {e}")
|
|
157
|
-
else:
|
|
158
|
-
design = HUDDesign()
|
|
159
|
-
design.error(f"Unknown transport: {transport}")
|
|
160
|
-
sys.exit(1)
|
|
1
|
+
"""Run Docker images as MCP servers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
from fastmcp import FastMCP
|
|
11
|
+
|
|
12
|
+
from hud.utils.design import HUDDesign
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def run_stdio_server(image: str, docker_args: list[str], verbose: bool) -> None:
|
|
16
|
+
"""Run Docker image as stdio MCP server (direct passthrough)."""
|
|
17
|
+
design = HUDDesign(stderr=True) # Use stderr for stdio mode
|
|
18
|
+
|
|
19
|
+
# Build docker command
|
|
20
|
+
docker_cmd = ["docker", "run", "--rm", "-i", *docker_args, image]
|
|
21
|
+
|
|
22
|
+
if verbose:
|
|
23
|
+
design.info(f"š³ Running: {' '.join(docker_cmd)}")
|
|
24
|
+
|
|
25
|
+
# Run docker directly with stdio passthrough
|
|
26
|
+
try:
|
|
27
|
+
result = subprocess.run(docker_cmd, stdin=sys.stdin) # noqa: S603
|
|
28
|
+
sys.exit(result.returncode)
|
|
29
|
+
except KeyboardInterrupt:
|
|
30
|
+
design.info("\nš Shutting down...")
|
|
31
|
+
sys.exit(0)
|
|
32
|
+
except Exception as e:
|
|
33
|
+
design.error(f"Error: {e}")
|
|
34
|
+
sys.exit(1)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def run_http_server(image: str, docker_args: list[str], port: int, verbose: bool) -> None:
|
|
38
|
+
"""Run Docker image as HTTP MCP server (proxy mode)."""
|
|
39
|
+
from .utils import find_free_port
|
|
40
|
+
|
|
41
|
+
design = HUDDesign()
|
|
42
|
+
|
|
43
|
+
# Find available port
|
|
44
|
+
actual_port = find_free_port(port)
|
|
45
|
+
if actual_port is None:
|
|
46
|
+
design.error(f"No available ports found starting from {port}")
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
if actual_port != port:
|
|
50
|
+
design.warning(f"Port {port} in use, using port {actual_port} instead")
|
|
51
|
+
|
|
52
|
+
# Generate container name
|
|
53
|
+
container_name = f"run-{image.replace(':', '-').replace('/', '-')}"
|
|
54
|
+
|
|
55
|
+
# Remove any existing container with the same name
|
|
56
|
+
try:
|
|
57
|
+
subprocess.run( # noqa: ASYNC221, S603
|
|
58
|
+
["docker", "rm", "-f", container_name], # noqa: S607
|
|
59
|
+
stdout=subprocess.DEVNULL,
|
|
60
|
+
stderr=subprocess.DEVNULL,
|
|
61
|
+
check=False, # Don't raise error if container doesn't exist
|
|
62
|
+
)
|
|
63
|
+
except Exception:
|
|
64
|
+
click.echo(f"Failed to remove existing container {container_name}", err=True)
|
|
65
|
+
|
|
66
|
+
# Build docker command for stdio container
|
|
67
|
+
docker_cmd = (
|
|
68
|
+
*[
|
|
69
|
+
"docker",
|
|
70
|
+
"run",
|
|
71
|
+
"--rm",
|
|
72
|
+
"-i",
|
|
73
|
+
"--name",
|
|
74
|
+
container_name,
|
|
75
|
+
],
|
|
76
|
+
docker_args,
|
|
77
|
+
[image],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Create MCP config for stdio transport
|
|
81
|
+
config = {
|
|
82
|
+
"mcpServers": {
|
|
83
|
+
"default": {
|
|
84
|
+
"command": docker_cmd[0],
|
|
85
|
+
"args": docker_cmd[1:] if len(docker_cmd) > 1 else [],
|
|
86
|
+
# transport defaults to stdio
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Set up logging suppression
|
|
92
|
+
import logging
|
|
93
|
+
import os
|
|
94
|
+
|
|
95
|
+
os.environ["FASTMCP_DISABLE_BANNER"] = "1"
|
|
96
|
+
|
|
97
|
+
if not verbose:
|
|
98
|
+
logging.getLogger("fastmcp").setLevel(logging.ERROR)
|
|
99
|
+
logging.getLogger("mcp").setLevel(logging.ERROR)
|
|
100
|
+
logging.getLogger("uvicorn").setLevel(logging.ERROR)
|
|
101
|
+
logging.getLogger("uvicorn.access").setLevel(logging.ERROR)
|
|
102
|
+
logging.getLogger("uvicorn.error").setLevel(logging.ERROR)
|
|
103
|
+
|
|
104
|
+
import warnings
|
|
105
|
+
|
|
106
|
+
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
107
|
+
|
|
108
|
+
# Create HTTP proxy
|
|
109
|
+
proxy = FastMCP.as_proxy(config, name=f"HUD Run - {image}")
|
|
110
|
+
|
|
111
|
+
# Show header
|
|
112
|
+
design.info("") # Empty line
|
|
113
|
+
design.header("HUD MCP Server", icon="š")
|
|
114
|
+
|
|
115
|
+
# Show configuration
|
|
116
|
+
design.section_title("Server Information")
|
|
117
|
+
design.info(f"Port: {actual_port}")
|
|
118
|
+
design.info(f"URL: http://localhost:{actual_port}/mcp")
|
|
119
|
+
design.info(f"Container: {container_name}")
|
|
120
|
+
design.info("")
|
|
121
|
+
design.progress_message("Press Ctrl+C to stop")
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
await proxy.run_async(
|
|
125
|
+
transport="http",
|
|
126
|
+
host="0.0.0.0", # noqa: S104
|
|
127
|
+
port=actual_port,
|
|
128
|
+
path="/mcp",
|
|
129
|
+
log_level="error" if not verbose else "info",
|
|
130
|
+
show_banner=False,
|
|
131
|
+
)
|
|
132
|
+
except KeyboardInterrupt:
|
|
133
|
+
design.info("\nš Shutting down...")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def run_mcp_server(
|
|
137
|
+
image: str, docker_args: list[str], transport: str, port: int, verbose: bool
|
|
138
|
+
) -> None:
|
|
139
|
+
"""Run Docker image as MCP server with specified transport."""
|
|
140
|
+
if transport == "stdio":
|
|
141
|
+
run_stdio_server(image, docker_args, verbose)
|
|
142
|
+
elif transport == "http":
|
|
143
|
+
try:
|
|
144
|
+
asyncio.run(run_http_server(image, docker_args, port, verbose))
|
|
145
|
+
except Exception as e:
|
|
146
|
+
# Suppress the graceful shutdown errors
|
|
147
|
+
if not any(
|
|
148
|
+
x in str(e)
|
|
149
|
+
for x in [
|
|
150
|
+
"timeout graceful shutdown exceeded",
|
|
151
|
+
"Cancel 0 running task(s)",
|
|
152
|
+
"Application shutdown complete",
|
|
153
|
+
]
|
|
154
|
+
):
|
|
155
|
+
design = HUDDesign()
|
|
156
|
+
design.error(f"Unexpected error: {e}")
|
|
157
|
+
else:
|
|
158
|
+
design = HUDDesign()
|
|
159
|
+
design.error(f"Unknown transport: {transport}")
|
|
160
|
+
sys.exit(1)
|
hud/cli/tests/__init__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"""Tests for HUD CLI module."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
1
|
+
"""Tests for HUD CLI module."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|