hypercli-cli 2026.4.6__tar.gz → 2026.4.9__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.
Files changed (39) hide show
  1. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/PKG-INFO +4 -4
  2. hypercli_cli-2026.4.9/hypercli_cli/__init__.py +1 -0
  3. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/agent.py +124 -21
  4. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/cli.py +2 -1
  5. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/flow.py +3 -3
  6. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/llm.py +1 -1
  7. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/stt.py +3 -3
  8. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/voice.py +28 -7
  9. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/pyproject.toml +4 -4
  10. hypercli_cli-2026.4.9/tests/test_flow_visibility.py +17 -0
  11. hypercli_cli-2026.4.9/tests/test_voice_command.py +72 -0
  12. hypercli_cli-2026.4.6/hypercli_cli/__init__.py +0 -1
  13. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/.gitignore +0 -0
  14. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/README.md +0 -0
  15. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/agents.py +0 -0
  16. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/billing.py +0 -0
  17. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/comfyui.py +0 -0
  18. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/embed.py +0 -0
  19. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/files.py +0 -0
  20. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/instances.py +0 -0
  21. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/jobs.py +0 -0
  22. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/keys.py +0 -0
  23. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/onboard.py +0 -0
  24. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/output.py +0 -0
  25. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/renders.py +0 -0
  26. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/tui/__init__.py +0 -0
  27. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/tui/job_monitor.py +0 -0
  28. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/user.py +0 -0
  29. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/wallet.py +0 -0
  30. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_agent_env_resolution.py +0 -0
  31. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_config_command.py +0 -0
  32. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_exec_shell_dryrun.py +0 -0
  33. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_flow_command.py +0 -0
  34. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_jobs_list_tags.py +0 -0
  35. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_llm_command.py +0 -0
  36. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_me_command.py +0 -0
  37. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_openclaw_config.py +0 -0
  38. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_wallet_command.py +0 -0
  39. {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_wallet_migration_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypercli-cli
3
- Version: 2026.4.6
3
+ Version: 2026.4.9
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
@@ -9,7 +9,7 @@ Author-email: HyperCLI <support@hypercli.com>
9
9
  License: MIT
10
10
  Requires-Python: >=3.10
11
11
  Requires-Dist: httpx>=0.27.0
12
- Requires-Dist: hypercli-sdk>=2026.4.6
12
+ Requires-Dist: hypercli-sdk>=2026.4.9
13
13
  Requires-Dist: mutagen>=1.47.0
14
14
  Requires-Dist: openai>=2.8.1
15
15
  Requires-Dist: pyyaml>=6.0
@@ -20,11 +20,11 @@ Provides-Extra: all
20
20
  Requires-Dist: argon2-cffi>=25.0.0; extra == 'all'
21
21
  Requires-Dist: eth-account>=0.13.0; extra == 'all'
22
22
  Requires-Dist: faster-whisper>=1.1.0; extra == 'all'
23
- Requires-Dist: hypercli-sdk[comfyui]>=2026.4.6; extra == 'all'
23
+ Requires-Dist: hypercli-sdk[comfyui]>=2026.4.9; extra == 'all'
24
24
  Requires-Dist: web3>=7.0.0; extra == 'all'
25
25
  Requires-Dist: x402[evm,httpx]>=2.0.0; extra == 'all'
26
26
  Provides-Extra: comfyui
27
- Requires-Dist: hypercli-sdk[comfyui]>=2026.4.6; extra == 'comfyui'
27
+ Requires-Dist: hypercli-sdk[comfyui]>=2026.4.9; extra == 'comfyui'
28
28
  Provides-Extra: dev
29
29
  Requires-Dist: pytest>=8.0.0; extra == 'dev'
30
30
  Requires-Dist: ruff>=0.3.0; extra == 'dev'
@@ -0,0 +1 @@
1
+ __version__ = "2026.4.7"
@@ -10,9 +10,10 @@ import typer
10
10
  from rich.console import Console
11
11
  from rich.table import Table
12
12
 
13
+ from hypercli import HyperCLI
14
+
13
15
  from .onboard import onboard as _onboard_fn
14
16
  from .voice import app as voice_app
15
- from .stt import transcribe as _stt_transcribe
16
17
  from .embed import app as embed_app
17
18
 
18
19
  app = typer.Typer(help="HyperAgent inference commands")
@@ -23,26 +24,6 @@ app.command("onboard")(_onboard_fn)
23
24
  app.add_typer(voice_app, name="voice")
24
25
  app.add_typer(embed_app, name="embed")
25
26
 
26
-
27
- @app.command("transcribe")
28
- def transcribe(
29
- audio_file: Path = typer.Argument(..., help="Audio file to transcribe (wav, mp3, ogg, m4a, etc.)"),
30
- model: str = typer.Option("turbo", "--model", "-m", help="Whisper model: tiny, base, small, medium, large-v3, turbo"),
31
- language: str = typer.Option(None, "--language", "-l", help="Language code (e.g. en, de, fr). Auto-detect if omitted."),
32
- device: str = typer.Option("auto", "--device", "-d", help="Device: auto, cpu, cuda"),
33
- compute_type: str = typer.Option("auto", "--compute", help="Compute type: auto, int8, float16, float32"),
34
- json_output: bool = typer.Option(False, "--json", help="Output as JSON with timestamps"),
35
- output: Path = typer.Option(None, "--output", "-o", help="Write transcript to file"),
36
- ):
37
- """Transcribe audio to text using faster-whisper (runs locally).
38
-
39
- Examples:
40
- hyper agent transcribe voice.ogg
41
- hyper agent transcribe meeting.mp3 --model large-v3 --language en
42
- hyper agent transcribe audio.wav --json -o transcript.json
43
- """
44
- _stt_transcribe(audio_file, model, language, device, compute_type, json_output, output)
45
-
46
27
  # Check if wallet dependencies are available
47
28
  try:
48
29
  from x402 import x402Client
@@ -70,6 +51,30 @@ def require_x402_deps():
70
51
  raise typer.Exit(1)
71
52
 
72
53
 
54
+ def _resolve_agent_query_key() -> str:
55
+ key = os.getenv("HYPER_AGENTS_API_KEY", "").strip() or os.getenv("HYPER_API_KEY", "").strip()
56
+ if key:
57
+ return key
58
+ if AGENT_KEY_PATH.exists():
59
+ try:
60
+ with open(AGENT_KEY_PATH) as f:
61
+ data = json.load(f)
62
+ key = str(data.get("key") or "").strip()
63
+ if key:
64
+ return key
65
+ except Exception:
66
+ pass
67
+ console.print("[red]❌ No HyperClaw API key found.[/red]")
68
+ console.print("Set HYPER_AGENTS_API_KEY or HYPER_API_KEY, or subscribe first with [bold]hyper agent subscribe[/bold].")
69
+ raise typer.Exit(1)
70
+
71
+
72
+ def _get_agent_query_client(dev: bool) -> HyperCLI:
73
+ key = _resolve_agent_query_key()
74
+ api_base = DEV_API_BASE if dev else PROD_API_BASE
75
+ return HyperCLI(api_key=key, agent_api_key=key, api_url=api_base, agent_dev=dev)
76
+
77
+
73
78
  @app.command("subscribe")
74
79
  def subscribe(
75
80
  plan_id: str = typer.Argument("1aiu", help="Plan ID: 1aiu, 2aiu, 5aiu, 10aiu (default: 1aiu)"),
@@ -346,6 +351,104 @@ def plans(
346
351
  console.print("Subscribe with: [bold]hyper agent subscribe <plan_id> <amount>[/bold]")
347
352
 
348
353
 
354
+ @app.command("current-plan")
355
+ def current_plan(
356
+ dev: bool = typer.Option(False, "--dev", help="Use dev API"),
357
+ json_output: bool = typer.Option(False, "--json", help="Print raw JSON response"),
358
+ ):
359
+ """Show the effective current HyperClaw plan."""
360
+ client = _get_agent_query_client(dev)
361
+ current = client.agent.current_plan()
362
+
363
+ if json_output:
364
+ console.print_json(json.dumps(current.__dict__, indent=2, default=str))
365
+ return
366
+
367
+ console.print("\n[bold]Current HyperClaw Plan[/bold]\n")
368
+ console.print(f"Plan: [bold]{current.name}[/bold] ({current.id})")
369
+ console.print(f"TPM/RPM: {current.tpm_limit:,} / {current.rpm_limit:,}")
370
+ if current.pooled_tpd:
371
+ console.print(f"Pooled TPD: {current.pooled_tpd:,}")
372
+ if current.expires_at:
373
+ console.print(f"Expires: {current.expires_at.strftime('%Y-%m-%d %H:%M:%S %Z')}")
374
+ if current.provider:
375
+ console.print(f"Provider: {current.provider}")
376
+ console.print(f"Cancel at period end: {'yes' if current.cancel_at_period_end else 'no'}")
377
+ if current.slot_inventory:
378
+ console.print("\n[bold]Slots[/bold]")
379
+ for tier, inventory in current.slot_inventory.items():
380
+ console.print(
381
+ f" {tier}: {inventory.get('used', 0)}/{inventory.get('granted', 0)} "
382
+ f"({inventory.get('available', 0)} available)"
383
+ )
384
+
385
+
386
+ @app.command("subscriptions")
387
+ def subscriptions(
388
+ dev: bool = typer.Option(False, "--dev", help="Use dev API"),
389
+ json_output: bool = typer.Option(False, "--json", help="Print raw JSON response"),
390
+ ):
391
+ """List your HyperClaw subscriptions/entitlements."""
392
+ client = _get_agent_query_client(dev)
393
+ items = client.agent.subscriptions()
394
+
395
+ if json_output:
396
+ console.print_json(json.dumps([item.__dict__ for item in items], indent=2, default=str))
397
+ return
398
+
399
+ table = Table(title="HyperClaw Subscriptions")
400
+ table.add_column("ID", style="cyan")
401
+ table.add_column("Plan", style="green")
402
+ table.add_column("Qty", justify="right")
403
+ table.add_column("Provider")
404
+ table.add_column("Status")
405
+ table.add_column("Expires")
406
+ for item in items:
407
+ expires = item.expires_at.strftime("%Y-%m-%d %H:%M:%S %Z") if item.expires_at else ""
408
+ table.add_row(item.id[:12], item.plan_name or item.plan_id, str(item.quantity), item.provider, item.status, expires)
409
+ console.print()
410
+ console.print(table)
411
+
412
+
413
+ @app.command("subscription-summary")
414
+ def subscription_summary(
415
+ dev: bool = typer.Option(False, "--dev", help="Use dev API"),
416
+ json_output: bool = typer.Option(False, "--json", help="Print raw JSON response"),
417
+ ):
418
+ """Show your effective HyperClaw entitlement summary."""
419
+ client = _get_agent_query_client(dev)
420
+ summary = client.agent.subscription_summary()
421
+
422
+ if json_output:
423
+ console.print_json(json.dumps({
424
+ "effective_plan_id": summary.effective_plan_id,
425
+ "current_subscription_id": summary.current_subscription_id,
426
+ "pooled_tpm_limit": summary.pooled_tpm_limit,
427
+ "pooled_rpm_limit": summary.pooled_rpm_limit,
428
+ "pooled_tpd": summary.pooled_tpd,
429
+ "slot_inventory": summary.slot_inventory,
430
+ "active_subscription_count": summary.active_subscription_count,
431
+ "active_subscriptions": [item.__dict__ for item in summary.active_subscriptions],
432
+ "subscriptions": [item.__dict__ for item in summary.subscriptions],
433
+ "user": summary.user,
434
+ }, indent=2, default=str))
435
+ return
436
+
437
+ console.print("\n[bold]HyperClaw Entitlement Summary[/bold]\n")
438
+ console.print(f"Effective plan: [bold]{summary.effective_plan_id}[/bold]")
439
+ console.print(f"Current subscription: {summary.current_subscription_id or ''}")
440
+ console.print(
441
+ f"Pooled limits: {summary.pooled_tpm_limit:,} TPM / "
442
+ f"{summary.pooled_rpm_limit:,} RPM / {summary.pooled_tpd:,} TPD"
443
+ )
444
+ console.print("\n[bold]Slots[/bold]")
445
+ for tier, inventory in summary.slot_inventory.items():
446
+ console.print(
447
+ f" {tier}: {inventory.get('used', 0)}/{inventory.get('granted', 0)} "
448
+ f"({inventory.get('available', 0)} available)"
449
+ )
450
+
451
+
349
452
  @app.command("models")
350
453
  def models(
351
454
  dev: bool = typer.Option(False, "--dev", help="Use dev API"),
@@ -9,7 +9,7 @@ from rich.table import Table
9
9
  from hypercli import HyperCLI, APIError, configure
10
10
  from hypercli.config import CONFIG_FILE
11
11
 
12
- from . import agent, agents, billing, comfyui, files, flow, instances, jobs, keys, llm, user, wallet
12
+ from . import agent, agents, billing, comfyui, files, flow, instances, jobs, keys, llm, user, voice, wallet
13
13
  from .output import output, spinner
14
14
 
15
15
  console = Console()
@@ -76,6 +76,7 @@ app.add_typer(keys.app, name="keys")
76
76
  app.add_typer(jobs.app, name="jobs")
77
77
  app.add_typer(llm.app, name="llm")
78
78
  app.add_typer(user.app, name="user")
79
+ app.add_typer(voice.app, name="voice")
79
80
  app.add_typer(wallet.app, name="wallet")
80
81
 
81
82
 
@@ -420,7 +420,7 @@ def image_to_video(
420
420
  print_render_result(render, fmt, x402_result)
421
421
 
422
422
 
423
- @app.command("speaking-video")
423
+ @app.command("speaking-video", hidden=True)
424
424
  def speaking_video(
425
425
  prompt: str = typer.Argument(..., help="Scene description prompt"),
426
426
  image: Optional[str] = typer.Option(None, "--image", "-i", help="Portrait image URL or local file path"),
@@ -579,7 +579,7 @@ def first_last_frame_video(
579
579
  print_render_result(render, fmt, x402_result)
580
580
 
581
581
 
582
- @app.command("audio-to-text")
582
+ @app.command("audio-to-text", hidden=True)
583
583
  def audio_to_text(
584
584
  input: Optional[str] = typer.Argument(None, help="Audio URL or local file path"),
585
585
  file_id: Optional[str] = typer.Option(None, "--file-id", help="Pre-uploaded audio file ID"),
@@ -612,7 +612,7 @@ def audio_to_text(
612
612
  print_render_result(render, fmt, x402_result)
613
613
 
614
614
 
615
- @app.command("text-to-speech")
615
+ @app.command("text-to-speech", hidden=True)
616
616
  def text_to_speech(
617
617
  text: str = typer.Argument(..., help="Text to synthesize"),
618
618
  mode: str = typer.Option("design", "--mode", "-m", help="TTS mode: custom, design, or clone"),
@@ -99,7 +99,7 @@ def _get_openai_client(api_key: str, api_base: str):
99
99
  from openai import OpenAI
100
100
  except ImportError:
101
101
  console.print("[red]❌ The llm command requires the openai package.[/red]")
102
- console.print("Reinstall or upgrade with [bold]pip install 'hypercli-cli>=2026.4.6'[/bold].")
102
+ console.print("Reinstall or upgrade with [bold]pip install 'hypercli-cli>=2026.4.7'[/bold].")
103
103
  raise typer.Exit(1)
104
104
  return OpenAI(api_key=api_key, base_url=f"{api_base}/v1")
105
105
 
@@ -40,9 +40,9 @@ def transcribe(
40
40
  """Transcribe audio to text using faster-whisper (runs locally).
41
41
 
42
42
  Examples:
43
- hyper agent stt transcribe voice.ogg
44
- hyper agent stt transcribe meeting.mp3 --model large-v3 --language en
45
- hyper agent stt transcribe audio.wav --json -o transcript.json
43
+ hyper voice transcribe voice.ogg
44
+ hyper voice transcribe meeting.mp3 --model large-v3 --language en
45
+ hyper voice transcribe audio.wav --json -o transcript.json
46
46
  """
47
47
  if not audio_file.exists():
48
48
  console.print(f"[red]❌ File not found: {audio_file}[/red]")
@@ -6,8 +6,9 @@ from pathlib import Path
6
6
  import typer
7
7
  from rich.console import Console
8
8
  from hypercli import HyperCLI, APIError
9
+ from .stt import transcribe as _stt_transcribe
9
10
 
10
- app = typer.Typer(help="Voice API — text-to-speech, voice cloning, voice design")
11
+ app = typer.Typer(help="Voice commands — text-to-speech, voice cloning, voice design, and local transcription")
11
12
  console = Console()
12
13
 
13
14
  HYPERCLI_DIR = Path.home() / ".hypercli"
@@ -84,6 +85,26 @@ def _post_voice(endpoint: str, api_key: str, output: Path, base_url: str | None
84
85
  raise typer.Exit(1)
85
86
 
86
87
 
88
+ @app.command("transcribe")
89
+ def transcribe(
90
+ audio_file: Path = typer.Argument(..., help="Audio file to transcribe (wav, mp3, ogg, m4a, etc.)"),
91
+ model: str = typer.Option("turbo", "--model", "-m", help="Whisper model: tiny, base, small, medium, large-v3, turbo"),
92
+ language: str = typer.Option(None, "--language", "-l", help="Language code (e.g. en, de, fr). Auto-detect if omitted."),
93
+ device: str = typer.Option("auto", "--device", "-d", help="Device: auto, cpu, cuda"),
94
+ compute_type: str = typer.Option("auto", "--compute", help="Compute type: auto, int8, float16, float32"),
95
+ json_output: bool = typer.Option(False, "--json", help="Output as JSON with timestamps"),
96
+ output: Path = typer.Option(None, "--output", "-o", help="Write transcript to file"),
97
+ ):
98
+ """Transcribe audio locally using faster-whisper.
99
+
100
+ Examples:
101
+ hyper voice transcribe voice.ogg
102
+ hyper voice transcribe meeting.mp3 --model large-v3 --language en
103
+ hyper voice transcribe audio.wav --json -o transcript.json
104
+ """
105
+ _stt_transcribe(audio_file, model, language, device, compute_type, json_output, output)
106
+
107
+
87
108
  @app.command("tts")
88
109
  def tts(
89
110
  text: str = typer.Argument(..., help="Text to synthesize"),
@@ -97,8 +118,8 @@ def tts(
97
118
  """Generate speech from text using a preset voice.
98
119
 
99
120
  Examples:
100
- hyper agent voice tts "Hello world"
101
- hyper agent voice tts "Bonjour" -v Etienne -l french -f opus -o hello.opus
121
+ hyper voice tts "Hello world"
122
+ hyper voice tts "Bonjour" -v Etienne -l french -f opus -o hello.opus
102
123
  """
103
124
  api_key = _get_api_key(key)
104
125
  if output is None:
@@ -129,8 +150,8 @@ def clone(
129
150
  """Clone a voice from reference audio.
130
151
 
131
152
  Examples:
132
- hyper agent voice clone "Hello" --ref voice.wav
133
- hyper agent voice clone "Test" -r ref.wav -l english -f mp3 -o cloned.mp3
153
+ hyper voice clone "Hello" --ref voice.wav
154
+ hyper voice clone "Test" -r ref.wav -l english -f mp3 -o cloned.mp3
134
155
  """
135
156
  api_key = _get_api_key(key)
136
157
  if output is None:
@@ -167,8 +188,8 @@ def design(
167
188
  """Design a voice from a text description.
168
189
 
169
190
  Examples:
170
- hyper agent voice design "Hello" --desc "deep male voice, British accent"
171
- hyper agent voice design "Test" -d "young woman, cheerful" -f mp3 -o designed.mp3
191
+ hyper voice design "Hello" --desc "deep male voice, British accent"
192
+ hyper voice design "Test" -d "young woman, cheerful" -f mp3 -o designed.mp3
172
193
  """
173
194
  api_key = _get_api_key(key)
174
195
  if output is None:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hypercli-cli"
7
- version = "2026.4.6"
7
+ version = "2026.4.9"
8
8
  description = "CLI for HyperCLI - GPU orchestration and LLM API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -13,7 +13,7 @@ authors = [
13
13
  { name = "HyperCLI", email = "support@hypercli.com" }
14
14
  ]
15
15
  dependencies = [
16
- "hypercli-sdk>=2026.4.6",
16
+ "hypercli-sdk>=2026.4.9",
17
17
  "openai>=2.8.1",
18
18
  "typer>=0.20.0",
19
19
  "rich>=14.2.0",
@@ -25,7 +25,7 @@ dependencies = [
25
25
 
26
26
  [project.optional-dependencies]
27
27
  comfyui = [
28
- "hypercli-sdk[comfyui]>=2026.4.6",
28
+ "hypercli-sdk[comfyui]>=2026.4.9",
29
29
  ]
30
30
  wallet = [
31
31
  "x402[httpx,evm]>=2.0.0",
@@ -38,7 +38,7 @@ stt = [
38
38
  "faster-whisper>=1.1.0",
39
39
  ]
40
40
  all = [
41
- "hypercli-sdk[comfyui]>=2026.4.6",
41
+ "hypercli-sdk[comfyui]>=2026.4.9",
42
42
  "x402[httpx,evm]>=2.0.0",
43
43
  "eth-account>=0.13.0",
44
44
  "web3>=7.0.0",
@@ -0,0 +1,17 @@
1
+ from typer.testing import CliRunner
2
+
3
+ from hypercli_cli.cli import app
4
+
5
+
6
+ runner = CliRunner()
7
+
8
+
9
+ def test_flow_help_hides_voice_and_transcription_flows():
10
+ result = runner.invoke(app, ["flow", "--help"])
11
+
12
+ assert result.exit_code == 0
13
+ assert "speaking-video" not in result.stdout
14
+ assert "audio-to-text" not in result.stdout
15
+ assert "text-to-speech" not in result.stdout
16
+ assert "text-to-image" in result.stdout
17
+ assert "image-to-video" in result.stdout
@@ -0,0 +1,72 @@
1
+ from pathlib import Path
2
+
3
+ from typer.testing import CliRunner
4
+
5
+ from hypercli_cli.cli import app
6
+
7
+
8
+ runner = CliRunner()
9
+
10
+
11
+ def test_top_level_voice_transcribe_delegates_to_local_stt(monkeypatch, tmp_path):
12
+ import hypercli_cli.voice as voice
13
+
14
+ called = {}
15
+
16
+ def _fake_transcribe(audio_file, model, language, device, compute_type, json_output, output):
17
+ called["audio_file"] = audio_file
18
+ called["model"] = model
19
+ called["language"] = language
20
+ called["device"] = device
21
+ called["compute_type"] = compute_type
22
+ called["json_output"] = json_output
23
+ called["output"] = output
24
+
25
+ monkeypatch.setattr(voice, "_stt_transcribe", _fake_transcribe)
26
+
27
+ audio_file = tmp_path / "voice.ogg"
28
+ audio_file.write_bytes(b"ogg")
29
+ output = tmp_path / "transcript.txt"
30
+
31
+ result = runner.invoke(
32
+ app,
33
+ [
34
+ "voice",
35
+ "transcribe",
36
+ str(audio_file),
37
+ "--model",
38
+ "turbo",
39
+ "--device",
40
+ "cpu",
41
+ "--compute",
42
+ "int8",
43
+ "--output",
44
+ str(output),
45
+ ],
46
+ )
47
+
48
+ assert result.exit_code == 0, result.stdout
49
+ assert called == {
50
+ "audio_file": audio_file,
51
+ "model": "turbo",
52
+ "language": None,
53
+ "device": "cpu",
54
+ "compute_type": "int8",
55
+ "json_output": False,
56
+ "output": output,
57
+ }
58
+
59
+
60
+ def test_top_level_voice_group_is_registered():
61
+ result = runner.invoke(app, ["voice", "--help"])
62
+
63
+ assert result.exit_code == 0
64
+ assert "Voice commands" in result.stdout
65
+ assert "transcribe" in result.stdout
66
+
67
+
68
+ def test_agent_transcribe_command_is_removed():
69
+ result = runner.invoke(app, ["agent", "transcribe", "voice.ogg"])
70
+
71
+ assert result.exit_code != 0
72
+ assert "No such command 'transcribe'" in result.stdout
@@ -1 +0,0 @@
1
- __version__ = "2026.4.6"