akashcli 3.4.0__tar.gz → 4.2.0__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: akashcli
3
- Version: 3.4.0
3
+ Version: 4.2.0
4
4
  Summary: Enterprise Developer SDK and CLI for Akash Vault
5
5
  Requires-Python: >=3.12
6
6
  Description-Content-Type: text/markdown
@@ -5,8 +5,9 @@ import os
5
5
  import subprocess
6
6
  import webbrowser
7
7
  import httpx
8
+ import urllib.parse
8
9
  import questionary
9
- from typing import Optional
10
+ from typing import Optional, List
10
11
  from pathlib import Path
11
12
  from rich.console import Console
12
13
  from rich.table import Table
@@ -193,21 +194,32 @@ def files(json_output: bool = typer.Option(False, "--json", help="Output in raw
193
194
  @app.command()
194
195
  def download(
195
196
  code: str,
196
- output: Optional[Path] = typer.Option(None, "--output", "-o", help="Custom save path")
197
+ output: Optional[Path] = typer.Option(None, "--output", "-o")
197
198
  ):
198
- """📥 Download a file from the vault to your local machine."""
199
+ """📥 Download a file from the vault to your current folder."""
199
200
  try:
200
201
  client = AkashClient()
201
- # Attach the console to the client for the progress bar
202
- client.console = console
203
202
 
204
- from .downloader import download_file
205
- path = download_file(client, code, output)
203
+ # 1. Get the secure link
204
+ with console.status("[bold cyan]Requesting link..."):
205
+ resp = client.generate_secure_link(code)
206
+ if not resp or 'stream_url' not in resp:
207
+ raise Exception("Failed to generate download link.")
208
+
209
+ # 2. Start the download
210
+ from .downloader import smart_download
211
+ # Convert Path to string for the downloader
212
+ out_str = str(output) if output else None
213
+ saved_path = smart_download(resp['stream_url'], out_str)
206
214
 
207
- console.print(f"[bold green]✓ Download Complete![/bold green]")
208
- console.print(f"📍 Saved to: [cyan]{path}[/cyan]")
215
+ if saved_path:
216
+ console.print(f"[bold green]✓ Download Complete![/bold green]")
217
+ console.print(f"📍 Saved as: [bold cyan]{saved_path}[/bold cyan]")
218
+ else:
219
+ console.print("[red]❌ Download failed (Empty stream).[/red]")
220
+
209
221
  except Exception as e:
210
- console.print(f"[bold red]✗ Download Failed:[/bold red] {e}")
222
+ console.print(f"[bold red]✗ Error:[/bold red] {e}")
211
223
 
212
224
  @app.command()
213
225
  def delete(code: str):
@@ -323,30 +335,22 @@ def share(code: Optional[str] = typer.Argument(None)):
323
335
 
324
336
  @app.command()
325
337
  def get(otp: str):
326
- """✨ Download a file using a 6-digit temporary code."""
327
- url = os.getenv("AKASH_API_URL", "https://jstore.2bd.net").rstrip("/")
338
+ """✨ Download a file using a 6-digit OTP code."""
339
+ config = ConfigManager.load()
340
+ base_url = os.getenv("AKASH_API_URL") or config.get("api_url", "https://jstore.2bd.net")
328
341
  try:
329
342
  with console.status("[bold cyan]Redeeming code..."):
330
- resp = httpx.get(f"{url}/api/otp/redeem/{otp}")
331
- if resp.status_code != 200:
332
- console.print("[red]❌ Invalid or expired code.[/red]")
333
- return
334
-
335
- data = resp.json()
336
- stream_url = data['stream_url']
337
- # We don't have the original filename easily, so we probe the headers
338
- head = httpx.head(stream_url)
339
- cd = head.headers.get("Content-Disposition", "")
340
- filename = cd.split("filename*=UTF-8''")[-1] if "''" in cd else f"shared_{otp}.bin"
341
- filename = urllib.parse.unquote(filename)
342
-
343
- # Re-use your high-performance downloader logic
344
- from .downloader import download_from_url
345
- download_from_url(stream_url, filename)
343
+ resp = httpx.get(f"{base_url}/api/otp/redeem/{otp}")
344
+ resp.raise_for_status()
345
+ stream_url = resp.json()['stream_url']
346
+
347
+ from .downloader import smart_download
348
+ saved_path = smart_download(stream_url)
346
349
 
347
- console.print(f"[bold green]✓ Successfully retrieved: {filename}[/bold green]")
350
+ if saved_path:
351
+ console.print(f"[bold green]✓ Successfully retrieved:[/bold green] [cyan]{saved_path}[/cyan]")
348
352
  except Exception as e:
349
- console.print(f"[red]Error:[/red] {e}")
353
+ console.print(f"[bold red]✗ Failed:[/bold red] {e}")
350
354
 
351
355
  @app.command()
352
356
  def bulk(path: Path):
@@ -0,0 +1,47 @@
1
+ import httpx
2
+ import os
3
+ import re
4
+ import urllib.parse
5
+ from pathlib import Path
6
+ from typing import Optional
7
+ from rich.console import Console
8
+ from rich.progress import Progress, TextColumn, BarColumn, DownloadColumn, TransferSpeedColumn
9
+
10
+ def smart_download(url: str, custom_filename: Optional[str] = None):
11
+ """Unified downloader that detects server errors properly."""
12
+ # We use a standard get first to check the response
13
+ with httpx.stream("GET", url, follow_redirects=True, timeout=None) as response:
14
+ # 🌟 THE FIX: If not 200/206, read the error message 🌟
15
+ if response.status_code not in [200, 206]:
16
+ # Load the first few bytes to see the error
17
+ error_text = response.read().decode('utf-8', errors='ignore')
18
+ print(f"\n[red]❌ Server Error ({response.status_code}):[/red] {error_text[:200]}")
19
+ return None
20
+
21
+ # Robust Filename Extraction
22
+ raw_name = response.headers.get("x-vault-filename")
23
+ if raw_name:
24
+ filename = urllib.parse.unquote(raw_name)
25
+ else:
26
+ cd = response.headers.get("Content-Disposition", "")
27
+ match = re.search(r"filename\*=UTF-8''([^;]+)", cd, re.IGNORECASE)
28
+ filename = urllib.parse.unquote(match.group(1)) if match else f"file_{os.urandom(2).hex()}.bin"
29
+
30
+ final_name = custom_filename or filename
31
+ total_size = int(response.headers.get("x-vault-size") or response.headers.get("Content-Length", 0))
32
+
33
+ # Stream the actual data
34
+ with Progress(
35
+ TextColumn("[bold blue]{task.fields[filename]}", justify="right"),
36
+ BarColumn(bar_width=None),
37
+ "[progress.percentage]{task.percentage:>3.1f}%",
38
+ DownloadColumn(),
39
+ console=Console()
40
+ ) as progress:
41
+ task = progress.add_task("download", total=total_size, filename=final_name)
42
+ with open(final_name, "wb") as f:
43
+ for chunk in response.iter_bytes():
44
+ f.write(chunk)
45
+ progress.update(task, advance=len(chunk))
46
+
47
+ return final_name
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 3.4.0
3
+ Version: 4.2.0
4
4
  Summary: Enterprise Developer SDK and CLI for Akash Vault
5
5
  Requires-Python: >=3.12
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "akashcli"
3
- version = "3.4.0"
3
+ version = "4.2.0"
4
4
  description = "Enterprise Developer SDK and CLI for Akash Vault"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,28 +0,0 @@
1
- # akashcli/downloader.py
2
- import httpx
3
- import os
4
- from pathlib import Path
5
- from rich.console import Console # 🌟 ADD THIS
6
- from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, DownloadColumn
7
-
8
- def download_from_url(url: str, filename: str):
9
- """Standalone downloader for OTP/Guest links."""
10
- # 🌟 FIX: Define the path
11
- final_path = Path(os.getcwd()) / filename
12
-
13
- with httpx.stream("GET", url, follow_redirects=True) as response:
14
- total = int(response.headers.get("Content-Length", 0))
15
-
16
- with Progress(
17
- TextColumn("[bold blue]{task.fields[filename]}", justify="right"),
18
- BarColumn(bar_width=None),
19
- "[progress.percentage]{task.percentage:>3.1f}%",
20
- DownloadColumn(),
21
- console=Console()
22
- ) as progress:
23
- task = progress.add_task("download", total=total, filename=filename)
24
- with open(final_path, "wb") as f:
25
- for chunk in response.iter_bytes():
26
- f.write(chunk)
27
- progress.update(task, advance=len(chunk))
28
- return final_path # 🌟 Now defined
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