hypercli-sdk 2026.6.26__tar.gz → 2026.7.31__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.
Potentially problematic release.
This version of hypercli-sdk might be problematic. Click here for more details.
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/.gitignore +3 -0
- hypercli_sdk-2026.7.31/PKG-INFO +331 -0
- hypercli_sdk-2026.7.31/README.md +296 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/__init__.py +70 -2
- hypercli_sdk-2026.7.31/hypercli/agents.py +3770 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/client.py +6 -1
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/http.py +10 -9
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/keys.py +13 -1
- hypercli_sdk-2026.7.31/hypercli/openclaw/__init__.py +67 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/openclaw/gateway.py +779 -56
- hypercli_sdk-2026.7.31/hypercli/openclaw/node_proxy.py +838 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/user.py +39 -5
- hypercli_sdk-2026.7.31/hypercli/voice.py +181 -0
- hypercli_sdk-2026.7.31/hypercli/voice_stream.py +246 -0
- hypercli_sdk-2026.7.31/hypercli/workspaces.py +920 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/pyproject.toml +11 -1
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/integration/test_agents.py +68 -1
- hypercli_sdk-2026.7.31/tests/integration/test_node_egress_e2e.py +231 -0
- hypercli_sdk-2026.7.31/tests/test_agent_files_source.py +247 -0
- hypercli_sdk-2026.7.31/tests/test_agent_lifecycle.py +208 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_agents.py +612 -14
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_bootstrap_console_test_key.py +24 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_bootstrap_dev_test_keys.py +24 -0
- hypercli_sdk-2026.7.31/tests/test_coding_agents.py +575 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_exec_shell_dryrun.py +39 -2
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_gateway.py +501 -25
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_http.py +24 -1
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_keys.py +32 -1
- hypercli_sdk-2026.7.31/tests/test_node_proxy.py +247 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_renders_subscription.py +16 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_voice.py +39 -5
- hypercli_sdk-2026.7.31/tests/test_voice_stream.py +293 -0
- hypercli_sdk-2026.7.31/tests/test_workspaces.py +910 -0
- hypercli_sdk-2026.6.26/PKG-INFO +0 -173
- hypercli_sdk-2026.6.26/README.md +0 -142
- hypercli_sdk-2026.6.26/hypercli/agents.py +0 -1949
- hypercli_sdk-2026.6.26/hypercli/openclaw/__init__.py +0 -25
- hypercli_sdk-2026.6.26/hypercli/voice.py +0 -102
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/_compat.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/agent.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/billing.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/config.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/files.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/gateway.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/instances.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/job/__init__.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/job/base.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/job/comfyui.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/job/gradio.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/jobs.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/logs.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/models.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/renders.py +4 -4
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/shell.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/hypercli/x402.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/integration/conftest.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/integration/test_auth.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/integration/test_billing.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/integration/test_instances.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/integration/test_jobs_dryrun.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/integration/test_keys.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/integration/test_renders.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_apply_params.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_claw.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_config.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_gateway_retry.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_graph_to_api.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_jobs.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_models.py +0 -0
- {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.7.31}/tests/test_status.py +0 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hypercli-sdk
|
|
3
|
+
Version: 2026.7.31
|
|
4
|
+
Summary: Python SDK for HyperCLI - GPU orchestration and HyperAgent API
|
|
5
|
+
Project-URL: Homepage, https://hypercli.com
|
|
6
|
+
Project-URL: Documentation, https://docs.hypercli.com
|
|
7
|
+
Project-URL: Repository, https://github.com/HyperCLI/hypercli
|
|
8
|
+
Author-email: HyperCLI <support@hypercli.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: httpx>=0.28.1
|
|
18
|
+
Requires-Dist: pynacl>=1.5.0
|
|
19
|
+
Requires-Dist: websockets>=15.0.1
|
|
20
|
+
Provides-Extra: agent
|
|
21
|
+
Requires-Dist: openai>=1.0.0; extra == 'agent'
|
|
22
|
+
Provides-Extra: all
|
|
23
|
+
Requires-Dist: comfyui-workflow-templates-media-image>=0.3.0; extra == 'all'
|
|
24
|
+
Requires-Dist: comfyui-workflow-templates>=0.7.0; extra == 'all'
|
|
25
|
+
Requires-Dist: openai>=1.0.0; extra == 'all'
|
|
26
|
+
Provides-Extra: comfyui
|
|
27
|
+
Requires-Dist: comfyui-workflow-templates-media-image>=0.3.0; extra == 'comfyui'
|
|
28
|
+
Requires-Dist: comfyui-workflow-templates>=0.7.0; extra == 'comfyui'
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: openai>=1.0.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.3.0; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# HyperCLI SDK
|
|
37
|
+
|
|
38
|
+
Python SDK for [HyperCLI](https://hypercli.com) - GPU orchestration API.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install hypercli-sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Setup
|
|
47
|
+
|
|
48
|
+
Set your API key:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
export HYPER_API_KEY=your_api_key
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Or create `~/.hypercli/config`:
|
|
55
|
+
```
|
|
56
|
+
HYPER_API_KEY=your_api_key
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Or pass directly:
|
|
60
|
+
```python
|
|
61
|
+
client = HyperCLI(api_key="your_api_key")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Usage
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from hypercli import HyperCLI
|
|
68
|
+
|
|
69
|
+
client = HyperCLI()
|
|
70
|
+
|
|
71
|
+
# Check balance
|
|
72
|
+
balance = client.billing.balance()
|
|
73
|
+
print(f"Balance: ${balance.total:.2f}")
|
|
74
|
+
print(f"Rewards: ${balance.rewards:.2f}")
|
|
75
|
+
|
|
76
|
+
# List transactions
|
|
77
|
+
for tx in client.billing.transactions(limit=10):
|
|
78
|
+
print(f"{tx.transaction_type}: ${tx.amount_usd:.4f}")
|
|
79
|
+
|
|
80
|
+
# Create a job
|
|
81
|
+
job = client.jobs.create(
|
|
82
|
+
image="nvidia/cuda:12.0",
|
|
83
|
+
command="python train.py",
|
|
84
|
+
gpu_type="l40s",
|
|
85
|
+
gpu_count=1,
|
|
86
|
+
)
|
|
87
|
+
print(f"Job ID: {job.job_id}")
|
|
88
|
+
print(f"State: {job.state}")
|
|
89
|
+
|
|
90
|
+
# List jobs
|
|
91
|
+
for job in client.jobs.list():
|
|
92
|
+
print(f"{job.job_id}: {job.state}")
|
|
93
|
+
|
|
94
|
+
# Get job details
|
|
95
|
+
job = client.jobs.get("job_id")
|
|
96
|
+
|
|
97
|
+
# Get job logs
|
|
98
|
+
logs = client.jobs.logs("job_id")
|
|
99
|
+
|
|
100
|
+
# Get GPU metrics
|
|
101
|
+
metrics = client.jobs.metrics("job_id")
|
|
102
|
+
for gpu in metrics.gpus:
|
|
103
|
+
print(f"GPU {gpu.index}: {gpu.utilization}% util, {gpu.temperature}°C")
|
|
104
|
+
|
|
105
|
+
# Cancel a job
|
|
106
|
+
client.jobs.cancel("job_id")
|
|
107
|
+
|
|
108
|
+
# Extend runtime
|
|
109
|
+
client.jobs.extend("job_id", runtime=7200)
|
|
110
|
+
|
|
111
|
+
# Get user info
|
|
112
|
+
user = client.user.get()
|
|
113
|
+
print(f"User: {user.email}")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## HyperAgent API
|
|
117
|
+
|
|
118
|
+
Use `client.agent` for discovery and plan metadata, and point the OpenAI SDK at
|
|
119
|
+
the HyperClaw inference base URL for chat completions:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from hypercli import HyperCLI
|
|
123
|
+
from openai import OpenAI
|
|
124
|
+
|
|
125
|
+
sdk = HyperCLI(api_key="hyper_api_key", agent_api_key="sk-agent")
|
|
126
|
+
plans = sdk.agent.plans()
|
|
127
|
+
activation = sdk.agent.redeem_grant_code("PROMO123")
|
|
128
|
+
renewal = sdk.agent.redeem_grant_code("PROMO123", extend_existing=True)
|
|
129
|
+
|
|
130
|
+
client = OpenAI(
|
|
131
|
+
api_key="your_hyperagent_api_key",
|
|
132
|
+
base_url="https://api.hypercli.com/v1"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
response = client.chat.completions.create(
|
|
136
|
+
model="deepseek-v3.1",
|
|
137
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
138
|
+
)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`redeem_grant_code()` applies a promo/activation code to the current HyperClaw account and returns the created entitlement. Codes create new entitlements by default; pass `extend_existing=True` only for renewal/extension behavior.
|
|
142
|
+
|
|
143
|
+
## OpenClaw Agents
|
|
144
|
+
|
|
145
|
+
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`.
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
agent = client.deployments.create_openclaw(
|
|
149
|
+
name="docs-demo",
|
|
150
|
+
start=True,
|
|
151
|
+
registry_url="git.nedos.co",
|
|
152
|
+
registry_auth={"username": "ci", "password": "token"},
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Use `create_openclaw_pro(...)` for the desktop/browser image. It enables noVNC through the protected `desktop-<agent>.hypercli.app` route and sets `OPENCLAW_DESKTOP_ENABLED=1`.
|
|
157
|
+
|
|
158
|
+
`heartbeat` maps directly to upstream OpenClaw config at `config.agents.defaults.heartbeat`. Omit it to keep upstream defaults, or pass values such as `heartbeat={"every": "1h", "target": "last"}`.
|
|
159
|
+
|
|
160
|
+
Automatic memory indexing is off by default. Opt in with `memory_index={"on_session_start": True, "on_search": True, "watch": True, "watch_debounce_ms": 30000, "interval_minutes": 0}`.
|
|
161
|
+
|
|
162
|
+
## Hosted Coding Agents
|
|
163
|
+
|
|
164
|
+
OpenCode, Codex, Claude Code, Goose, and Kimi Code use canonical Reef images.
|
|
165
|
+
They have no public runtime port: lifecycle, exec, shell, workspace sync, and
|
|
166
|
+
authentication all use the existing authenticated deployment APIs. OpenCode
|
|
167
|
+
and Goose default to HyperCLI's Anthropic-native `kimi-k2.6-anthropic` route.
|
|
168
|
+
Kimi Code keeps Moonshot's upstream device login and service.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
agent = client.deployments.create_opencode(name="opencode")
|
|
172
|
+
codex = client.deployments.create_codex(name="codex")
|
|
173
|
+
claude = client.deployments.create_claude_code(name="claude")
|
|
174
|
+
goose = client.deployments.create_goose(name="goose")
|
|
175
|
+
kimi = client.deployments.create_kimi_code(name="kimi")
|
|
176
|
+
|
|
177
|
+
methods = codex.auth.methods()
|
|
178
|
+
status = codex.auth.status()
|
|
179
|
+
|
|
180
|
+
async with await codex.auth.login("device") as login:
|
|
181
|
+
print(login.verification_url, login.user_code)
|
|
182
|
+
await login.wait()
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The login helper opens a short-lived, agent-bound shell WebSocket and runs the
|
|
186
|
+
runtime's native login command inside the Reef pod. It never puts an API key on
|
|
187
|
+
the command line. Runtime credentials and state live under the persistent
|
|
188
|
+
`/home/node` sync root.
|
|
189
|
+
|
|
190
|
+
The images default to a long-lived direct shell/exec container. A Buzz provider
|
|
191
|
+
launches one for a Buzz-managed identity with the typed launch contract:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from hypercli import BuzzLaunchConfig
|
|
195
|
+
|
|
196
|
+
agent = client.deployments.create_opencode(
|
|
197
|
+
name="buzz-opencode",
|
|
198
|
+
env={"HYPER_API_KEY": inference_key},
|
|
199
|
+
buzz=BuzzLaunchConfig(
|
|
200
|
+
private_key_nsec=agent_nsec,
|
|
201
|
+
relay_url=relay_url,
|
|
202
|
+
auth_tag=owner_signed_auth_tag,
|
|
203
|
+
parallelism=1,
|
|
204
|
+
),
|
|
205
|
+
)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The SDK selects `/usr/local/bin/buzz-acp`, the runtime-specific child ACP
|
|
209
|
+
command and arguments, the hosted Buzz MCP command, lazy pool creation, relay
|
|
210
|
+
observation, and persistent `/home/node` settings. Buzz-reserved environment
|
|
211
|
+
keys are rendered from the typed object after caller environment values.
|
|
212
|
+
`buzz_enabled=True` remains as a deprecated raw-environment compatibility path.
|
|
213
|
+
Typed and compatibility Buzz launches select the matching `hypercli-buzz`
|
|
214
|
+
image family (`opencode`, `codex`, `claude`, `goose`, or `kimi-code`) by
|
|
215
|
+
default. Ordinary coding-agent helpers without Buzz keep the generic
|
|
216
|
+
`ghcr.io/hypercli/hypercli-<runtime>:latest` default. An explicit `image=`
|
|
217
|
+
continues to override either default.
|
|
218
|
+
|
|
219
|
+
Direct `BuzzLaunchConfig` renders timeout and response-policy values but does
|
|
220
|
+
not duplicate the stock Desktop provider's validation; invalid combinations
|
|
221
|
+
are rejected later by `buzz-acp`. The Desktop provider also maps structured
|
|
222
|
+
Goose model/provider fields to `GOOSE_MODEL`/`GOOSE_PROVIDER`; direct Python
|
|
223
|
+
SDK callers must set any Goose-specific environment themselves.
|
|
224
|
+
|
|
225
|
+
Buzz launches require `size="large"`; ordinary coding-agent helpers preserve a
|
|
226
|
+
caller-provided size or the backend default. Stock Buzz provider agents do not
|
|
227
|
+
start on app launch and the current provider protocol has no stop callback.
|
|
228
|
+
Editing a running agent does not replace its HyperCLI launch environment: stop
|
|
229
|
+
the deployment through the authenticated HyperCLI API and deploy it again from
|
|
230
|
+
Buzz to apply changes. Desktop's best-effort `!shutdown` chat control may exit
|
|
231
|
+
the harness, but it does not stop or release the HyperCLI deployment.
|
|
232
|
+
|
|
233
|
+
Stock Buzz expects ACP NDJSON. It skips non-JSON child stdout, and
|
|
234
|
+
`agent_message_chunk` is activity telemetry rather than a channel reply. There
|
|
235
|
+
is no plaintext fallback; a visible reply requires the agent to invoke the Buzz
|
|
236
|
+
send command/tool. The five-runtime SDK coverage validates request rendering,
|
|
237
|
+
not live launches.
|
|
238
|
+
|
|
239
|
+
The agent nsec and caller environment become raw deployment environment values.
|
|
240
|
+
The HyperClaw backend currently persists them in `Agent.launch_config`, and
|
|
241
|
+
authenticated deployment read, environment, or exec surfaces may expose them.
|
|
242
|
+
The default `RUST_LOG` filter disables `acp::stream` content logging; overriding
|
|
243
|
+
it can expose generated text in container logs.
|
|
244
|
+
|
|
245
|
+
## OpenClaw Node Egress
|
|
246
|
+
|
|
247
|
+
The Python SDK includes an experimental reference implementation for user-owned
|
|
248
|
+
node egress in `hypercli.openclaw.node_proxy`. It uses the existing OpenClaw
|
|
249
|
+
node model:
|
|
250
|
+
|
|
251
|
+
- a node connects to the gateway with `role="node"`
|
|
252
|
+
- the node declares explicit `egress.*` command names during the connect
|
|
253
|
+
handshake
|
|
254
|
+
- an operator/client calls `GatewayClient.node_invoke(node_id, command, params)`
|
|
255
|
+
- the gateway sends one `node.invoke.request` and waits for one
|
|
256
|
+
`node.invoke.result`
|
|
257
|
+
|
|
258
|
+
This is not raw sockets over the gateway. It is node RPC with chunked payloads
|
|
259
|
+
and gateway policy approval.
|
|
260
|
+
|
|
261
|
+
Node side:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
from hypercli.openclaw import NodeEgressServer
|
|
265
|
+
|
|
266
|
+
node = NodeEgressServer(
|
|
267
|
+
"wss://my-agent.hypercli.app",
|
|
268
|
+
"home-linux-egress",
|
|
269
|
+
gateway_token="...",
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
await node.connect()
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Operator side:
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
from hypercli.openclaw import EGRESS_COMMANDS, NodeEgressClient
|
|
279
|
+
|
|
280
|
+
egress = NodeEgressClient(gateway, node_id="home-linux-egress")
|
|
281
|
+
res = await egress.http_fetch("https://example.com/")
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Commands:
|
|
285
|
+
|
|
286
|
+
- `egress.http.fetch`: bounded HTTP(S) fetch, response body returned as base64
|
|
287
|
+
chunks
|
|
288
|
+
- `egress.tcp.open/read/write/close`: experimental TCP tunnel primitives used
|
|
289
|
+
by `LoopbackNodeProxy` for HTTP `CONNECT`
|
|
290
|
+
|
|
291
|
+
Security defaults:
|
|
292
|
+
|
|
293
|
+
- local proxy binds to `127.0.0.1` by default
|
|
294
|
+
- node id is explicit; no automatic node selection
|
|
295
|
+
- RFC1918/private, loopback, link-local, multicast, reserved, and metadata IPs
|
|
296
|
+
are blocked by default unless explicitly allowed on the node
|
|
297
|
+
- chunks are small and bounded; responses are not returned as one unbounded
|
|
298
|
+
base64 blob
|
|
299
|
+
|
|
300
|
+
Pairing and policy:
|
|
301
|
+
|
|
302
|
+
- the node must be device-paired
|
|
303
|
+
- the node command surface must be approved
|
|
304
|
+
- custom `egress.*` commands may need `gateway.nodes.allowCommands`
|
|
305
|
+
|
|
306
|
+
Python/Linux is first because it is easiest to test in CI and the Python SDK
|
|
307
|
+
already ships `NodeServer`. The portable contract is the command surface and
|
|
308
|
+
payload shape, not the Python implementation. macOS Backseat Driver already
|
|
309
|
+
proves the native node-host precedent; Android should eventually gain Kotlin
|
|
310
|
+
`NodeRuntime` parity; the TS SDK can mirror operator/client types if useful.
|
|
311
|
+
|
|
312
|
+
`LoopbackNodeProxy` can relay absolute-form HTTP requests and has experimental
|
|
313
|
+
`CONNECT` support over polling/chunked `node.invoke`. Treat CONNECT as a
|
|
314
|
+
feasibility prototype, not production-grade streaming.
|
|
315
|
+
|
|
316
|
+
## Error Handling
|
|
317
|
+
|
|
318
|
+
```python
|
|
319
|
+
from hypercli import HyperCLI, APIError
|
|
320
|
+
|
|
321
|
+
client = HyperCLI()
|
|
322
|
+
|
|
323
|
+
try:
|
|
324
|
+
job = client.jobs.get("invalid_id")
|
|
325
|
+
except APIError as e:
|
|
326
|
+
print(f"Error {e.status_code}: {e.detail}")
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## License
|
|
330
|
+
|
|
331
|
+
MIT
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# HyperCLI SDK
|
|
2
|
+
|
|
3
|
+
Python SDK for [HyperCLI](https://hypercli.com) - GPU orchestration API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install hypercli-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
Set your API key:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
export HYPER_API_KEY=your_api_key
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or create `~/.hypercli/config`:
|
|
20
|
+
```
|
|
21
|
+
HYPER_API_KEY=your_api_key
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or pass directly:
|
|
25
|
+
```python
|
|
26
|
+
client = HyperCLI(api_key="your_api_key")
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from hypercli import HyperCLI
|
|
33
|
+
|
|
34
|
+
client = HyperCLI()
|
|
35
|
+
|
|
36
|
+
# Check balance
|
|
37
|
+
balance = client.billing.balance()
|
|
38
|
+
print(f"Balance: ${balance.total:.2f}")
|
|
39
|
+
print(f"Rewards: ${balance.rewards:.2f}")
|
|
40
|
+
|
|
41
|
+
# List transactions
|
|
42
|
+
for tx in client.billing.transactions(limit=10):
|
|
43
|
+
print(f"{tx.transaction_type}: ${tx.amount_usd:.4f}")
|
|
44
|
+
|
|
45
|
+
# Create a job
|
|
46
|
+
job = client.jobs.create(
|
|
47
|
+
image="nvidia/cuda:12.0",
|
|
48
|
+
command="python train.py",
|
|
49
|
+
gpu_type="l40s",
|
|
50
|
+
gpu_count=1,
|
|
51
|
+
)
|
|
52
|
+
print(f"Job ID: {job.job_id}")
|
|
53
|
+
print(f"State: {job.state}")
|
|
54
|
+
|
|
55
|
+
# List jobs
|
|
56
|
+
for job in client.jobs.list():
|
|
57
|
+
print(f"{job.job_id}: {job.state}")
|
|
58
|
+
|
|
59
|
+
# Get job details
|
|
60
|
+
job = client.jobs.get("job_id")
|
|
61
|
+
|
|
62
|
+
# Get job logs
|
|
63
|
+
logs = client.jobs.logs("job_id")
|
|
64
|
+
|
|
65
|
+
# Get GPU metrics
|
|
66
|
+
metrics = client.jobs.metrics("job_id")
|
|
67
|
+
for gpu in metrics.gpus:
|
|
68
|
+
print(f"GPU {gpu.index}: {gpu.utilization}% util, {gpu.temperature}°C")
|
|
69
|
+
|
|
70
|
+
# Cancel a job
|
|
71
|
+
client.jobs.cancel("job_id")
|
|
72
|
+
|
|
73
|
+
# Extend runtime
|
|
74
|
+
client.jobs.extend("job_id", runtime=7200)
|
|
75
|
+
|
|
76
|
+
# Get user info
|
|
77
|
+
user = client.user.get()
|
|
78
|
+
print(f"User: {user.email}")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## HyperAgent API
|
|
82
|
+
|
|
83
|
+
Use `client.agent` for discovery and plan metadata, and point the OpenAI SDK at
|
|
84
|
+
the HyperClaw inference base URL for chat completions:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from hypercli import HyperCLI
|
|
88
|
+
from openai import OpenAI
|
|
89
|
+
|
|
90
|
+
sdk = HyperCLI(api_key="hyper_api_key", agent_api_key="sk-agent")
|
|
91
|
+
plans = sdk.agent.plans()
|
|
92
|
+
activation = sdk.agent.redeem_grant_code("PROMO123")
|
|
93
|
+
renewal = sdk.agent.redeem_grant_code("PROMO123", extend_existing=True)
|
|
94
|
+
|
|
95
|
+
client = OpenAI(
|
|
96
|
+
api_key="your_hyperagent_api_key",
|
|
97
|
+
base_url="https://api.hypercli.com/v1"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
response = client.chat.completions.create(
|
|
101
|
+
model="deepseek-v3.1",
|
|
102
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`redeem_grant_code()` applies a promo/activation code to the current HyperClaw account and returns the created entitlement. Codes create new entitlements by default; pass `extend_existing=True` only for renewal/extension behavior.
|
|
107
|
+
|
|
108
|
+
## OpenClaw Agents
|
|
109
|
+
|
|
110
|
+
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`.
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
agent = client.deployments.create_openclaw(
|
|
114
|
+
name="docs-demo",
|
|
115
|
+
start=True,
|
|
116
|
+
registry_url="git.nedos.co",
|
|
117
|
+
registry_auth={"username": "ci", "password": "token"},
|
|
118
|
+
)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Use `create_openclaw_pro(...)` for the desktop/browser image. It enables noVNC through the protected `desktop-<agent>.hypercli.app` route and sets `OPENCLAW_DESKTOP_ENABLED=1`.
|
|
122
|
+
|
|
123
|
+
`heartbeat` maps directly to upstream OpenClaw config at `config.agents.defaults.heartbeat`. Omit it to keep upstream defaults, or pass values such as `heartbeat={"every": "1h", "target": "last"}`.
|
|
124
|
+
|
|
125
|
+
Automatic memory indexing is off by default. Opt in with `memory_index={"on_session_start": True, "on_search": True, "watch": True, "watch_debounce_ms": 30000, "interval_minutes": 0}`.
|
|
126
|
+
|
|
127
|
+
## Hosted Coding Agents
|
|
128
|
+
|
|
129
|
+
OpenCode, Codex, Claude Code, Goose, and Kimi Code use canonical Reef images.
|
|
130
|
+
They have no public runtime port: lifecycle, exec, shell, workspace sync, and
|
|
131
|
+
authentication all use the existing authenticated deployment APIs. OpenCode
|
|
132
|
+
and Goose default to HyperCLI's Anthropic-native `kimi-k2.6-anthropic` route.
|
|
133
|
+
Kimi Code keeps Moonshot's upstream device login and service.
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
agent = client.deployments.create_opencode(name="opencode")
|
|
137
|
+
codex = client.deployments.create_codex(name="codex")
|
|
138
|
+
claude = client.deployments.create_claude_code(name="claude")
|
|
139
|
+
goose = client.deployments.create_goose(name="goose")
|
|
140
|
+
kimi = client.deployments.create_kimi_code(name="kimi")
|
|
141
|
+
|
|
142
|
+
methods = codex.auth.methods()
|
|
143
|
+
status = codex.auth.status()
|
|
144
|
+
|
|
145
|
+
async with await codex.auth.login("device") as login:
|
|
146
|
+
print(login.verification_url, login.user_code)
|
|
147
|
+
await login.wait()
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The login helper opens a short-lived, agent-bound shell WebSocket and runs the
|
|
151
|
+
runtime's native login command inside the Reef pod. It never puts an API key on
|
|
152
|
+
the command line. Runtime credentials and state live under the persistent
|
|
153
|
+
`/home/node` sync root.
|
|
154
|
+
|
|
155
|
+
The images default to a long-lived direct shell/exec container. A Buzz provider
|
|
156
|
+
launches one for a Buzz-managed identity with the typed launch contract:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from hypercli import BuzzLaunchConfig
|
|
160
|
+
|
|
161
|
+
agent = client.deployments.create_opencode(
|
|
162
|
+
name="buzz-opencode",
|
|
163
|
+
env={"HYPER_API_KEY": inference_key},
|
|
164
|
+
buzz=BuzzLaunchConfig(
|
|
165
|
+
private_key_nsec=agent_nsec,
|
|
166
|
+
relay_url=relay_url,
|
|
167
|
+
auth_tag=owner_signed_auth_tag,
|
|
168
|
+
parallelism=1,
|
|
169
|
+
),
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The SDK selects `/usr/local/bin/buzz-acp`, the runtime-specific child ACP
|
|
174
|
+
command and arguments, the hosted Buzz MCP command, lazy pool creation, relay
|
|
175
|
+
observation, and persistent `/home/node` settings. Buzz-reserved environment
|
|
176
|
+
keys are rendered from the typed object after caller environment values.
|
|
177
|
+
`buzz_enabled=True` remains as a deprecated raw-environment compatibility path.
|
|
178
|
+
Typed and compatibility Buzz launches select the matching `hypercli-buzz`
|
|
179
|
+
image family (`opencode`, `codex`, `claude`, `goose`, or `kimi-code`) by
|
|
180
|
+
default. Ordinary coding-agent helpers without Buzz keep the generic
|
|
181
|
+
`ghcr.io/hypercli/hypercli-<runtime>:latest` default. An explicit `image=`
|
|
182
|
+
continues to override either default.
|
|
183
|
+
|
|
184
|
+
Direct `BuzzLaunchConfig` renders timeout and response-policy values but does
|
|
185
|
+
not duplicate the stock Desktop provider's validation; invalid combinations
|
|
186
|
+
are rejected later by `buzz-acp`. The Desktop provider also maps structured
|
|
187
|
+
Goose model/provider fields to `GOOSE_MODEL`/`GOOSE_PROVIDER`; direct Python
|
|
188
|
+
SDK callers must set any Goose-specific environment themselves.
|
|
189
|
+
|
|
190
|
+
Buzz launches require `size="large"`; ordinary coding-agent helpers preserve a
|
|
191
|
+
caller-provided size or the backend default. Stock Buzz provider agents do not
|
|
192
|
+
start on app launch and the current provider protocol has no stop callback.
|
|
193
|
+
Editing a running agent does not replace its HyperCLI launch environment: stop
|
|
194
|
+
the deployment through the authenticated HyperCLI API and deploy it again from
|
|
195
|
+
Buzz to apply changes. Desktop's best-effort `!shutdown` chat control may exit
|
|
196
|
+
the harness, but it does not stop or release the HyperCLI deployment.
|
|
197
|
+
|
|
198
|
+
Stock Buzz expects ACP NDJSON. It skips non-JSON child stdout, and
|
|
199
|
+
`agent_message_chunk` is activity telemetry rather than a channel reply. There
|
|
200
|
+
is no plaintext fallback; a visible reply requires the agent to invoke the Buzz
|
|
201
|
+
send command/tool. The five-runtime SDK coverage validates request rendering,
|
|
202
|
+
not live launches.
|
|
203
|
+
|
|
204
|
+
The agent nsec and caller environment become raw deployment environment values.
|
|
205
|
+
The HyperClaw backend currently persists them in `Agent.launch_config`, and
|
|
206
|
+
authenticated deployment read, environment, or exec surfaces may expose them.
|
|
207
|
+
The default `RUST_LOG` filter disables `acp::stream` content logging; overriding
|
|
208
|
+
it can expose generated text in container logs.
|
|
209
|
+
|
|
210
|
+
## OpenClaw Node Egress
|
|
211
|
+
|
|
212
|
+
The Python SDK includes an experimental reference implementation for user-owned
|
|
213
|
+
node egress in `hypercli.openclaw.node_proxy`. It uses the existing OpenClaw
|
|
214
|
+
node model:
|
|
215
|
+
|
|
216
|
+
- a node connects to the gateway with `role="node"`
|
|
217
|
+
- the node declares explicit `egress.*` command names during the connect
|
|
218
|
+
handshake
|
|
219
|
+
- an operator/client calls `GatewayClient.node_invoke(node_id, command, params)`
|
|
220
|
+
- the gateway sends one `node.invoke.request` and waits for one
|
|
221
|
+
`node.invoke.result`
|
|
222
|
+
|
|
223
|
+
This is not raw sockets over the gateway. It is node RPC with chunked payloads
|
|
224
|
+
and gateway policy approval.
|
|
225
|
+
|
|
226
|
+
Node side:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
from hypercli.openclaw import NodeEgressServer
|
|
230
|
+
|
|
231
|
+
node = NodeEgressServer(
|
|
232
|
+
"wss://my-agent.hypercli.app",
|
|
233
|
+
"home-linux-egress",
|
|
234
|
+
gateway_token="...",
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
await node.connect()
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Operator side:
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
from hypercli.openclaw import EGRESS_COMMANDS, NodeEgressClient
|
|
244
|
+
|
|
245
|
+
egress = NodeEgressClient(gateway, node_id="home-linux-egress")
|
|
246
|
+
res = await egress.http_fetch("https://example.com/")
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Commands:
|
|
250
|
+
|
|
251
|
+
- `egress.http.fetch`: bounded HTTP(S) fetch, response body returned as base64
|
|
252
|
+
chunks
|
|
253
|
+
- `egress.tcp.open/read/write/close`: experimental TCP tunnel primitives used
|
|
254
|
+
by `LoopbackNodeProxy` for HTTP `CONNECT`
|
|
255
|
+
|
|
256
|
+
Security defaults:
|
|
257
|
+
|
|
258
|
+
- local proxy binds to `127.0.0.1` by default
|
|
259
|
+
- node id is explicit; no automatic node selection
|
|
260
|
+
- RFC1918/private, loopback, link-local, multicast, reserved, and metadata IPs
|
|
261
|
+
are blocked by default unless explicitly allowed on the node
|
|
262
|
+
- chunks are small and bounded; responses are not returned as one unbounded
|
|
263
|
+
base64 blob
|
|
264
|
+
|
|
265
|
+
Pairing and policy:
|
|
266
|
+
|
|
267
|
+
- the node must be device-paired
|
|
268
|
+
- the node command surface must be approved
|
|
269
|
+
- custom `egress.*` commands may need `gateway.nodes.allowCommands`
|
|
270
|
+
|
|
271
|
+
Python/Linux is first because it is easiest to test in CI and the Python SDK
|
|
272
|
+
already ships `NodeServer`. The portable contract is the command surface and
|
|
273
|
+
payload shape, not the Python implementation. macOS Backseat Driver already
|
|
274
|
+
proves the native node-host precedent; Android should eventually gain Kotlin
|
|
275
|
+
`NodeRuntime` parity; the TS SDK can mirror operator/client types if useful.
|
|
276
|
+
|
|
277
|
+
`LoopbackNodeProxy` can relay absolute-form HTTP requests and has experimental
|
|
278
|
+
`CONNECT` support over polling/chunked `node.invoke`. Treat CONNECT as a
|
|
279
|
+
feasibility prototype, not production-grade streaming.
|
|
280
|
+
|
|
281
|
+
## Error Handling
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
from hypercli import HyperCLI, APIError
|
|
285
|
+
|
|
286
|
+
client = HyperCLI()
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
job = client.jobs.get("invalid_id")
|
|
290
|
+
except APIError as e:
|
|
291
|
+
print(f"Error {e.status_code}: {e.detail}")
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## License
|
|
295
|
+
|
|
296
|
+
MIT
|