hypercli-sdk 2026.6.26__tar.gz → 2026.8.11__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.

Files changed (78) hide show
  1. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/.gitignore +5 -0
  2. hypercli_sdk-2026.8.11/PKG-INFO +426 -0
  3. hypercli_sdk-2026.8.11/README.md +391 -0
  4. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/__init__.py +161 -2
  5. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/agent.py +320 -26
  6. hypercli_sdk-2026.8.11/hypercli/agents.py +4824 -0
  7. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/billing.py +4 -0
  8. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/client.py +12 -4
  9. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/config.py +3 -4
  10. hypercli_sdk-2026.8.11/hypercli/hermes/__init__.py +37 -0
  11. hypercli_sdk-2026.8.11/hypercli/hermes/gateway.py +402 -0
  12. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/http.py +10 -9
  13. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/job/base.py +8 -1
  14. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/keys.py +53 -2
  15. hypercli_sdk-2026.8.11/hypercli/openclaw/__init__.py +67 -0
  16. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/openclaw/gateway.py +779 -56
  17. hypercli_sdk-2026.8.11/hypercli/openclaw/node_proxy.py +838 -0
  18. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/user.py +47 -5
  19. hypercli_sdk-2026.8.11/hypercli/voice.py +181 -0
  20. hypercli_sdk-2026.8.11/hypercli/voice_stream.py +246 -0
  21. hypercli_sdk-2026.8.11/hypercli/workspaces.py +920 -0
  22. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/pyproject.toml +11 -1
  23. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/integration/conftest.py +1 -1
  24. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/integration/test_agents.py +71 -1
  25. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/integration/test_auth.py +1 -1
  26. hypercli_sdk-2026.8.11/tests/integration/test_node_egress_e2e.py +231 -0
  27. hypercli_sdk-2026.8.11/tests/test_agent_files_source.py +245 -0
  28. hypercli_sdk-2026.8.11/tests/test_agent_lifecycle.py +202 -0
  29. hypercli_sdk-2026.8.11/tests/test_agents.py +2856 -0
  30. hypercli_sdk-2026.8.11/tests/test_bootstrap_agents_e2e_user.py +133 -0
  31. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_bootstrap_console_test_key.py +24 -0
  32. hypercli_sdk-2026.8.11/tests/test_bootstrap_dev_test_keys.py +207 -0
  33. hypercli_sdk-2026.8.11/tests/test_ci_dev_control_plane_lock.py +62 -0
  34. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_claw.py +388 -35
  35. hypercli_sdk-2026.8.11/tests/test_coding_agents.py +843 -0
  36. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_config.py +21 -5
  37. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_exec_shell_dryrun.py +40 -3
  38. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_gateway.py +501 -25
  39. hypercli_sdk-2026.8.11/tests/test_hermes.py +241 -0
  40. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_http.py +24 -1
  41. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_jobs.py +23 -0
  42. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_keys.py +104 -2
  43. hypercli_sdk-2026.8.11/tests/test_node_proxy.py +247 -0
  44. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_renders_subscription.py +16 -0
  45. hypercli_sdk-2026.8.11/tests/test_repository_integrity.py +27 -0
  46. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_voice.py +39 -5
  47. hypercli_sdk-2026.8.11/tests/test_voice_stream.py +293 -0
  48. hypercli_sdk-2026.8.11/tests/test_workspaces.py +910 -0
  49. hypercli_sdk-2026.6.26/PKG-INFO +0 -173
  50. hypercli_sdk-2026.6.26/README.md +0 -142
  51. hypercli_sdk-2026.6.26/hypercli/agents.py +0 -1949
  52. hypercli_sdk-2026.6.26/hypercli/openclaw/__init__.py +0 -25
  53. hypercli_sdk-2026.6.26/hypercli/voice.py +0 -102
  54. hypercli_sdk-2026.6.26/tests/test_agents.py +0 -1426
  55. hypercli_sdk-2026.6.26/tests/test_bootstrap_dev_test_keys.py +0 -104
  56. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/_compat.py +0 -0
  57. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/files.py +0 -0
  58. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/gateway.py +0 -0
  59. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/instances.py +0 -0
  60. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/job/__init__.py +0 -0
  61. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/job/comfyui.py +0 -0
  62. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/job/gradio.py +0 -0
  63. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/jobs.py +0 -0
  64. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/logs.py +0 -0
  65. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/models.py +0 -0
  66. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/renders.py +4 -4
  67. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/shell.py +0 -0
  68. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/hypercli/x402.py +0 -0
  69. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/integration/test_billing.py +0 -0
  70. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/integration/test_instances.py +0 -0
  71. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/integration/test_jobs_dryrun.py +0 -0
  72. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/integration/test_keys.py +0 -0
  73. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/integration/test_renders.py +0 -0
  74. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_apply_params.py +0 -0
  75. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_gateway_retry.py +0 -0
  76. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_graph_to_api.py +0 -0
  77. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_models.py +0 -0
  78. {hypercli_sdk-2026.6.26 → hypercli_sdk-2026.8.11}/tests/test_status.py +0 -0
@@ -20,6 +20,7 @@ ENV/
20
20
  out
21
21
  dist
22
22
  build
23
+ target/
23
24
 
24
25
  # Testing
25
26
  coverage
@@ -28,6 +29,10 @@ coverage
28
29
  .DS_Store
29
30
  .agents/
30
31
  .opencode/
32
+ .github/
33
+ /skills/
34
+ /skills-lock.json
35
+ docs/agent-display-names.md
31
36
  *.pem
32
37
 
33
38
  # IDE
@@ -0,0 +1,426 @@
1
+ Metadata-Version: 2.5
2
+ Name: hypercli-sdk
3
+ Version: 2026.8.11
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="hyper_api_agent_key")
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
+ Plan IDs are open strings on the wire so future and historical plans continue
144
+ to parse. `plan.canonical_id` recognizes the current `solo`, `team`, and `pro`
145
+ IDs. Plan access comes from the HyperClaw summary, including direct grants:
146
+
147
+ ```python
148
+ summary = sdk.agent.subscription_summary()
149
+ if summary.has_active_plan: # active subscription OR direct entitlement
150
+ print(summary.agent_slots)
151
+ ```
152
+
153
+ Do not substitute the Orchestra `/api/auth/me` subscription flag. If the
154
+ summary request returns `401` or `403`, the selected key cannot establish plan
155
+ state; treat it as unknown rather than as no plan.
156
+
157
+ ## OpenClaw Agents
158
+
159
+ OpenClaw uses the generic deployment launch surface. `registry_url`, `registry_auth`, and `sync_root` are generic deployment options. A nonblank `sync_root` enables Reef persistence; no separate `sync_enabled` field is serialized. The OpenClaw helpers add defaults such as routes, image, and `sync_root=/home/node`.
160
+
161
+ ```python
162
+ agent = client.deployments.create_openclaw(
163
+ name="docs-demo",
164
+ start=True,
165
+ registry_url="git.nedos.co",
166
+ registry_auth={"username": "ci", "password": "token"},
167
+ )
168
+ agent = client.deployments.wait_running(agent.id, timeout=300)
169
+
170
+ capacity = client.deployments.list_with_capacity()
171
+ print(capacity.max_agents_per_account, capacity.running_agents)
172
+ for slot in capacity.agent_slots:
173
+ print(slot.size, slot.plan_id, slot.agent_id)
174
+ ```
175
+
176
+ `list()` remains the compatibility list of agents. `list_with_capacity()`
177
+ preserves the full deployment envelope: saved/running account limits, pooled
178
+ TPD, aggregate slot inventory, and individual entitlement-backed agent slots.
179
+
180
+ For a long-lived UI, subscribe to thin invalidations and refresh REST in the
181
+ handler:
182
+
183
+ ```python
184
+ import asyncio
185
+
186
+ async def changed(_event):
187
+ agents = await asyncio.to_thread(client.deployments.list)
188
+ render(agents)
189
+
190
+ async def snapshot():
191
+ agents = await asyncio.to_thread(client.deployments.list)
192
+ render(agents)
193
+
194
+ stop = asyncio.Event()
195
+ await client.deployments.subscribe(changed, stop_event=stop, on_ready=snapshot)
196
+ ```
197
+
198
+ The `on_ready` callback runs after user-stream authentication and before event
199
+ frames are read; it repeats after reconnect so no transition can slip
200
+ between snapshot and subscription. Transition events carry `agent_id` for
201
+ local filtering plus `state`, `reason`, `error`, and `message`, but are not
202
+ resource snapshots and may be duplicated or coalesced; refresh REST for
203
+ authority.
204
+
205
+ Managed-agent lifecycle snapshots currently use `CREATING`, `STARTING`,
206
+ `RESTORING`, `RUNNING`, `STOPPING`, `STOPPED`, `ARCHIVING`, `ARCHIVED`,
207
+ `FAILED`, and `DELETED`. `CREATING` is fresh admission, `STARTING` resumes a
208
+ warm retained agent, and `RESTORING` hydrates a cold agent from its exact
209
+ archive checkpoint. `STOPPED` retains warm local storage. `ARCHIVING` is the
210
+ public transition to verified cold storage.
211
+ `ARCHIVED` is the Backend-persisted cold-restorable terminal projection after
212
+ Lagoon drops its agent task, namespace, PVC, and local S3 copy. `DELETED` is a
213
+ Backend-only terminal state and normally hidden from user lists. State values
214
+ remain open strings; use REST as authority instead of
215
+ recreating the server lifecycle machine in the client. Each snapshot may also
216
+ carry open-string diagnostics: `reason` is the stable cause such as `start`,
217
+ `api_stop`, `runtime_exit`, `timeout`, or `delete`, `error` is a failure code
218
+ when the transition failed, and `message` is human-readable context.
219
+
220
+ 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`.
221
+
222
+ `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"}`.
223
+
224
+ 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}`.
225
+
226
+ ## Hosted Coding Agents
227
+
228
+ Native Buzz Agent, OpenCode, Codex, Claude Code, Goose, and Kimi Code use
229
+ canonical managed-runtime images.
230
+ They have no public runtime port: lifecycle, exec, shell, workspace sync, and
231
+ authentication all use the existing authenticated deployment APIs. OpenCode
232
+ and Goose default to HyperCLI's Anthropic-native `kimi-k2.6-anthropic` route.
233
+ Kimi Code keeps Moonshot's upstream device login and service.
234
+ Claude Code, Codex, and Kimi Code are native-login-first. For Buzz-managed
235
+ launches, `HYPERCLI_RUNTIME_INFERENCE=hypercli` is an explicit compatibility
236
+ switch for Claude and Kimi. The gateway now exposes `/v1/responses`, and Buzz
237
+ compatibility mode renders Codex with `wire_api="responses"`; a successful
238
+ HyperCLI-model Codex Responses E2E remains unvalidated, so that path is not yet
239
+ advertised as supported. See the
240
+ [runtime and persistence matrix](../docs/agents/coding-runtimes.mdx).
241
+
242
+ ```python
243
+ buzz_agent = client.deployments.create_buzz_agent(name="buzz-agent")
244
+ agent = client.deployments.create_opencode(name="opencode")
245
+ codex = client.deployments.create_codex(name="codex")
246
+ claude = client.deployments.create_claude_code(name="claude")
247
+ goose = client.deployments.create_goose(name="goose")
248
+ kimi = client.deployments.create_kimi_code(name="kimi")
249
+
250
+ methods = codex.auth.methods()
251
+ status = codex.auth.status()
252
+
253
+ async with await codex.auth.login("device") as login:
254
+ print(login.verification_url, login.user_code)
255
+ await login.wait()
256
+ ```
257
+
258
+ The login helper opens a short-lived, agent-bound shell WebSocket and runs the
259
+ runtime's native login command inside the managed runtime. It never puts an API key on
260
+ the command line. Runtime credentials and state live under the persistent
261
+ `/home/node` sync root.
262
+
263
+ Authentication is runtime-specific rather than one universal login protocol.
264
+ Native Buzz Agent has no separate login step and uses its injected model and
265
+ provider configuration. OpenCode combines adapter discovery with its
266
+ interactive provider login; Codex
267
+ adds native device login; Claude Code exposes Claude.ai, Console, and SSO;
268
+ Goose uses its injected deployment credential; and Kimi Code uses the
269
+ upstream adapter's methods. Goose and Kimi Code do not expose a noninteractive
270
+ logout command through this SDK surface.
271
+
272
+ The images default to a long-lived direct shell/exec container. A Buzz provider
273
+ launches one for a Buzz-managed identity with the typed launch contract:
274
+
275
+ ```python
276
+ from hypercli import BuzzLaunchConfig
277
+
278
+ agent = client.deployments.create_opencode(
279
+ name="buzz-opencode",
280
+ buzz=BuzzLaunchConfig(
281
+ private_key_nsec=agent_nsec,
282
+ relay_url=relay_url,
283
+ auth_tag=owner_signed_auth_tag,
284
+ parallelism=1,
285
+ ),
286
+ )
287
+ ```
288
+
289
+ The managed platform injects an agent-scoped `HYPER_AGENTS_API_KEY` into the
290
+ runtime. Do not copy an account API key into the launch environment.
291
+
292
+ The SDK selects `/usr/local/bin/buzz-acp`, the runtime-specific child ACP
293
+ command and arguments, the hosted Buzz MCP command, lazy pool creation, relay
294
+ observation, and persistent `/home/node` settings. `/home/node/shared`
295
+ remains reserved for Workspace projections; the specialized image reconciles
296
+ the Buzz nest after the home mount and runs the harness from
297
+ `/home/node/.buzz`. OpenCode and Codex read its canonical `AGENTS.md`, while
298
+ Claude Code receives `CLAUDE.md -> AGENTS.md`. `base_prompt.md` stays compiled
299
+ into `buzz-acp`. Buzz-reserved environment keys are rendered from the typed
300
+ object after caller environment values.
301
+ `buzz_enabled=True` remains as a deprecated raw-environment compatibility path.
302
+ Typed and compatibility Buzz launches select the matching `hypercli-buzz`
303
+ image family (`buzz-agent`, `opencode`, `codex`, `claude`, `goose`, or
304
+ `kimi-code`) by
305
+ default. Ordinary coding-agent helpers without Buzz keep the generic
306
+ `ghcr.io/hypercli/hypercli-<runtime>:latest` default, except native Buzz Agent,
307
+ whose runtime image is already `hypercli-buzz-agent`. An explicit `image=`
308
+ continues to override either default.
309
+
310
+ Direct `BuzzLaunchConfig` renders timeout and response-policy values but does
311
+ not duplicate the stock Desktop provider's validation; invalid combinations
312
+ are rejected later by `buzz-acp`. The Desktop provider also maps structured
313
+ Goose model/provider fields to `GOOSE_MODEL`/`GOOSE_PROVIDER`; direct Python
314
+ SDK callers must set any Goose-specific environment themselves.
315
+
316
+ Buzz launches leave size unset for live backend/provider slot selection;
317
+ ordinary coding-agent helpers preserve a caller-provided size or the backend
318
+ default. Stock Buzz provider agents do not
319
+ start on app launch and the current provider protocol has no stop callback.
320
+ Editing a running agent does not replace its HyperCLI launch environment: stop
321
+ the deployment through the authenticated HyperCLI API and deploy it again from
322
+ Buzz to apply changes. A successfully delivered and accepted `!shutdown` can
323
+ exit a new `restart=False` launch; the hosted terminal-state observer then
324
+ reports `stopping`, completes runtime cleanup, marks the deployment `stopped`,
325
+ and releases its slot. Desktop receives no provider acknowledgement and keeps
326
+ its local deployed record.
327
+
328
+ Stock Buzz expects ACP NDJSON. It skips non-JSON child stdout, and
329
+ `agent_message_chunk` is activity telemetry rather than a channel reply. There
330
+ is no plaintext fallback; a visible reply requires the agent to invoke the Buzz
331
+ send command/tool. The six-runtime SDK coverage validates request rendering,
332
+ not live launches.
333
+
334
+ The agent nsec and caller environment become raw deployment environment values.
335
+ The HyperClaw backend currently persists them in `Agent.launch_config`, and
336
+ authenticated deployment read, environment, or exec surfaces may expose them.
337
+ The default `RUST_LOG` filter disables `acp::stream` content logging; overriding
338
+ it can expose generated text in container logs.
339
+
340
+ ## OpenClaw Node Egress
341
+
342
+ The Python SDK includes an experimental reference implementation for user-owned
343
+ node egress in `hypercli.openclaw.node_proxy`. It uses the existing OpenClaw
344
+ node model:
345
+
346
+ - a node connects to the gateway with `role="node"`
347
+ - the node declares explicit `egress.*` command names during the connect
348
+ handshake
349
+ - an operator/client calls `GatewayClient.node_invoke(node_id, command, params)`
350
+ - the gateway sends one `node.invoke.request` and waits for one
351
+ `node.invoke.result`
352
+
353
+ This is not raw sockets over the gateway. It is node RPC with chunked payloads
354
+ and gateway policy approval.
355
+
356
+ Node side:
357
+
358
+ ```python
359
+ from hypercli.openclaw import NodeEgressServer
360
+
361
+ node = NodeEgressServer(
362
+ "wss://my-agent.hypercli.app",
363
+ "home-linux-egress",
364
+ gateway_token="...",
365
+ )
366
+
367
+ await node.connect()
368
+ ```
369
+
370
+ Operator side:
371
+
372
+ ```python
373
+ from hypercli.openclaw import EGRESS_COMMANDS, NodeEgressClient
374
+
375
+ egress = NodeEgressClient(gateway, node_id="home-linux-egress")
376
+ res = await egress.http_fetch("https://example.com/")
377
+ ```
378
+
379
+ Commands:
380
+
381
+ - `egress.http.fetch`: bounded HTTP(S) fetch, response body returned as base64
382
+ chunks
383
+ - `egress.tcp.open/read/write/close`: experimental TCP tunnel primitives used
384
+ by `LoopbackNodeProxy` for HTTP `CONNECT`
385
+
386
+ Security defaults:
387
+
388
+ - local proxy binds to `127.0.0.1` by default
389
+ - node id is explicit; no automatic node selection
390
+ - RFC1918/private, loopback, link-local, multicast, reserved, and metadata IPs
391
+ are blocked by default unless explicitly allowed on the node
392
+ - chunks are small and bounded; responses are not returned as one unbounded
393
+ base64 blob
394
+
395
+ Pairing and policy:
396
+
397
+ - the node must be device-paired
398
+ - the node command surface must be approved
399
+ - custom `egress.*` commands may need `gateway.nodes.allowCommands`
400
+
401
+ Python/Linux is first because it is easiest to test in CI and the Python SDK
402
+ already ships `NodeServer`. The portable contract is the command surface and
403
+ payload shape, not the Python implementation. macOS Backseat Driver already
404
+ proves the native node-host precedent; Android should eventually gain Kotlin
405
+ `NodeRuntime` parity; the TS SDK can mirror operator/client types if useful.
406
+
407
+ `LoopbackNodeProxy` can relay absolute-form HTTP requests and has experimental
408
+ `CONNECT` support over polling/chunked `node.invoke`. Treat CONNECT as a
409
+ feasibility prototype, not production-grade streaming.
410
+
411
+ ## Error Handling
412
+
413
+ ```python
414
+ from hypercli import HyperCLI, APIError
415
+
416
+ client = HyperCLI()
417
+
418
+ try:
419
+ job = client.jobs.get("invalid_id")
420
+ except APIError as e:
421
+ print(f"Error {e.status_code}: {e.detail}")
422
+ ```
423
+
424
+ ## License
425
+
426
+ MIT