hypercli-sdk 2026.4.7__tar.gz → 2026.4.13__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.7 → hypercli_sdk-2026.4.13}/PKG-INFO +14 -1
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/README.md +13 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/__init__.py +8 -1
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/agent.py +148 -2
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/agents.py +56 -15
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/client.py +2 -0
- hypercli_sdk-2026.4.13/hypercli/gateway.py +7 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/http.py +2 -2
- hypercli_sdk-2026.4.13/hypercli/models.py +33 -0
- hypercli_sdk-2026.4.13/hypercli/openclaw/__init__.py +25 -0
- {hypercli_sdk-2026.4.7/hypercli → hypercli_sdk-2026.4.13/hypercli/openclaw}/gateway.py +7 -2
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/renders.py +67 -4
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/voice.py +8 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/pyproject.toml +1 -1
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/integration/test_agents.py +29 -31
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/integration/test_keys.py +13 -2
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_agents.py +77 -4
- hypercli_sdk-2026.4.13/tests/test_bootstrap_console_test_key.py +81 -0
- hypercli_sdk-2026.4.13/tests/test_bootstrap_dev_test_keys.py +104 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_claw.py +206 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_gateway.py +52 -2
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_gateway_retry.py +4 -2
- hypercli_sdk-2026.4.13/tests/test_models.py +27 -0
- hypercli_sdk-2026.4.13/tests/test_renders_subscription.py +188 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_voice.py +6 -3
- hypercli_sdk-2026.4.7/tests/test_renders_subscription.py +0 -86
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/.gitignore +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/billing.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/config.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/files.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/instances.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/job/__init__.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/job/base.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/job/comfyui.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/job/gradio.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/jobs.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/keys.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/logs.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/shell.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/user.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/hypercli/x402.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/integration/conftest.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/integration/test_auth.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/integration/test_billing.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/integration/test_instances.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/integration/test_jobs_dryrun.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/integration/test_renders.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_apply_params.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_config.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_exec_shell_dryrun.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_graph_to_api.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_http.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_jobs.py +0 -0
- {hypercli_sdk-2026.4.7 → hypercli_sdk-2026.4.13}/tests/test_keys.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hypercli-sdk
|
|
3
|
-
Version: 2026.4.
|
|
3
|
+
Version: 2026.4.13
|
|
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
|
|
@@ -132,6 +132,19 @@ response = client.chat.completions.create(
|
|
|
132
132
|
)
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
+
## OpenClaw Agents
|
|
136
|
+
|
|
137
|
+
OpenClaw uses the generic deployment launch surface. `registry_url`, `registry_auth`, `sync_root`, and `sync_enabled` are generic deployment options; the OpenClaw helpers only add defaults such as routes, image, and `sync_root=/home/node`.
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
agent = client.deployments.create_openclaw(
|
|
141
|
+
name="docs-demo",
|
|
142
|
+
start=True,
|
|
143
|
+
registry_url="git.nedos.co",
|
|
144
|
+
registry_auth={"username": "ci", "password": "token"},
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
135
148
|
## Error Handling
|
|
136
149
|
|
|
137
150
|
```python
|
|
@@ -101,6 +101,19 @@ response = client.chat.completions.create(
|
|
|
101
101
|
)
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
+
## OpenClaw Agents
|
|
105
|
+
|
|
106
|
+
OpenClaw uses the generic deployment launch surface. `registry_url`, `registry_auth`, `sync_root`, and `sync_enabled` are generic deployment options; the OpenClaw helpers only add defaults such as routes, image, and `sync_root=/home/node`.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
agent = client.deployments.create_openclaw(
|
|
110
|
+
name="docs-demo",
|
|
111
|
+
start=True,
|
|
112
|
+
registry_url="git.nedos.co",
|
|
113
|
+
registry_auth={"username": "ci", "password": "token"},
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
104
117
|
## Error Handling
|
|
105
118
|
|
|
106
119
|
```python
|
|
@@ -32,6 +32,7 @@ from .jobs import (
|
|
|
32
32
|
)
|
|
33
33
|
from .renders import Render, RenderStatus
|
|
34
34
|
from .voice import VoiceAPI
|
|
35
|
+
from .models import Model, ModelsAPI
|
|
35
36
|
from .x402 import X402Client, X402JobLaunch, X402FlowCreate, X402RenderCreate, FlowCatalogItem
|
|
36
37
|
from .files import File, AsyncFiles
|
|
37
38
|
from .job import BaseJob, ComfyUIJob, GradioJob, apply_params, apply_graph_modes, find_node, find_nodes, load_template, graph_to_api, expand_subgraphs, DEFAULT_OBJECT_INFO
|
|
@@ -42,6 +43,8 @@ from .agent import (
|
|
|
42
43
|
HyperAgent,
|
|
43
44
|
HyperAgentPlan,
|
|
44
45
|
HyperAgentCurrentPlan,
|
|
46
|
+
HyperAgentEntitlements,
|
|
47
|
+
HyperAgentEntitlementsSummary,
|
|
45
48
|
HyperAgentSubscription,
|
|
46
49
|
HyperAgentSubscriptionSummary,
|
|
47
50
|
HyperAgentModel,
|
|
@@ -57,7 +60,7 @@ from .gateway import (
|
|
|
57
60
|
extract_gateway_chat_tool_calls,
|
|
58
61
|
normalize_gateway_chat_message,
|
|
59
62
|
)
|
|
60
|
-
__version__ = "2026.4.
|
|
63
|
+
__version__ = "2026.4.13"
|
|
61
64
|
__all__ = [
|
|
62
65
|
"HyperCLI",
|
|
63
66
|
"configure",
|
|
@@ -93,6 +96,8 @@ __all__ = [
|
|
|
93
96
|
"Render",
|
|
94
97
|
"RenderStatus",
|
|
95
98
|
"VoiceAPI",
|
|
99
|
+
"Model",
|
|
100
|
+
"ModelsAPI",
|
|
96
101
|
# x402 API
|
|
97
102
|
"X402Client",
|
|
98
103
|
"X402JobLaunch",
|
|
@@ -138,6 +143,8 @@ __all__ = [
|
|
|
138
143
|
"HyperAgent",
|
|
139
144
|
"HyperAgentPlan",
|
|
140
145
|
"HyperAgentCurrentPlan",
|
|
146
|
+
"HyperAgentEntitlements",
|
|
147
|
+
"HyperAgentEntitlementsSummary",
|
|
141
148
|
"HyperAgentSubscription",
|
|
142
149
|
"HyperAgentSubscriptionSummary",
|
|
143
150
|
"HyperAgentModel",
|
|
@@ -85,7 +85,7 @@ class HyperAgentCurrentPlan:
|
|
|
85
85
|
|
|
86
86
|
@dataclass
|
|
87
87
|
class HyperAgentSubscription:
|
|
88
|
-
"""A
|
|
88
|
+
"""A recurring HyperClaw billing subscription."""
|
|
89
89
|
|
|
90
90
|
id: str
|
|
91
91
|
user_id: str
|
|
@@ -106,10 +106,11 @@ class HyperAgentSubscription:
|
|
|
106
106
|
plan_tpd: int = 0
|
|
107
107
|
plan_agent_tier: str | None = None
|
|
108
108
|
slot_grants: dict[str, int] | None = None
|
|
109
|
+
entitlements: list["HyperAgentEntitlement"] | None = None
|
|
109
110
|
|
|
110
111
|
@classmethod
|
|
111
112
|
def from_dict(cls, data: dict) -> "HyperAgentSubscription":
|
|
112
|
-
expires_at = data.get("expires_at")
|
|
113
|
+
expires_at = data.get("current_period_end", data.get("expires_at"))
|
|
113
114
|
updated_at = data.get("updated_at")
|
|
114
115
|
return cls(
|
|
115
116
|
id=data["id"],
|
|
@@ -131,6 +132,92 @@ class HyperAgentSubscription:
|
|
|
131
132
|
plan_tpd=int(data.get("plan_tpd", 0) or 0),
|
|
132
133
|
plan_agent_tier=data.get("plan_agent_tier"),
|
|
133
134
|
slot_grants=data.get("slot_grants") or None,
|
|
135
|
+
entitlements=[HyperAgentEntitlement.from_dict(item) for item in data.get("entitlements", [])] or None,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass
|
|
140
|
+
class HyperAgentEntitlement:
|
|
141
|
+
"""A concrete 1:1 entitlement grant."""
|
|
142
|
+
|
|
143
|
+
id: str
|
|
144
|
+
user_id: str
|
|
145
|
+
subscription_id: str | None
|
|
146
|
+
plan_id: str
|
|
147
|
+
plan_name: str
|
|
148
|
+
provider: str
|
|
149
|
+
status: str
|
|
150
|
+
expires_at: datetime | None = None
|
|
151
|
+
updated_at: datetime | None = None
|
|
152
|
+
tpm_limit: int = 0
|
|
153
|
+
rpm_limit: int = 0
|
|
154
|
+
tpd_limit: int = 0
|
|
155
|
+
agent_tier: str | None = None
|
|
156
|
+
features: dict[str, bool] | None = None
|
|
157
|
+
tags: list[str] | None = None
|
|
158
|
+
meta: dict[str, Any] | None = None
|
|
159
|
+
slot_grants: dict[str, int] | None = None
|
|
160
|
+
active_agent_count: int = 0
|
|
161
|
+
active_agent_ids: list[str] | None = None
|
|
162
|
+
|
|
163
|
+
@classmethod
|
|
164
|
+
def from_dict(cls, data: dict) -> "HyperAgentEntitlement":
|
|
165
|
+
expires_at = data.get("expires_at")
|
|
166
|
+
updated_at = data.get("updated_at")
|
|
167
|
+
return cls(
|
|
168
|
+
id=data["id"],
|
|
169
|
+
user_id=data.get("user_id", ""),
|
|
170
|
+
subscription_id=data.get("subscription_id"),
|
|
171
|
+
plan_id=data.get("plan_id", ""),
|
|
172
|
+
plan_name=data.get("plan_name", data.get("plan_id", "")),
|
|
173
|
+
provider=data.get("provider", ""),
|
|
174
|
+
status=data.get("status", ""),
|
|
175
|
+
expires_at=datetime.fromisoformat(str(expires_at).replace("Z", "+00:00")) if expires_at else None,
|
|
176
|
+
updated_at=datetime.fromisoformat(str(updated_at).replace("Z", "+00:00")) if updated_at else None,
|
|
177
|
+
tpm_limit=int(data.get("tpm_limit", 0) or 0),
|
|
178
|
+
rpm_limit=int(data.get("rpm_limit", 0) or 0),
|
|
179
|
+
tpd_limit=int(data.get("tpd_limit", 0) or 0),
|
|
180
|
+
agent_tier=data.get("agent_tier"),
|
|
181
|
+
features=data.get("features") or {},
|
|
182
|
+
tags=data.get("tags") or [],
|
|
183
|
+
meta=data.get("meta") or None,
|
|
184
|
+
slot_grants=data.get("slot_grants") or None,
|
|
185
|
+
active_agent_count=int(data.get("active_agent_count", 0) or 0),
|
|
186
|
+
active_agent_ids=data.get("active_agent_ids") or [],
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@dataclass
|
|
191
|
+
class HyperAgentEntitlements:
|
|
192
|
+
"""Effective account entitlements computed by the backend."""
|
|
193
|
+
|
|
194
|
+
effective_plan_id: str
|
|
195
|
+
pooled_tpm_limit: int
|
|
196
|
+
pooled_rpm_limit: int
|
|
197
|
+
pooled_tpd: int
|
|
198
|
+
slot_inventory: dict[str, Any]
|
|
199
|
+
active_entitlement_count: int
|
|
200
|
+
billing_reset_at: datetime | None = None
|
|
201
|
+
|
|
202
|
+
@classmethod
|
|
203
|
+
def from_dict(cls, data: dict) -> "HyperAgentEntitlements":
|
|
204
|
+
payload = data.get("entitlements") if isinstance(data.get("entitlements"), dict) else data
|
|
205
|
+
return cls(
|
|
206
|
+
effective_plan_id=payload.get("effective_plan_id", data.get("effective_plan_id", "")),
|
|
207
|
+
pooled_tpm_limit=int(payload.get("pooled_tpm_limit", data.get("pooled_tpm_limit", 0)) or 0),
|
|
208
|
+
pooled_rpm_limit=int(payload.get("pooled_rpm_limit", data.get("pooled_rpm_limit", 0)) or 0),
|
|
209
|
+
pooled_tpd=int(payload.get("pooled_tpd", data.get("pooled_tpd", 0)) or 0),
|
|
210
|
+
slot_inventory=payload.get("slot_inventory") or data.get("slot_inventory") or {},
|
|
211
|
+
active_entitlement_count=int(
|
|
212
|
+
payload.get(
|
|
213
|
+
"active_entitlement_count",
|
|
214
|
+
data.get("active_entitlement_count", data.get("active_subscription_count", 0)),
|
|
215
|
+
)
|
|
216
|
+
or 0
|
|
217
|
+
),
|
|
218
|
+
billing_reset_at=datetime.fromisoformat(str(payload.get("billing_reset_at")).replace("Z", "+00:00"))
|
|
219
|
+
if payload.get("billing_reset_at")
|
|
220
|
+
else None,
|
|
134
221
|
)
|
|
135
222
|
|
|
136
223
|
|
|
@@ -140,11 +227,16 @@ class HyperAgentSubscriptionSummary:
|
|
|
140
227
|
|
|
141
228
|
effective_plan_id: str
|
|
142
229
|
current_subscription_id: str | None
|
|
230
|
+
current_entitlement_id: str | None
|
|
143
231
|
pooled_tpm_limit: int
|
|
144
232
|
pooled_rpm_limit: int
|
|
145
233
|
pooled_tpd: int
|
|
146
234
|
slot_inventory: dict[str, Any]
|
|
235
|
+
billing_reset_at: datetime | None
|
|
147
236
|
active_subscription_count: int
|
|
237
|
+
active_entitlement_count: int
|
|
238
|
+
entitlements: HyperAgentEntitlements
|
|
239
|
+
entitlement_items: list[HyperAgentEntitlement]
|
|
148
240
|
active_subscriptions: list[HyperAgentSubscription]
|
|
149
241
|
subscriptions: list[HyperAgentSubscription]
|
|
150
242
|
user: dict[str, Any]
|
|
@@ -154,17 +246,42 @@ class HyperAgentSubscriptionSummary:
|
|
|
154
246
|
return cls(
|
|
155
247
|
effective_plan_id=data.get("effective_plan_id", ""),
|
|
156
248
|
current_subscription_id=data.get("current_subscription_id"),
|
|
249
|
+
current_entitlement_id=data.get("current_entitlement_id", data.get("current_subscription_id")),
|
|
157
250
|
pooled_tpm_limit=int(data.get("pooled_tpm_limit", 0) or 0),
|
|
158
251
|
pooled_rpm_limit=int(data.get("pooled_rpm_limit", 0) or 0),
|
|
159
252
|
pooled_tpd=int(data.get("pooled_tpd", 0) or 0),
|
|
160
253
|
slot_inventory=data.get("slot_inventory") or {},
|
|
254
|
+
billing_reset_at=datetime.fromisoformat(str(data.get("billing_reset_at")).replace("Z", "+00:00"))
|
|
255
|
+
if data.get("billing_reset_at")
|
|
256
|
+
else None,
|
|
161
257
|
active_subscription_count=int(data.get("active_subscription_count", 0) or 0),
|
|
258
|
+
active_entitlement_count=int(data.get("active_entitlement_count", data.get("active_subscription_count", 0)) or 0),
|
|
259
|
+
entitlements=HyperAgentEntitlements.from_dict(data),
|
|
260
|
+
entitlement_items=[HyperAgentEntitlement.from_dict(item) for item in data.get("entitlement_items", [])],
|
|
162
261
|
active_subscriptions=[HyperAgentSubscription.from_dict(item) for item in data.get("active_subscriptions", [])],
|
|
163
262
|
subscriptions=[HyperAgentSubscription.from_dict(item) for item in data.get("subscriptions", [])],
|
|
164
263
|
user=data.get("user") or {},
|
|
165
264
|
)
|
|
166
265
|
|
|
167
266
|
|
|
267
|
+
HyperAgentEntitlementsSummary = HyperAgentSubscriptionSummary
|
|
268
|
+
|
|
269
|
+
|
|
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
|
+
|
|
168
285
|
@dataclass
|
|
169
286
|
class HyperAgentModel:
|
|
170
287
|
"""Available model on HyperAgent."""
|
|
@@ -368,6 +485,35 @@ class HyperAgent:
|
|
|
368
485
|
response.raise_for_status()
|
|
369
486
|
return HyperAgentSubscriptionSummary.from_dict(response.json())
|
|
370
487
|
|
|
488
|
+
def entitlements(self) -> HyperAgentEntitlementsSummary:
|
|
489
|
+
response = self._http._session.get(
|
|
490
|
+
f"{self._control_base_url}/entitlements",
|
|
491
|
+
headers={"Authorization": f"Bearer {self._api_key}"},
|
|
492
|
+
)
|
|
493
|
+
response.raise_for_status()
|
|
494
|
+
return HyperAgentEntitlementsSummary.from_dict(response.json())
|
|
495
|
+
|
|
496
|
+
def entitlement_instances(self) -> list[HyperAgentEntitlement]:
|
|
497
|
+
response = self._http._session.get(
|
|
498
|
+
f"{self._control_base_url}/entitlements/instances",
|
|
499
|
+
headers={"Authorization": f"Bearer {self._api_key}"},
|
|
500
|
+
)
|
|
501
|
+
response.raise_for_status()
|
|
502
|
+
data = response.json()
|
|
503
|
+
return [HyperAgentEntitlement.from_dict(item) for item in data.get("items", [])]
|
|
504
|
+
|
|
505
|
+
def update_subscription(self, subscription_id: str, bundle: dict[str, int] | None) -> HyperAgentSubscriptionMutationResult:
|
|
506
|
+
response = self._http._session.post(
|
|
507
|
+
f"{self._control_base_url}/subscriptions/{subscription_id}/update",
|
|
508
|
+
headers={"Authorization": f"Bearer {self._api_key}"},
|
|
509
|
+
json={"bundle": dict(bundle or {})},
|
|
510
|
+
)
|
|
511
|
+
response.raise_for_status()
|
|
512
|
+
return HyperAgentSubscriptionMutationResult.from_dict(response.json())
|
|
513
|
+
|
|
514
|
+
def cancel_subscription(self, subscription_id: str) -> HyperAgentSubscriptionMutationResult:
|
|
515
|
+
return self.update_subscription(subscription_id, {})
|
|
516
|
+
|
|
371
517
|
def discovery_health(self) -> Dict[str, Any]:
|
|
372
518
|
response = self._http._session.get(f"{self._api_base_without_v1()}/discovery/health")
|
|
373
519
|
response.raise_for_status()
|
|
@@ -32,7 +32,7 @@ DEV_AGENTS_API_BASE = "https://api.dev.hypercli.com/agents"
|
|
|
32
32
|
DEV_AGENTS_WS_URL = "wss://api.agents.dev.hypercli.com/ws"
|
|
33
33
|
DEFAULT_OPENCLAW_IMAGE = "ghcr.io/hypercli/hypercli-openclaw:prod"
|
|
34
34
|
LAUNCH_CONFIG_KEYS = frozenset({"image", "env", "routes", "ports", "command", "entrypoint", "sync_root", "sync_enabled", "registry_url", "registry_auth"})
|
|
35
|
-
DEFAULT_OPENCLAW_SYNC_ROOT = "/home/
|
|
35
|
+
DEFAULT_OPENCLAW_SYNC_ROOT = "/home/node"
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def _is_directory_listing_payload(value: object) -> bool:
|
|
@@ -386,6 +386,28 @@ class Agent:
|
|
|
386
386
|
self._deployments = agent._deployments
|
|
387
387
|
return self
|
|
388
388
|
|
|
389
|
+
def update(
|
|
390
|
+
self,
|
|
391
|
+
*,
|
|
392
|
+
name: str | None = None,
|
|
393
|
+
size: str | None = None,
|
|
394
|
+
refresh_from_lagoon: bool | None = None,
|
|
395
|
+
last_error: str | None = None,
|
|
396
|
+
) -> "Agent":
|
|
397
|
+
agent = self._require_deployments().update(
|
|
398
|
+
self.id,
|
|
399
|
+
name=name,
|
|
400
|
+
size=size,
|
|
401
|
+
refresh_from_lagoon=refresh_from_lagoon,
|
|
402
|
+
last_error=last_error,
|
|
403
|
+
)
|
|
404
|
+
self.__dict__.update(agent.__dict__)
|
|
405
|
+
self._deployments = agent._deployments
|
|
406
|
+
return self
|
|
407
|
+
|
|
408
|
+
def resize(self, *, size: str | None = None) -> "Agent":
|
|
409
|
+
return self.update(size=size)
|
|
410
|
+
|
|
389
411
|
def env(self) -> dict[str, str]:
|
|
390
412
|
"""Fetch runtime environment from the pod's K8s secret."""
|
|
391
413
|
data = self._require_deployments().env(self.id)
|
|
@@ -980,8 +1002,6 @@ class Deployments:
|
|
|
980
1002
|
self,
|
|
981
1003
|
name: str = None,
|
|
982
1004
|
size: str = None,
|
|
983
|
-
cpu: int = None,
|
|
984
|
-
memory: int = None,
|
|
985
1005
|
config: dict = None,
|
|
986
1006
|
tags: list[str] = None,
|
|
987
1007
|
env: dict = None,
|
|
@@ -1004,8 +1024,6 @@ class Deployments:
|
|
|
1004
1024
|
Args:
|
|
1005
1025
|
name: Agent name.
|
|
1006
1026
|
size: Size preset (small/medium/large). Default: medium.
|
|
1007
|
-
cpu: Custom CPU in cores (overrides size).
|
|
1008
|
-
memory: Custom memory in GB (overrides size).
|
|
1009
1027
|
config: Optional config overrides.
|
|
1010
1028
|
env: Optional environment variables to pass through to the pod.
|
|
1011
1029
|
ports: Optional exposed ports config.
|
|
@@ -1035,10 +1053,6 @@ class Deployments:
|
|
|
1035
1053
|
body["name"] = name
|
|
1036
1054
|
if size:
|
|
1037
1055
|
body["size"] = size
|
|
1038
|
-
if cpu is not None:
|
|
1039
|
-
body["cpu"] = cpu
|
|
1040
|
-
if memory is not None:
|
|
1041
|
-
body["memory"] = memory
|
|
1042
1056
|
if meta_ui:
|
|
1043
1057
|
body["meta"] = {"ui": copy.deepcopy(meta_ui)}
|
|
1044
1058
|
if tags:
|
|
@@ -1056,8 +1070,6 @@ class Deployments:
|
|
|
1056
1070
|
self,
|
|
1057
1071
|
name: str = None,
|
|
1058
1072
|
size: str = None,
|
|
1059
|
-
cpu: int = None,
|
|
1060
|
-
memory: int = None,
|
|
1061
1073
|
config: dict = None,
|
|
1062
1074
|
tags: list[str] = None,
|
|
1063
1075
|
env: dict = None,
|
|
@@ -1077,14 +1089,13 @@ class Deployments:
|
|
|
1077
1089
|
openclaw_routes: dict | None = None,
|
|
1078
1090
|
openclaw_route_options: dict | None = None,
|
|
1079
1091
|
) -> Agent:
|
|
1092
|
+
effective_env = dict(env or {})
|
|
1080
1093
|
return self.create(
|
|
1081
1094
|
name=name,
|
|
1082
1095
|
size=size,
|
|
1083
|
-
cpu=cpu,
|
|
1084
|
-
memory=memory,
|
|
1085
1096
|
config=config,
|
|
1086
1097
|
tags=tags,
|
|
1087
|
-
env=
|
|
1098
|
+
env=effective_env,
|
|
1088
1099
|
ports=ports,
|
|
1089
1100
|
routes=_resolve_openclaw_routes(
|
|
1090
1101
|
routes,
|
|
@@ -1229,10 +1240,11 @@ class Deployments:
|
|
|
1229
1240
|
openclaw_routes: dict | None = None,
|
|
1230
1241
|
openclaw_route_options: dict | None = None,
|
|
1231
1242
|
) -> Agent:
|
|
1243
|
+
effective_env = dict(env or {})
|
|
1232
1244
|
return self.start(
|
|
1233
1245
|
agent_id,
|
|
1234
1246
|
config=config,
|
|
1235
|
-
env=
|
|
1247
|
+
env=effective_env,
|
|
1236
1248
|
ports=ports,
|
|
1237
1249
|
routes=_resolve_openclaw_routes(
|
|
1238
1250
|
routes,
|
|
@@ -1250,6 +1262,35 @@ class Deployments:
|
|
|
1250
1262
|
dry_run=dry_run,
|
|
1251
1263
|
)
|
|
1252
1264
|
|
|
1265
|
+
def update(
|
|
1266
|
+
self,
|
|
1267
|
+
agent_id: str,
|
|
1268
|
+
*,
|
|
1269
|
+
name: str | None = None,
|
|
1270
|
+
size: str | None = None,
|
|
1271
|
+
refresh_from_lagoon: bool | None = None,
|
|
1272
|
+
last_error: str | None = None,
|
|
1273
|
+
) -> Agent:
|
|
1274
|
+
body: dict[str, Any] = {}
|
|
1275
|
+
if name is not None:
|
|
1276
|
+
body["name"] = name
|
|
1277
|
+
if size is not None:
|
|
1278
|
+
body["size"] = size
|
|
1279
|
+
if refresh_from_lagoon is not None:
|
|
1280
|
+
body["refresh_from_lagoon"] = refresh_from_lagoon
|
|
1281
|
+
if last_error is not None:
|
|
1282
|
+
body["last_error"] = last_error
|
|
1283
|
+
data = self._http.patch(f"{AGENTS_API_PREFIX}/{agent_id}", json=body)
|
|
1284
|
+
return self._hydrate_agent(data)
|
|
1285
|
+
|
|
1286
|
+
def resize(
|
|
1287
|
+
self,
|
|
1288
|
+
agent_id: str,
|
|
1289
|
+
*,
|
|
1290
|
+
size: str | None = None,
|
|
1291
|
+
) -> Agent:
|
|
1292
|
+
return self.update(agent_id, size=size)
|
|
1293
|
+
|
|
1253
1294
|
def stop(self, agent_id: str) -> Agent:
|
|
1254
1295
|
"""Stop an agent (tears down pod, keeps DB record).
|
|
1255
1296
|
|
|
@@ -19,6 +19,7 @@ from .voice import VoiceAPI
|
|
|
19
19
|
from .agents import Deployments
|
|
20
20
|
from .agent import HyperAgent
|
|
21
21
|
from .keys import KeysAPI
|
|
22
|
+
from .models import ModelsAPI
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def _derive_agents_api_base(api_url: str, agent_dev: bool) -> str:
|
|
@@ -100,6 +101,7 @@ class HyperCLI:
|
|
|
100
101
|
self.files = Files(self._http)
|
|
101
102
|
self.voice = VoiceAPI(self._http)
|
|
102
103
|
self.keys = KeysAPI(self._http)
|
|
104
|
+
self.models = ModelsAPI(self._http)
|
|
103
105
|
self.agent = HyperAgent(
|
|
104
106
|
self._http,
|
|
105
107
|
agent_api_key=resolved_agent_api_key,
|
|
@@ -166,10 +166,10 @@ class HTTPClient:
|
|
|
166
166
|
)
|
|
167
167
|
return _handle_response(resp)
|
|
168
168
|
|
|
169
|
-
def post_bytes(self, path: str, json: dict = None) -> bytes:
|
|
169
|
+
def post_bytes(self, path: str, json: dict = None, timeout: float | None = None) -> bytes:
|
|
170
170
|
resp = request_with_retry(
|
|
171
171
|
"post", f"{self.base_url}{path}",
|
|
172
|
-
headers=self.headers, timeout=self.timeout, json=json
|
|
172
|
+
headers=self.headers, timeout=timeout if timeout is not None else self.timeout, json=json
|
|
173
173
|
)
|
|
174
174
|
return _handle_bytes_response(resp)
|
|
175
175
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""OpenAI-compatible models API"""
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import TYPE_CHECKING, List
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from .http import HTTPClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Model:
|
|
11
|
+
id: str
|
|
12
|
+
object: str
|
|
13
|
+
owned_by: str | None = None
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def from_dict(cls, data: dict) -> "Model":
|
|
17
|
+
return cls(
|
|
18
|
+
id=data.get("id", ""),
|
|
19
|
+
object=data.get("object", "model"),
|
|
20
|
+
owned_by=data.get("owned_by"),
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ModelsAPI:
|
|
25
|
+
"""OpenAI-compatible models API"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, http: "HTTPClient"):
|
|
28
|
+
self._http = http
|
|
29
|
+
|
|
30
|
+
def list(self) -> List[Model]:
|
|
31
|
+
payload = self._http.get("/v1/models")
|
|
32
|
+
data = payload.get("data") if isinstance(payload, dict) else payload
|
|
33
|
+
return [Model.from_dict(item) for item in (data or [])]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""OpenClaw-specific SDK surfaces."""
|
|
2
|
+
|
|
3
|
+
from .gateway import (
|
|
4
|
+
GatewayClient,
|
|
5
|
+
GatewayError,
|
|
6
|
+
ChatEvent,
|
|
7
|
+
GatewayChatToolCall,
|
|
8
|
+
GatewayChatMessageSummary,
|
|
9
|
+
extract_gateway_chat_thinking,
|
|
10
|
+
extract_gateway_chat_media_urls,
|
|
11
|
+
extract_gateway_chat_tool_calls,
|
|
12
|
+
normalize_gateway_chat_message,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"GatewayClient",
|
|
17
|
+
"GatewayError",
|
|
18
|
+
"ChatEvent",
|
|
19
|
+
"GatewayChatToolCall",
|
|
20
|
+
"GatewayChatMessageSummary",
|
|
21
|
+
"extract_gateway_chat_thinking",
|
|
22
|
+
"extract_gateway_chat_media_urls",
|
|
23
|
+
"extract_gateway_chat_tool_calls",
|
|
24
|
+
"normalize_gateway_chat_message",
|
|
25
|
+
]
|
|
@@ -14,12 +14,13 @@ import base64
|
|
|
14
14
|
import hashlib
|
|
15
15
|
import json
|
|
16
16
|
import os
|
|
17
|
+
import shlex
|
|
17
18
|
import time
|
|
18
19
|
import uuid
|
|
19
20
|
from dataclasses import asdict, dataclass
|
|
20
21
|
from pathlib import Path
|
|
21
22
|
from typing import Any, AsyncIterator, Callable, Literal, Optional
|
|
22
|
-
from urllib.parse import parse_qsl, quote, urlsplit
|
|
23
|
+
from urllib.parse import parse_qsl, quote, urlsplit, urlunsplit
|
|
23
24
|
|
|
24
25
|
import httpx
|
|
25
26
|
import websockets
|
|
@@ -815,6 +816,10 @@ class GatewayClient:
|
|
|
815
816
|
raise RuntimeError(
|
|
816
817
|
"auto_approve_pairing requires deployment_id, api_key, and api_base"
|
|
817
818
|
)
|
|
819
|
+
command = (
|
|
820
|
+
"openclaw devices approve "
|
|
821
|
+
f"{shlex.quote(request_id)} --json"
|
|
822
|
+
)
|
|
818
823
|
async with httpx.AsyncClient(timeout=30) as client:
|
|
819
824
|
response = await client.post(
|
|
820
825
|
f"{self.api_base}/deployments/{quote(self.deployment_id or '')}/exec",
|
|
@@ -823,7 +828,7 @@ class GatewayClient:
|
|
|
823
828
|
"Content-Type": "application/json",
|
|
824
829
|
},
|
|
825
830
|
json={
|
|
826
|
-
"command":
|
|
831
|
+
"command": command,
|
|
827
832
|
"timeout": 30,
|
|
828
833
|
},
|
|
829
834
|
)
|