hypercli-cli 0.8.11__tar.gz → 0.8.12__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypercli-cli
3
- Version: 0.8.11
3
+ Version: 0.8.12
4
4
  Summary: CLI for HyperCLI - GPU orchestration and LLM API
5
5
  Project-URL: Homepage, https://hypercli.com
6
6
  Project-URL: Documentation, https://docs.hypercli.com
@@ -1,5 +1,7 @@
1
1
  """hyper flow commands - simplified flow interfaces"""
2
+ import json
2
3
  import math
4
+ from datetime import datetime, timezone
3
5
  from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
4
6
  import typer
5
7
  from pathlib import Path
@@ -8,6 +10,8 @@ from typing import Any, Optional, List
8
10
  from hypercli import HyperCLI, X402Client
9
11
  from .output import output, console, spinner
10
12
 
13
+ X402_RENDERS_FILE = Path.home() / ".hypercli" / "x402_renders.jsonl"
14
+
11
15
  HUMO_FPS = 25 # HuMo video_humo template frame rate
12
16
  USDC_ATOMIC_UNITS = Decimal("1000000")
13
17
 
@@ -52,6 +56,25 @@ def _usd_to_atomic(amount_usd: float) -> int:
52
56
  return int((usd * USDC_ATOMIC_UNITS).to_integral_value(rounding=ROUND_HALF_UP))
53
57
 
54
58
 
59
+ def _save_x402_render(flow_type: str, amount: float, x402_result) -> None:
60
+ """Append x402 render info to ~/.hypercli/x402_renders.jsonl"""
61
+ try:
62
+ X402_RENDERS_FILE.parent.mkdir(parents=True, exist_ok=True)
63
+ entry = {
64
+ "ts": datetime.now(timezone.utc).isoformat(),
65
+ "flow_type": flow_type,
66
+ "render_id": x402_result.render.render_id,
67
+ "amount_usd": amount,
68
+ "access_key": x402_result.access_key,
69
+ "status_url": x402_result.status_url,
70
+ "cancel_url": x402_result.cancel_url,
71
+ }
72
+ with open(X402_RENDERS_FILE, "a") as f:
73
+ f.write(json.dumps(entry) + "\n")
74
+ except Exception:
75
+ pass # best-effort, don't break the flow
76
+
77
+
55
78
  def _create_flow_render(
56
79
  flow_type: str,
57
80
  payload: dict[str, Any],
@@ -90,6 +113,7 @@ def _create_flow_render(
90
113
  params=clean_payload,
91
114
  notify_url=notify_url,
92
115
  )
116
+ _save_x402_render(flow_type, amount, x402_result)
93
117
  return x402_result.render, x402_result
94
118
 
95
119
  with spinner("Creating render..."):
@@ -139,6 +163,78 @@ OPT_AMOUNT = typer.Option(None, "--amount", help="USDC amount to spend with --x4
139
163
  OPT_FMT = typer.Option("table", "--output", "-o", help="Output format: table|json")
140
164
 
141
165
 
166
+ @app.command("renders")
167
+ def list_renders(
168
+ limit: int = typer.Option(10, "--limit", "-n", help="Number of recent renders to show"),
169
+ check: bool = typer.Option(False, "--check", "-c", help="Check current status of each render"),
170
+ fmt: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
171
+ ):
172
+ """List saved x402 render history from ~/.hypercli/x402_renders.jsonl"""
173
+ if not X402_RENDERS_FILE.exists():
174
+ console.print("[dim]No x402 renders recorded yet.[/dim]")
175
+ raise typer.Exit(0)
176
+
177
+ entries = []
178
+ with open(X402_RENDERS_FILE) as f:
179
+ for line in f:
180
+ line = line.strip()
181
+ if line:
182
+ try:
183
+ entries.append(json.loads(line))
184
+ except json.JSONDecodeError:
185
+ pass
186
+
187
+ if not entries:
188
+ console.print("[dim]No x402 renders recorded yet.[/dim]")
189
+ raise typer.Exit(0)
190
+
191
+ entries = entries[-limit:]
192
+
193
+ if check:
194
+ import httpx
195
+ for entry in entries:
196
+ access_key = entry.get("access_key", "")
197
+ status_path = entry.get("status_url", "")
198
+ if access_key and status_path:
199
+ try:
200
+ url = f"https://api.hypercli.com/api{status_path}" if status_path.startswith("/flow") else f"https://api.hypercli.com{status_path}"
201
+ resp = httpx.get(url, headers={"Authorization": f"Bearer {access_key}"}, timeout=10)
202
+ if resp.status_code == 200:
203
+ data = resp.json()
204
+ entry["live_state"] = data.get("state", "?")
205
+ entry["result_url"] = data.get("result_url")
206
+ entry["error"] = data.get("error")
207
+ except Exception:
208
+ entry["live_state"] = "error"
209
+
210
+ if fmt == "json":
211
+ output(entries, "json")
212
+ return
213
+
214
+ from rich.table import Table
215
+ table = Table(title="x402 Renders")
216
+ table.add_column("Time", style="dim")
217
+ table.add_column("Flow")
218
+ table.add_column("Render ID", style="cyan")
219
+ table.add_column("USD", justify="right")
220
+ if check:
221
+ table.add_column("State")
222
+ table.add_column("Result")
223
+
224
+ for e in entries:
225
+ ts = e.get("ts", "?")[:19].replace("T", " ")
226
+ row = [ts, e.get("flow_type", "?"), e.get("render_id", "?")[:13] + "…", f"${e.get('amount_usd', 0):.2f}"]
227
+ if check:
228
+ state = e.get("live_state", "?")
229
+ style = "green" if state == "completed" else "red" if state == "failed" else "yellow"
230
+ row.append(f"[{style}]{state}[/{style}]")
231
+ result = e.get("result_url") or e.get("error") or ""
232
+ row.append(result[:60] if result else "")
233
+ table.add_row(*row)
234
+
235
+ console.print(table)
236
+
237
+
142
238
  @app.command("text-to-image")
143
239
  def text_to_image(
144
240
  prompt: str = typer.Argument(..., help="Text description of the image"),
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hypercli-cli"
7
- version = "0.8.11"
7
+ version = "0.8.12"
8
8
  description = "CLI for HyperCLI - GPU orchestration and LLM API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes