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.
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/PKG-INFO +1 -1
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/__init__.py +1 -1
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/client.py +2 -3
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/renders.py +4 -4
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/user.py +1 -1
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/pyproject.toml +1 -1
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_agents.py +34 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_keys.py +2 -6
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_renders_subscription.py +7 -7
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/.gitignore +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/README.md +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/agent.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/agents.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/billing.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/config.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/files.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/gateway.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/http.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/instances.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/job/__init__.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/job/base.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/job/comfyui.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/job/gradio.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/jobs.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/keys.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/logs.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/shell.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/voice.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/hypercli/x402.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/conftest.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_agents.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_auth.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_billing.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_instances.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_jobs_dryrun.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_keys.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/integration/test_renders.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_apply_params.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_claw.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_config.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_exec_shell_dryrun.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_gateway.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_gateway_retry.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_graph_to_api.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_http.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_jobs.py +0 -0
- {hypercli_sdk-2026.4.5 → hypercli_sdk-2026.4.6}/tests/test_voice.py +0 -0
|
@@ -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
|
|
97
|
+
self.user = UserAPI(self._http)
|
|
99
98
|
self.instances = Instances(self._http)
|
|
100
|
-
self.renders = Renders(self._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("
|
|
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("
|
|
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("
|
|
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:
|
|
@@ -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
|
-
|
|
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()
|
|
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": ["
|
|
39
|
+
"capabilities": ["flows:*"],
|
|
40
40
|
"has_active_subscription": True,
|
|
41
41
|
}
|
|
42
|
-
renders = Renders(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": ["
|
|
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
|
|
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
|
|
75
|
+
renders = Renders(http)
|
|
76
76
|
|
|
77
77
|
render = renders.get("render-123")
|
|
78
78
|
status = renders.status("render-123")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|