hypercli-sdk 2026.4.17__tar.gz → 2026.4.18__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 (53) hide show
  1. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/PKG-INFO +1 -1
  2. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/__init__.py +1 -1
  3. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/agent.py +33 -109
  4. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/pyproject.toml +1 -1
  5. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_claw.py +2 -256
  6. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/.gitignore +0 -0
  7. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/README.md +0 -0
  8. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/agents.py +0 -0
  9. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/billing.py +0 -0
  10. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/client.py +0 -0
  11. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/config.py +0 -0
  12. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/files.py +0 -0
  13. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/gateway.py +0 -0
  14. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/http.py +0 -0
  15. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/instances.py +0 -0
  16. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/job/__init__.py +0 -0
  17. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/job/base.py +0 -0
  18. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/job/comfyui.py +0 -0
  19. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/job/gradio.py +0 -0
  20. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/jobs.py +0 -0
  21. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/keys.py +0 -0
  22. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/logs.py +0 -0
  23. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/models.py +0 -0
  24. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/openclaw/__init__.py +0 -0
  25. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/openclaw/gateway.py +0 -0
  26. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/renders.py +0 -0
  27. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/shell.py +0 -0
  28. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/user.py +0 -0
  29. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/voice.py +0 -0
  30. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/hypercli/x402.py +0 -0
  31. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/integration/conftest.py +0 -0
  32. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/integration/test_agents.py +0 -0
  33. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/integration/test_auth.py +0 -0
  34. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/integration/test_billing.py +0 -0
  35. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/integration/test_instances.py +0 -0
  36. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/integration/test_jobs_dryrun.py +0 -0
  37. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/integration/test_keys.py +0 -0
  38. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/integration/test_renders.py +0 -0
  39. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_agents.py +0 -0
  40. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_apply_params.py +0 -0
  41. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_bootstrap_console_test_key.py +0 -0
  42. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_bootstrap_dev_test_keys.py +0 -0
  43. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_config.py +0 -0
  44. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_exec_shell_dryrun.py +0 -0
  45. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_gateway.py +0 -0
  46. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_gateway_retry.py +0 -0
  47. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_graph_to_api.py +0 -0
  48. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_http.py +0 -0
  49. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_jobs.py +0 -0
  50. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_keys.py +0 -0
  51. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_models.py +0 -0
  52. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/tests/test_renders_subscription.py +0 -0
  53. {hypercli_sdk-2026.4.17 → hypercli_sdk-2026.4.18}/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.17
3
+ Version: 2026.4.18
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
@@ -78,7 +78,7 @@ from .gateway import (
78
78
  extract_gateway_chat_tool_calls,
79
79
  normalize_gateway_chat_message,
80
80
  )
81
- __version__ = "2026.4.17"
81
+ __version__ = "2026.4.18"
82
82
  __all__ = [
83
83
  "HyperCLI",
84
84
  "configure",
@@ -221,6 +221,36 @@ class HyperAgentEntitlements:
221
221
  )
222
222
 
223
223
 
224
+ @dataclass
225
+ class HyperAgentEntitlements:
226
+ """Effective account entitlements computed by the backend."""
227
+
228
+ effective_plan_id: str
229
+ pooled_tpm_limit: int
230
+ pooled_rpm_limit: int
231
+ pooled_tpd: int
232
+ slot_inventory: dict[str, Any]
233
+ active_entitlement_count: int
234
+
235
+ @classmethod
236
+ def from_dict(cls, data: dict) -> "HyperAgentEntitlements":
237
+ payload = data.get("entitlements") if isinstance(data.get("entitlements"), dict) else data
238
+ return cls(
239
+ effective_plan_id=payload.get("effective_plan_id", data.get("effective_plan_id", "")),
240
+ pooled_tpm_limit=int(payload.get("pooled_tpm_limit", data.get("pooled_tpm_limit", 0)) or 0),
241
+ pooled_rpm_limit=int(payload.get("pooled_rpm_limit", data.get("pooled_rpm_limit", 0)) or 0),
242
+ pooled_tpd=int(payload.get("pooled_tpd", data.get("pooled_tpd", 0)) or 0),
243
+ slot_inventory=payload.get("slot_inventory") or data.get("slot_inventory") or {},
244
+ active_entitlement_count=int(
245
+ payload.get(
246
+ "active_entitlement_count",
247
+ data.get("active_entitlement_count", data.get("active_subscription_count", 0)),
248
+ )
249
+ or 0
250
+ ),
251
+ )
252
+
253
+
224
254
  @dataclass
225
255
  class HyperAgentSubscriptionSummary:
226
256
  """Effective entitlement summary for an authenticated HyperClaw user."""
@@ -236,7 +266,6 @@ class HyperAgentSubscriptionSummary:
236
266
  active_subscription_count: int
237
267
  active_entitlement_count: int
238
268
  entitlements: HyperAgentEntitlements
239
- entitlement_items: list[HyperAgentEntitlement]
240
269
  active_subscriptions: list[HyperAgentSubscription]
241
270
  subscriptions: list[HyperAgentSubscription]
242
271
  user: dict[str, Any]
@@ -257,7 +286,6 @@ class HyperAgentSubscriptionSummary:
257
286
  active_subscription_count=int(data.get("active_subscription_count", 0) or 0),
258
287
  active_entitlement_count=int(data.get("active_entitlement_count", data.get("active_subscription_count", 0)) or 0),
259
288
  entitlements=HyperAgentEntitlements.from_dict(data),
260
- entitlement_items=[HyperAgentEntitlement.from_dict(item) for item in data.get("entitlement_items", [])],
261
289
  active_subscriptions=[HyperAgentSubscription.from_dict(item) for item in data.get("active_subscriptions", [])],
262
290
  subscriptions=[HyperAgentSubscription.from_dict(item) for item in data.get("subscriptions", [])],
263
291
  user=data.get("user") or {},
@@ -267,21 +295,6 @@ class HyperAgentSubscriptionSummary:
267
295
  HyperAgentEntitlementsSummary = HyperAgentSubscriptionSummary
268
296
 
269
297
 
270
- @dataclass
271
- class HyperAgentSubscriptionMutationResult:
272
- ok: bool
273
- message: str
274
- subscription: HyperAgentSubscription | None = None
275
-
276
- @classmethod
277
- def from_dict(cls, data: dict) -> "HyperAgentSubscriptionMutationResult":
278
- return cls(
279
- ok=bool(data.get("ok", False)),
280
- message=str(data.get("message") or ""),
281
- subscription=HyperAgentSubscription.from_dict(data["subscription"]) if data.get("subscription") else None,
282
- )
283
-
284
-
285
298
  @dataclass
286
299
  class HyperAgentModel:
287
300
  """Available model on HyperAgent."""
@@ -888,102 +901,13 @@ class HyperAgent:
888
901
  response.raise_for_status()
889
902
  return HyperAgentEntitlementsSummary.from_dict(response.json())
890
903
 
891
- def entitlement_instances(self) -> list[HyperAgentEntitlement]:
892
- response = self._http._session.get(
893
- f"{self._control_base_url}/entitlements/instances",
894
- headers={"Authorization": f"Bearer {self._api_key}"},
895
- )
896
- response.raise_for_status()
897
- data = response.json()
898
- return [HyperAgentEntitlement.from_dict(item) for item in data.get("items", [])]
899
-
900
- def update_subscription(self, subscription_id: str, bundle: dict[str, int] | None) -> HyperAgentSubscriptionMutationResult:
904
+ def cancel_subscription(self, subscription_id: str) -> Dict[str, Any]:
901
905
  response = self._http._session.post(
902
- f"{self._control_base_url}/subscriptions/{subscription_id}/update",
906
+ f"{self._control_base_url}/subscriptions/{subscription_id}/cancel",
903
907
  headers={"Authorization": f"Bearer {self._api_key}"},
904
- json={"bundle": dict(bundle or {})},
905
908
  )
906
909
  response.raise_for_status()
907
- return HyperAgentSubscriptionMutationResult.from_dict(response.json())
908
-
909
- def cancel_subscription(self, subscription_id: str) -> HyperAgentSubscriptionMutationResult:
910
- return self.update_subscription(subscription_id, {})
911
-
912
- def usage_summary(self) -> HyperAgentUsageSummary:
913
- return HyperAgentUsageSummary.from_dict(self._control_get("/usage"))
914
-
915
- def usage_history(self, days: int = 7) -> HyperAgentUsageHistory:
916
- return HyperAgentUsageHistory.from_dict(self._control_get("/usage/history", params={"days": days}))
917
-
918
- def key_usage(self, days: int = 7) -> HyperAgentKeyUsage:
919
- return HyperAgentKeyUsage.from_dict(self._control_get("/usage/keys", params={"days": days}))
920
-
921
- def agent_types(self) -> HyperAgentTypeCatalog:
922
- return HyperAgentTypeCatalog.from_dict(self._control_get("/types"))
923
-
924
- def billing_info(self) -> HyperAgentBillingInfo:
925
- return HyperAgentBillingInfo.from_dict(self._control_get("/billing/info").get("company_billing", {}))
926
-
927
- def billing_profile(self) -> HyperAgentBillingProfileResponse:
928
- return HyperAgentBillingProfileResponse.from_dict(self._control_get("/billing/profile"))
929
-
930
- def update_billing_profile(self, profile: HyperAgentBillingProfileFields) -> HyperAgentBillingProfileResponse:
931
- return HyperAgentBillingProfileResponse.from_dict(
932
- self._control_put("/billing/profile", payload=profile.to_dict())
933
- )
934
-
935
- def payments(
936
- self,
937
- *,
938
- limit: int | None = None,
939
- provider: str | None = None,
940
- status: str | None = None,
941
- ) -> HyperAgentPaymentsResponse:
942
- params: dict[str, Any] = {}
943
- if limit is not None:
944
- params["limit"] = limit
945
- if provider:
946
- params["provider"] = provider
947
- if status:
948
- params["status"] = status
949
- return HyperAgentPaymentsResponse.from_dict(self._control_get("/billing/payments", params=params or None))
950
-
951
- def payment(self, payment_id: str) -> HyperAgentPayment:
952
- return HyperAgentPayment.from_dict(self._control_get(f"/billing/payments/{payment_id}"))
953
-
954
- def create_stripe_checkout(
955
- self,
956
- *,
957
- bundle: dict[str, int] | None = None,
958
- quantity: int | None = None,
959
- success_url: str | None = None,
960
- cancel_url: str | None = None,
961
- plan_id: str | None = None,
962
- ) -> HyperAgentStripeCheckoutResponse:
963
- payload: dict[str, Any] = {}
964
- if bundle is not None:
965
- payload["bundle"] = dict(bundle)
966
- if quantity is not None:
967
- payload["quantity"] = quantity
968
- if success_url is not None:
969
- payload["success_url"] = success_url
970
- if cancel_url is not None:
971
- payload["cancel_url"] = cancel_url
972
- path = f"/stripe/{plan_id}" if plan_id else "/stripe/checkout"
973
- return HyperAgentStripeCheckoutResponse.from_dict(self._control_post(path, payload=payload))
974
-
975
- def create_x402_checkout(
976
- self,
977
- *,
978
- bundle: dict[str, int] | None = None,
979
- quantity: int | None = None,
980
- ) -> HyperAgentX402CheckoutResponse:
981
- payload: dict[str, Any] = {}
982
- if bundle is not None:
983
- payload["bundle"] = dict(bundle)
984
- if quantity is not None:
985
- payload["quantity"] = quantity
986
- return HyperAgentX402CheckoutResponse.from_dict(self._control_post("/x402/checkout", payload=payload))
910
+ return response.json()
987
911
 
988
912
  def discovery_health(self) -> Dict[str, Any]:
989
913
  response = self._http._session.get(f"{self._api_base_without_v1()}/discovery/health")
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hypercli-sdk"
7
- version = "2026.4.17"
7
+ version = "2026.4.18"
8
8
  description = "Python SDK for HyperCLI - GPU orchestration and HyperAgent API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -7,7 +7,6 @@ from unittest.mock import Mock, patch, MagicMock
7
7
  from hypercli import HyperCLI
8
8
  from hypercli.agent import (
9
9
  HyperAgent,
10
- HyperAgentEntitlement,
11
10
  HyperAgentEntitlements,
12
11
  HyperAgentEntitlementsSummary,
13
12
  HyperAgentPlan,
@@ -97,27 +96,9 @@ class TestHyperAgentDataclasses:
97
96
  "pooled_tpm_limit": 2000,
98
97
  "pooled_rpm_limit": 20,
99
98
  "pooled_tpd": 2000000,
100
- "billing_reset_at": "2026-04-15T00:00:00Z",
101
99
  "slot_inventory": {"large": {"granted": 2, "used": 1, "available": 1}},
102
100
  "active_entitlement_count": 1,
103
101
  },
104
- "entitlement_items": [
105
- {
106
- "id": "ent-1",
107
- "user_id": "user-1",
108
- "subscription_id": "sub-1",
109
- "plan_id": "large",
110
- "plan_name": "Large",
111
- "provider": "STRIPE",
112
- "status": "ACTIVE",
113
- "expires_at": "2026-04-15T00:00:00Z",
114
- "agent_tier": "large",
115
- "features": {"voice": True},
116
- "tags": ["customer=acme"],
117
- "active_agent_count": 1,
118
- "active_agent_ids": ["agent-1"],
119
- }
120
- ],
121
102
  "active_subscriptions": [
122
103
  {
123
104
  "id": "sub-1",
@@ -137,8 +118,6 @@ class TestHyperAgentDataclasses:
137
118
  assert summary.active_subscription_count == 1
138
119
  assert isinstance(summary.entitlements, HyperAgentEntitlements)
139
120
  assert summary.entitlements.active_entitlement_count == 1
140
- assert summary.billing_reset_at is not None
141
- assert summary.entitlements.billing_reset_at is not None
142
121
  assert summary.active_subscriptions[0].plan_id == "large"
143
122
  assert isinstance(summary.entitlement_items[0], HyperAgentEntitlement)
144
123
  assert summary.entitlement_items[0].tags == ["customer=acme"]
@@ -279,7 +258,6 @@ class TestHyperAgentClient:
279
258
  "pooled_tpm_limit": 2000,
280
259
  "pooled_rpm_limit": 20,
281
260
  "pooled_tpd": 2000000,
282
- "billing_reset_at": "2026-04-15T00:00:00Z",
283
261
  "slot_inventory": {"large": {"granted": 2, "used": 1, "available": 1}},
284
262
  "active_subscription_count": 1,
285
263
  "active_entitlement_count": 1,
@@ -288,26 +266,9 @@ class TestHyperAgentClient:
288
266
  "pooled_tpm_limit": 2000,
289
267
  "pooled_rpm_limit": 20,
290
268
  "pooled_tpd": 2000000,
291
- "billing_reset_at": "2026-04-15T00:00:00Z",
292
269
  "slot_inventory": {"large": {"granted": 2, "used": 1, "available": 1}},
293
270
  "active_entitlement_count": 1,
294
271
  },
295
- "entitlement_items": [
296
- {
297
- "id": "ent-1",
298
- "user_id": "user-1",
299
- "plan_id": "large",
300
- "plan_name": "Large",
301
- "provider": "X402",
302
- "status": "ACTIVE",
303
- "expires_at": "2026-04-20T00:00:00Z",
304
- "agent_tier": "large",
305
- "features": {"voice": True},
306
- "tags": ["customer=acme"],
307
- "active_agent_count": 0,
308
- "active_agent_ids": [],
309
- }
310
- ],
311
272
  "active_subscriptions": [],
312
273
  "subscriptions": [],
313
274
  "user": {"id": "user-1", "team_id": "team-1"},
@@ -318,242 +279,27 @@ class TestHyperAgentClient:
318
279
  summary = agent.entitlements()
319
280
 
320
281
  assert isinstance(summary, HyperAgentEntitlementsSummary)
321
- assert summary.billing_reset_at is not None
322
282
  assert summary.entitlements.slot_inventory["large"]["available"] == 1
323
- assert summary.entitlement_items[0].provider == "X402"
324
283
  mock_http._session.get.assert_called_with(
325
284
  "https://api.hypercli.com/agents/entitlements",
326
285
  headers={"Authorization": "Bearer sk-hyper-test"},
327
286
  )
328
287
 
329
- def test_entitlement_instances(self, mock_http):
330
- mock_http._session.get.return_value.json.return_value = {
331
- "items": [
332
- {
333
- "id": "ent-1",
334
- "user_id": "user-1",
335
- "subscription_id": None,
336
- "plan_id": "large",
337
- "plan_name": "Large",
338
- "provider": "X402",
339
- "status": "ACTIVE",
340
- "expires_at": "2026-04-20T00:00:00Z",
341
- "agent_tier": "large",
342
- "features": {"voice": True},
343
- "tags": ["customer=acme"],
344
- "active_agent_count": 0,
345
- "active_agent_ids": [],
346
- }
347
- ]
348
- }
349
- mock_http._session.get.return_value.raise_for_status = Mock()
350
-
351
- agent = HyperAgent(mock_http, agent_api_key="sk-hyper-test", agents_api_base_url="https://api.hypercli.com/agents")
352
- entitlements = agent.entitlement_instances()
353
-
354
- assert len(entitlements) == 1
355
- assert entitlements[0].plan_id == "large"
356
- assert entitlements[0].tags == ["customer=acme"]
357
- mock_http._session.get.assert_called_with(
358
- "https://api.hypercli.com/agents/entitlements/instances",
359
- headers={"Authorization": "Bearer sk-hyper-test"},
360
- )
361
-
362
288
  def test_cancel_subscription(self, mock_http):
363
289
  mock_http._session.post.return_value.json.return_value = {
364
290
  "ok": True,
365
291
  "message": "Subscription will be cancelled at the end of the current billing period",
366
- "subscription": {
367
- "id": "sub-1",
368
- "user_id": "user-1",
369
- "plan_id": "large",
370
- "plan_name": "Large",
371
- "provider": "STRIPE",
372
- "status": "ACTIVE",
373
- "cancel_at_period_end": True,
374
- "can_cancel": True,
375
- },
376
292
  }
377
293
  mock_http._session.post.return_value.raise_for_status = Mock()
378
294
 
379
295
  agent = HyperAgent(mock_http, agent_api_key="sk-hyper-test", agents_api_base_url="https://api.hypercli.com/agents")
380
296
  result = agent.cancel_subscription("sub-1")
381
297
 
382
- assert result.ok is True
383
- assert result.subscription is not None
384
- assert result.subscription.cancel_at_period_end is True
385
- mock_http._session.post.assert_called_with(
386
- "https://api.hypercli.com/agents/subscriptions/sub-1/update",
387
- headers={"Authorization": "Bearer sk-hyper-test"},
388
- json={"bundle": {}},
389
- )
390
-
391
- def test_update_subscription(self, mock_http):
392
- mock_http._session.post.return_value.json.return_value = {
393
- "ok": True,
394
- "message": "Subscription upgraded immediately",
395
- "subscription": {
396
- "id": "sub-1",
397
- "user_id": "user-1",
398
- "plan_id": "large",
399
- "plan_name": "Large",
400
- "provider": "STRIPE",
401
- "status": "ACTIVE",
402
- "cancel_at_period_end": False,
403
- "can_cancel": True,
404
- },
405
- }
406
- mock_http._session.post.return_value.raise_for_status = Mock()
407
-
408
- agent = HyperAgent(mock_http, agent_api_key="sk-hyper-test", agents_api_base_url="https://api.hypercli.com/agents")
409
- result = agent.update_subscription("sub-1", {"large": 1})
410
-
411
- assert result.ok is True
412
- assert result.subscription is not None
413
- assert result.subscription.plan_id == "large"
298
+ assert result["ok"] is True
414
299
  mock_http._session.post.assert_called_with(
415
- "https://api.hypercli.com/agents/subscriptions/sub-1/update",
300
+ "https://api.hypercli.com/agents/subscriptions/sub-1/cancel",
416
301
  headers={"Authorization": "Bearer sk-hyper-test"},
417
- json={"bundle": {"large": 1}},
418
302
  )
419
-
420
- def test_usage_endpoints(self, mock_http):
421
- mock_http._session.get.side_effect = [
422
- Mock(json=Mock(return_value={
423
- "total_tokens": 100,
424
- "prompt_tokens": 60,
425
- "completion_tokens": 40,
426
- "request_count": 5,
427
- "active_keys": 2,
428
- "current_tpm": 1000,
429
- "current_rpm": 10,
430
- "period": "30d",
431
- }), raise_for_status=Mock()),
432
- Mock(json=Mock(return_value={
433
- "history": [{"date": "2026-04-13", "total_tokens": 100, "prompt_tokens": 60, "completion_tokens": 40, "requests": 5}],
434
- "days": 7,
435
- }), raise_for_status=Mock()),
436
- Mock(json=Mock(return_value={
437
- "keys": [{"key_hash": "key-1", "name": "Primary", "total_tokens": 100, "prompt_tokens": 60, "completion_tokens": 40, "requests": 5}],
438
- "days": 7,
439
- }), raise_for_status=Mock()),
440
- ]
441
-
442
- agent = HyperAgent(mock_http, agent_api_key="sk-hyper-test", agents_api_base_url="https://api.hypercli.com/agents")
443
- summary = agent.usage_summary()
444
- history = agent.usage_history()
445
- keys = agent.key_usage()
446
-
447
- assert isinstance(summary, HyperAgentUsageSummary)
448
- assert summary.total_tokens == 100
449
- assert isinstance(history, HyperAgentUsageHistory)
450
- assert history.history[0].date == "2026-04-13"
451
- assert isinstance(keys, HyperAgentKeyUsage)
452
- assert keys.keys[0].key_hash == "key-1"
453
-
454
- def test_types_and_billing_endpoints(self, mock_http):
455
- mock_http._session.get.side_effect = [
456
- Mock(json=Mock(return_value={
457
- "types": [{"id": "medium", "name": "Medium", "cpu": 1, "memory": 2, "cpu_limit": 1, "memory_limit": 2}],
458
- "plans": [{"id": "2aiu", "name": "2 AIU", "price": 20, "agents": 1, "agent_type": "medium", "highlighted": True}],
459
- }), raise_for_status=Mock()),
460
- Mock(json=Mock(return_value={
461
- "company_billing": {"address": ["HyperCLI"], "email": "support@hypercli.com"},
462
- "profile": None,
463
- }), raise_for_status=Mock()),
464
- Mock(json=Mock(return_value={
465
- "company_billing": {"address": ["HyperCLI"], "email": "support@hypercli.com"},
466
- "profile": {"billing_name": "Test User"},
467
- }), raise_for_status=Mock()),
468
- ]
469
- mock_http._session.put.return_value.json.return_value = {
470
- "company_billing": {"address": ["HyperCLI"], "email": "support@hypercli.com"},
471
- "profile": {"billing_name": "Test User"},
472
- "synced_stripe_customer_ids": ["cus_123"],
473
- }
474
- mock_http._session.put.return_value.raise_for_status = Mock()
475
-
476
- agent = HyperAgent(mock_http, agent_api_key="sk-hyper-test", agents_api_base_url="https://api.hypercli.com/agents")
477
- catalog = agent.agent_types()
478
- info = agent.billing_info()
479
- profile = agent.billing_profile()
480
- updated = agent.update_billing_profile(HyperAgentBillingProfileFields(billing_name="Test User"))
481
-
482
- assert isinstance(catalog, HyperAgentTypeCatalog)
483
- assert catalog.types[0].id == "medium"
484
- assert isinstance(info, HyperAgentBillingInfo)
485
- assert info.email == "support@hypercli.com"
486
- assert isinstance(profile, HyperAgentBillingProfileResponse)
487
- assert profile.profile is not None
488
- assert profile.profile.billing_name == "Test User"
489
- assert updated.synced_stripe_customer_ids == ["cus_123"]
490
-
491
- def test_payments_and_checkout_endpoints(self, mock_http):
492
- mock_http._session.get.side_effect = [
493
- Mock(json=Mock(return_value={
494
- "items": [{
495
- "id": "pay_123",
496
- "user_id": "user-1",
497
- "subscription_id": None,
498
- "entitlement_id": None,
499
- "provider": "STRIPE",
500
- "status": "SUCCEEDED",
501
- "amount": "2000",
502
- "currency": "usd",
503
- "external_payment_id": "pi_123",
504
- "created_at": "2026-04-13T00:00:00Z",
505
- "updated_at": "2026-04-13T00:00:00Z",
506
- "user": {"id": "user-1", "email": "user@example.com", "wallet_address": None, "team_id": "team-1", "plan_id": "2aiu"},
507
- "subscription": None,
508
- "entitlement": None,
509
- }]
510
- }), raise_for_status=Mock()),
511
- Mock(json=Mock(return_value={
512
- "id": "pay_123",
513
- "user_id": "user-1",
514
- "subscription_id": None,
515
- "entitlement_id": None,
516
- "provider": "STRIPE",
517
- "status": "SUCCEEDED",
518
- "amount": "2000",
519
- "currency": "usd",
520
- "external_payment_id": "pi_123",
521
- "created_at": "2026-04-13T00:00:00Z",
522
- "updated_at": "2026-04-13T00:00:00Z",
523
- "user": {"id": "user-1", "email": "user@example.com", "wallet_address": None, "team_id": "team-1", "plan_id": "2aiu"},
524
- "subscription": None,
525
- "entitlement": None,
526
- }), raise_for_status=Mock()),
527
- ]
528
- mock_http._session.post.side_effect = [
529
- Mock(json=Mock(return_value={"checkout_url": "https://checkout.stripe.test/session"}), raise_for_status=Mock()),
530
- Mock(json=Mock(return_value={
531
- "ok": True,
532
- "key": "sk-x402",
533
- "plan_id": "2aiu",
534
- "quantity": 1,
535
- "bundle": {"medium": 1},
536
- "amount_paid": "20.000000",
537
- "duration_days": 30,
538
- "expires_at": "2026-05-13T00:00:00Z",
539
- "tpm_limit": 1000,
540
- "rpm_limit": 10,
541
- }), raise_for_status=Mock()),
542
- ]
543
-
544
- agent = HyperAgent(mock_http, agent_api_key="sk-hyper-test", agents_api_base_url="https://api.hypercli.com/agents")
545
- payments = agent.payments(limit=10, provider="stripe", status="succeeded")
546
- payment = agent.payment("pay_123")
547
- stripe = agent.create_stripe_checkout(bundle={"medium": 1})
548
- x402 = agent.create_x402_checkout(bundle={"medium": 1})
549
-
550
- assert isinstance(payments, HyperAgentPaymentsResponse)
551
- assert payments.items[0].id == "pay_123"
552
- assert payment.external_payment_id == "pi_123"
553
- assert isinstance(stripe, HyperAgentStripeCheckoutResponse)
554
- assert stripe.checkout_url == "https://checkout.stripe.test/session"
555
- assert isinstance(x402, HyperAgentX402CheckoutResponse)
556
- assert x402.plan_id == "2aiu"
557
303
 
558
304
  def test_openai_client_creation(self, mock_http):
559
305
  """Test that OpenAI client is created with correct config."""