hypercli-sdk 2026.4.5__tar.gz → 2026.4.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/PKG-INFO +1 -1
  2. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/__init__.py +1 -1
  3. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/client.py +2 -3
  4. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/renders.py +4 -4
  5. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/user.py +1 -1
  6. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/pyproject.toml +1 -1
  7. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_agents.py +34 -0
  8. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_keys.py +2 -6
  9. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_renders_subscription.py +7 -7
  10. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/.gitignore +0 -0
  11. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/README.md +0 -0
  12. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/agent.py +0 -0
  13. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/agents.py +0 -0
  14. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/billing.py +0 -0
  15. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/config.py +0 -0
  16. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/files.py +0 -0
  17. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/gateway.py +0 -0
  18. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/http.py +0 -0
  19. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/instances.py +0 -0
  20. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/job/__init__.py +0 -0
  21. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/job/base.py +0 -0
  22. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/job/comfyui.py +0 -0
  23. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/job/gradio.py +0 -0
  24. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/jobs.py +0 -0
  25. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/keys.py +0 -0
  26. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/logs.py +0 -0
  27. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/shell.py +0 -0
  28. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/voice.py +0 -0
  29. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/x402.py +0 -0
  30. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/conftest.py +0 -0
  31. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_agents.py +0 -0
  32. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_auth.py +0 -0
  33. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_billing.py +0 -0
  34. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_instances.py +0 -0
  35. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_jobs_dryrun.py +0 -0
  36. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_keys.py +0 -0
  37. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_renders.py +0 -0
  38. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_apply_params.py +0 -0
  39. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_claw.py +0 -0
  40. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_config.py +0 -0
  41. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_exec_shell_dryrun.py +0 -0
  42. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_gateway.py +0 -0
  43. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_gateway_retry.py +0 -0
  44. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_graph_to_api.py +0 -0
  45. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_http.py +0 -0
  46. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_jobs.py +0 -0
  47. {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_voice.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypercli-sdk
3
- Version: 2026.4.5
3
+ Version: 2026.4.6
4
4
  Summary: Python SDK for HyperCLI - GPU orchestration and HyperAgent API
5
5
  Project-URL: Homepage, https://hypercli.com
6
6
  Project-URL: Documentation, https://docs.hypercli.com
@@ -50,7 +50,7 @@ from .gateway import (
50
50
  extract_gateway_chat_tool_calls,
51
51
  normalize_gateway_chat_message,
52
52
  )
53
- __version__ = "2026.4.5"
53
+ __version__ = "2026.4.6"
54
54
  __all__ = [
55
55
  "HyperCLI",
56
56
  "configure",
@@ -86,7 +86,6 @@ class HyperCLI:
86
86
  agents_ws_url
87
87
  or (_derive_agents_ws_url(self._api_url, agent_dev) if api_url else get_agents_ws_url(agent_dev))
88
88
  )
89
- auth_http = HTTPClient(resolved_agents_api_base, self._api_key)
90
89
  self.deployments = Deployments(
91
90
  self._http,
92
91
  api_key=resolved_agent_api_key,
@@ -95,9 +94,9 @@ class HyperCLI:
95
94
  )
96
95
  self.billing = Billing(self._http)
97
96
  self.jobs = Jobs(self._http)
98
- self.user = UserAPI(self._http, auth_http=auth_http)
97
+ self.user = UserAPI(self._http)
99
98
  self.instances = Instances(self._http)
100
- self.renders = Renders(self._http, auth_http=auth_http)
99
+ self.renders = Renders(self._http)
101
100
  self.files = Files(self._http)
102
101
  self.voice = VoiceAPI(self._http)
103
102
  self.keys = KeysAPI(self._http)
@@ -63,7 +63,7 @@ class Renders:
63
63
 
64
64
  def _auth_me(self) -> dict[str, Any]:
65
65
  if self._auth_me_cache is None:
66
- self._auth_me_cache = self._auth_http.get("/auth/me")
66
+ self._auth_me_cache = self._auth_http.get("/api/auth/me")
67
67
  return self._auth_me_cache
68
68
 
69
69
  def _supports_subscription_family(self, family: str, resource: str | None = None) -> bool:
@@ -83,7 +83,7 @@ class Renders:
83
83
  return False
84
84
 
85
85
  def _post_flow(self, flow_type: str, payload: dict[str, Any]) -> dict[str, Any]:
86
- primary = f"/agents/flow/{flow_type}" if self._supports_subscription_family("renders", flow_type) else f"/api/flow/{flow_type}"
86
+ primary = f"/agents/flow/{flow_type}" if self._supports_subscription_family("flows", flow_type) else f"/api/flow/{flow_type}"
87
87
  try:
88
88
  return self._http.post(primary, json=payload)
89
89
  except APIError as exc:
@@ -93,7 +93,7 @@ class Renders:
93
93
 
94
94
  def _get_render(self, render_id: str, *, status: bool = False) -> dict[str, Any]:
95
95
  suffix = "/status" if status else ""
96
- primary = f"/agents/flow/renders/{render_id}{suffix}" if self._supports_subscription_family("renders") else f"/api/renders/{render_id}{suffix}"
96
+ primary = f"/agents/flow/renders/{render_id}{suffix}" if self._supports_subscription_family("flows") else f"/api/renders/{render_id}{suffix}"
97
97
  try:
98
98
  return self._http.get(primary)
99
99
  except APIError as exc:
@@ -102,7 +102,7 @@ class Renders:
102
102
  raise
103
103
 
104
104
  def _delete_render(self, render_id: str) -> dict:
105
- primary = f"/agents/flow/renders/{render_id}" if self._supports_subscription_family("renders") else f"/api/renders/{render_id}"
105
+ primary = f"/agents/flow/renders/{render_id}" if self._supports_subscription_family("flows") else f"/api/renders/{render_id}"
106
106
  try:
107
107
  return self._http.delete(primary)
108
108
  except APIError as exc:
@@ -68,5 +68,5 @@ class UserAPI:
68
68
 
69
69
  def auth_me(self) -> AuthMe:
70
70
  """Resolve the current auth context, including key capabilities."""
71
- data = self._auth_http.get("/auth/me")
71
+ data = self._auth_http.get("/api/auth/me")
72
72
  return AuthMe.from_dict(data)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hypercli-sdk"
7
- version = "2026.4.5"
7
+ version = "2026.4.6"
8
8
  description = "Python SDK for HyperCLI - GPU orchestration and HyperAgent API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -740,6 +740,40 @@ def test_agents_start_stop_delete(agents_client):
740
740
  assert agents_client.delete("agent-123") == {"status": "deleted"}
741
741
 
742
742
 
743
+ def test_agents_start_preserves_generic_launch_fields(agents_client):
744
+ with patch("httpx.Client") as mock_client_class, patch("hypercli.agents.secrets.token_hex", return_value="gw-token-generic"):
745
+ mock_client = MagicMock()
746
+ mock_response = Mock()
747
+ mock_response.status_code = 200
748
+ mock_response.json.return_value = {
749
+ "id": "agent-456",
750
+ "user_id": "user-456",
751
+ "pod_id": "pod-456",
752
+ "pod_name": "generic-pod",
753
+ "state": "starting",
754
+ "hostname": "generic.hypercli.com",
755
+ }
756
+ mock_client.post.return_value = mock_response
757
+ mock_client.__enter__.return_value = mock_client
758
+ mock_client.__exit__.return_value = False
759
+ mock_client_class.return_value = mock_client
760
+
761
+ agent = agents_client.start(
762
+ "agent-456",
763
+ image="python:3.12-alpine",
764
+ command=["sh", "-c", "python -m http.server 80"],
765
+ routes={"web": {"port": 80, "auth": False, "prefix": ""}},
766
+ sync_enabled=True,
767
+ )
768
+
769
+ assert isinstance(agent, Agent)
770
+ posted_json = mock_client.post.call_args[1]["json"]
771
+ assert posted_json["image"] == "python:3.12-alpine"
772
+ assert posted_json["command"] == ["sh", "-c", "python -m http.server 80"]
773
+ assert posted_json["routes"] == {"web": {"port": 80, "auth": False, "prefix": ""}}
774
+ assert posted_json["sync_enabled"] is True
775
+
776
+
743
777
  def test_build_agent_launch_rejects_nested_launch_fields():
744
778
  with pytest.raises(ValueError, match="Launch settings must be top-level fields"):
745
779
  _build_agent_launch(
@@ -109,11 +109,7 @@ def test_keys_disable():
109
109
  def test_user_auth_me_returns_capabilities():
110
110
  class DummyUserHTTP:
111
111
  def get(self, path):
112
- raise AssertionError(f"unexpected product path {path}")
113
-
114
- class DummyAuthHTTP:
115
- def get(self, path):
116
- assert path == "/auth/me"
112
+ assert path == "/api/auth/me"
117
113
  return {
118
114
  "user_id": "user-123",
119
115
  "orchestra_user_id": "orch-123",
@@ -127,7 +123,7 @@ def test_user_auth_me_returns_capabilities():
127
123
  "key_name": "runtime-key",
128
124
  }
129
125
 
130
- auth_me = UserAPI(DummyUserHTTP(), auth_http=DummyAuthHTTP()).auth_me()
126
+ auth_me = UserAPI(DummyUserHTTP()).auth_me()
131
127
 
132
128
  assert auth_me.user_id == "user-123"
133
129
  assert auth_me.capabilities == ["models:*", "voice:*"]
@@ -9,7 +9,7 @@ class DummyHTTP:
9
9
 
10
10
  def get(self, path, params=None):
11
11
  self.calls.append(("get", path, params))
12
- if path == "/auth/me":
12
+ if path == "/api/auth/me":
13
13
  return self.auth_me
14
14
  if path.endswith("/status"):
15
15
  render_id = path.split("/")[-2]
@@ -36,15 +36,15 @@ def test_create_flow_uses_subscription_route_when_available():
36
36
  http = DummyHTTP()
37
37
  http.auth_me = {
38
38
  "auth_type": "orchestra_key",
39
- "capabilities": ["renders:*"],
39
+ "capabilities": ["flows:*"],
40
40
  "has_active_subscription": True,
41
41
  }
42
- renders = Renders(http, auth_http=http)
42
+ renders = Renders(http)
43
43
 
44
44
  render = renders.create_flow("text-to-image", prompt="hello")
45
45
 
46
46
  assert render.render_id == "render-123"
47
- assert http.calls[0] == ("get", "/auth/me", None)
47
+ assert http.calls[0] == ("get", "/api/auth/me", None)
48
48
  assert http.calls[1] == ("post", "/agents/flow/text-to-image", {"prompt": "hello"})
49
49
 
50
50
 
@@ -52,11 +52,11 @@ def test_create_flow_falls_back_to_paid_route_on_subscription_rejection():
52
52
  http = DummyHTTP()
53
53
  http.auth_me = {
54
54
  "auth_type": "orchestra_key",
55
- "capabilities": ["renders:*"],
55
+ "capabilities": ["flows:*"],
56
56
  "has_active_subscription": True,
57
57
  }
58
58
  http.fail_once["/agents/flow/text-to-image"] = APIError(403, "Active paid subscription required")
59
- renders = Renders(http, auth_http=http)
59
+ renders = Renders(http)
60
60
 
61
61
  render = renders.create_flow("text-to-image", prompt="hello")
62
62
 
@@ -72,7 +72,7 @@ def test_get_status_and_cancel_prefer_subscription_render_routes():
72
72
  "capabilities": [],
73
73
  "has_active_subscription": True,
74
74
  }
75
- renders = Renders(http, auth_http=http)
75
+ renders = Renders(http)
76
76
 
77
77
  render = renders.get("render-123")
78
78
  status = renders.status("render-123")