hypercli-cli 0.8.0__tar.gz → 0.8.2__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.0
3
+ Version: 0.8.2
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
@@ -21,7 +21,7 @@ Requires-Dist: argon2-cffi>=25.0.0; extra == 'all'
21
21
  Requires-Dist: eth-account>=0.13.0; extra == 'all'
22
22
  Requires-Dist: hypercli-sdk[comfyui]>=0.7.1; extra == 'all'
23
23
  Requires-Dist: web3>=7.0.0; extra == 'all'
24
- Requires-Dist: x402[httpx]>=2.0.0; extra == 'all'
24
+ Requires-Dist: x402[evm,httpx]>=2.0.0; extra == 'all'
25
25
  Provides-Extra: comfyui
26
26
  Requires-Dist: hypercli-sdk[comfyui]>=0.7.1; extra == 'comfyui'
27
27
  Provides-Extra: dev
@@ -32,7 +32,7 @@ Requires-Dist: argon2-cffi>=25.0.0; extra == 'wallet'
32
32
  Requires-Dist: eth-account>=0.13.0; extra == 'wallet'
33
33
  Requires-Dist: qrcode[pil]>=7.4.0; extra == 'wallet'
34
34
  Requires-Dist: web3>=7.0.0; extra == 'wallet'
35
- Requires-Dist: x402[httpx]>=2.0.0; extra == 'wallet'
35
+ Requires-Dist: x402[evm,httpx]>=2.0.0; extra == 'wallet'
36
36
  Description-Content-Type: text/markdown
37
37
 
38
38
  # HyperCLI CLI
@@ -320,7 +320,6 @@ def fetch_models(api_key: str, api_base: str = PROD_API_BASE) -> list[dict]:
320
320
  "reasoning": False,
321
321
  "input": ["text"],
322
322
  "contextWindow": 200000,
323
- "maxTokens": 8192,
324
323
  }
325
324
  for m in data
326
325
  if m.get("id")
@@ -335,7 +334,6 @@ def fetch_models(api_key: str, api_base: str = PROD_API_BASE) -> list[dict]:
335
334
  "reasoning": False,
336
335
  "input": ["text"],
337
336
  "contextWindow": 200000,
338
- "maxTokens": 8192,
339
337
  },
340
338
  ]
341
339
 
@@ -240,6 +240,175 @@ def balance():
240
240
  console.print(f"\n[green]✓[/green] You have [bold]{balance_usdc:.2f} USDC[/bold]")
241
241
 
242
242
 
243
+ @app.command("topup")
244
+ def topup(
245
+ amount: str = typer.Argument(help="Amount in USDC to top up (max 6 decimals)"),
246
+ api_url: str = typer.Option(None, help="API URL override"),
247
+ ):
248
+ """Top up account balance via Orchestra x402 endpoint.
249
+
250
+ Flow:
251
+ 1) Resolve user_id via GET /api/user using your existing API key
252
+ 2) POST /api/x402/top_up with {user_id, amount}
253
+ 3) Handle 402 and retry with x402 payment headers
254
+
255
+ Examples:
256
+ hyper wallet topup 10
257
+ hyper wallet topup 25.50
258
+ """
259
+ require_wallet_deps()
260
+ from decimal import Decimal, InvalidOperation
261
+ import httpx
262
+ from hypercli.config import get_api_key, get_api_url
263
+
264
+ try:
265
+ from x402 import x402ClientSync
266
+ from x402.http import x402HTTPClientSync
267
+ from x402.mechanisms.evm import EthAccountSigner
268
+ from x402.mechanisms.evm.exact.register import register_exact_evm_client
269
+ except ImportError:
270
+ console.print("[red]❌ x402 payment requires wallet/x402 dependencies[/red]")
271
+ console.print("\nInstall with:")
272
+ console.print(" [bold]pip install 'hypercli-cli[wallet]'[/bold]")
273
+ raise typer.Exit(1)
274
+
275
+ try:
276
+ amount_dec = Decimal(amount)
277
+ except InvalidOperation:
278
+ console.print(f"[red]❌ Invalid amount: {amount}[/red]")
279
+ raise typer.Exit(1)
280
+
281
+ if amount_dec <= 0:
282
+ console.print("[red]❌ Amount must be greater than 0[/red]")
283
+ raise typer.Exit(1)
284
+ if amount_dec.as_tuple().exponent < -6:
285
+ console.print("[red]❌ Amount supports at most 6 decimals[/red]")
286
+ raise typer.Exit(1)
287
+
288
+ amount_atomic = int(amount_dec * Decimal("1000000"))
289
+
290
+ # Step 1: Load wallet
291
+ account = load_wallet()
292
+ console.print(f"[green]✓[/green] Wallet: {account.address}")
293
+
294
+ # Step 2: Check USDC balance
295
+ w3 = Web3(Web3.HTTPProvider(BASE_RPC))
296
+ usdc_abi = [
297
+ {
298
+ "constant": True,
299
+ "inputs": [{"name": "_owner", "type": "address"}],
300
+ "name": "balanceOf",
301
+ "outputs": [{"name": "balance", "type": "uint256"}],
302
+ "type": "function",
303
+ }
304
+ ]
305
+ usdc = w3.eth.contract(address=USDC_CONTRACT, abi=usdc_abi)
306
+ balance_raw = usdc.functions.balanceOf(account.address).call()
307
+ balance_usdc = Decimal(balance_raw) / Decimal("1000000")
308
+
309
+ console.print(f"[green]✓[/green] Balance: {balance_usdc:.6f} USDC")
310
+
311
+ if balance_raw < amount_atomic:
312
+ console.print(
313
+ f"\n[red]❌ Insufficient balance: {balance_usdc:.6f} < {amount_dec:.6f} USDC[/red]"
314
+ )
315
+ console.print(f"Send USDC on Base to: [bold]{account.address}[/bold]")
316
+ raise typer.Exit(1)
317
+
318
+ # Step 3: Set up x402 v2 client
319
+ api_key = get_api_key()
320
+ if not api_key:
321
+ console.print("[red]❌ API key required for top-up[/red]")
322
+ console.print(
323
+ "Set it with: [bold]hyper configure[/bold] or [bold]hyper wallet login[/bold]"
324
+ )
325
+ raise typer.Exit(1)
326
+
327
+ base_url = (api_url or get_api_url()).rstrip("/")
328
+ user_endpoint = f"{base_url}/api/user"
329
+ topup_endpoint = f"{base_url}/api/x402/top_up"
330
+ auth_headers = {
331
+ "Authorization": f"Bearer {api_key}",
332
+ "Content-Type": "application/json",
333
+ }
334
+
335
+ signer = EthAccountSigner(account)
336
+ x402_client = x402ClientSync()
337
+ register_exact_evm_client(x402_client, signer)
338
+ http_client = x402HTTPClientSync(x402_client)
339
+
340
+ with httpx.Client(timeout=30) as client:
341
+ # Step 4: Resolve user_id from API key
342
+ console.print("\n[bold]Resolving user...[/bold]")
343
+ user_resp = client.get(user_endpoint, headers=auth_headers)
344
+ if user_resp.status_code != 200:
345
+ console.print(
346
+ f"[red]❌ Failed to get user: {user_resp.status_code} {user_resp.text}[/red]"
347
+ )
348
+ raise typer.Exit(1)
349
+
350
+ user_id = user_resp.json().get("user_id")
351
+ if not user_id:
352
+ console.print("[red]❌ /api/user response missing user_id[/red]")
353
+ raise typer.Exit(1)
354
+
355
+ payload = {"user_id": user_id, "amount": float(amount_dec)}
356
+
357
+ # Step 5: Request top-up (expect 402 then retry with payment)
358
+ console.print(f"[bold]Requesting top-up of ${amount_dec:.2f}...[/bold]")
359
+ resp = client.post(topup_endpoint, headers=auth_headers, json=payload)
360
+
361
+ if resp.status_code == 402:
362
+ console.print("[bold]Signing x402 payment...[/bold]")
363
+ try:
364
+ payment_headers, _ = http_client.handle_402_response(
365
+ dict(resp.headers), resp.content
366
+ )
367
+ except Exception as e:
368
+ console.print(f"[red]❌ Failed to build payment header: {e}[/red]")
369
+ raise typer.Exit(1)
370
+
371
+ console.print("[bold]Submitting payment...[/bold]")
372
+ retry_headers = {**auth_headers, **payment_headers}
373
+ retry_headers["Access-Control-Expose-Headers"] = "PAYMENT-RESPONSE,X-PAYMENT-RESPONSE"
374
+ resp = client.post(topup_endpoint, headers=retry_headers, json=payload)
375
+
376
+ if resp.status_code != 200:
377
+ console.print(f"[red]❌ Top-up failed: {resp.status_code} {resp.text}[/red]")
378
+ try:
379
+ settle = http_client.get_payment_settle_response(lambda name: resp.headers.get(name))
380
+ if getattr(settle, "error_reason", None):
381
+ console.print(f"[red]❌ Payment error: {settle.error_reason}[/red]")
382
+ except Exception:
383
+ pass
384
+ raise typer.Exit(1)
385
+
386
+ result = resp.json()
387
+
388
+ credited = result.get("amount", float(amount_dec))
389
+ wallet = result.get("wallet", account.address)
390
+ tx_id = result.get("transaction_id", "")
391
+ msg = result.get("message", "Top-up successful")
392
+
393
+ console.print(f"\n[bold green]✓ Top-up successful![/bold green]\n")
394
+ console.print(f" User: {result.get('user_id', 'N/A')}")
395
+ console.print(f" Credited: ${credited} USDC")
396
+ console.print(f" Wallet: {wallet}")
397
+ console.print(f" Tx ID: {tx_id or 'N/A'}")
398
+ console.print(f" Message: {msg}")
399
+
400
+ try:
401
+ settle = http_client.get_payment_settle_response(lambda name: resp.headers.get(name))
402
+ if getattr(settle, "transaction", None):
403
+ console.print(f" On-chain: {settle.transaction}")
404
+ if getattr(settle, "network", None):
405
+ console.print(f" Network: {settle.network}")
406
+ except Exception:
407
+ pass
408
+
409
+ console.print()
410
+
411
+
243
412
  @app.command("login")
244
413
  def wallet_login(
245
414
  name: str = typer.Option("cli", help="Name for the generated API key"),
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hypercli-cli"
7
- version = "0.8.0"
7
+ version = "0.8.2"
8
8
  description = "CLI for HyperCLI - GPU orchestration and LLM API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -28,7 +28,7 @@ comfyui = [
28
28
  "hypercli-sdk[comfyui]>=0.7.1",
29
29
  ]
30
30
  wallet = [
31
- "x402[httpx]>=2.0.0",
31
+ "x402[httpx,evm]>=2.0.0",
32
32
  "eth-account>=0.13.0",
33
33
  "web3>=7.0.0",
34
34
  "argon2-cffi>=25.0.0",
@@ -36,7 +36,7 @@ wallet = [
36
36
  ]
37
37
  all = [
38
38
  "hypercli-sdk[comfyui]>=0.7.1",
39
- "x402[httpx]>=2.0.0",
39
+ "x402[httpx,evm]>=2.0.0",
40
40
  "eth-account>=0.13.0",
41
41
  "web3>=7.0.0",
42
42
  "argon2-cffi>=25.0.0",
File without changes
File without changes