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.
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/PKG-INFO +4 -4
- hypercli_cli-2026.4.9/hypercli_cli/__init__.py +1 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/agent.py +124 -21
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/cli.py +2 -1
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/flow.py +3 -3
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/llm.py +1 -1
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/stt.py +3 -3
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/voice.py +28 -7
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/pyproject.toml +4 -4
- hypercli_cli-2026.4.9/tests/test_flow_visibility.py +17 -0
- hypercli_cli-2026.4.9/tests/test_voice_command.py +72 -0
- hypercli_cli-2026.4.6/hypercli_cli/__init__.py +0 -1
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/.gitignore +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/README.md +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/agents.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/billing.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/comfyui.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/embed.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/files.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/instances.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/jobs.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/keys.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/onboard.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/output.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/renders.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/tui/__init__.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/tui/job_monitor.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/user.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/hypercli_cli/wallet.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_agent_env_resolution.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_config_command.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_exec_shell_dryrun.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_flow_command.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_jobs_list_tags.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_llm_command.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_me_command.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_openclaw_config.py +0 -0
- {hypercli_cli-2026.4.6 → hypercli_cli-2026.4.9}/tests/test_wallet_command.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
44
|
-
hyper
|
|
45
|
-
hyper
|
|
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
|
|
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
|
|
101
|
-
hyper
|
|
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
|
|
133
|
-
hyper
|
|
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
|
|
171
|
-
hyper
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|