akashcli 3.0.0__py3-none-any.whl

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.
akashcli/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ # akashcli/__init__.py
2
+ from .client import AkashClient
3
+ from .models import UploadResult, FileInfo, UserInfo
4
+ from .exceptions import AkashError
5
+
6
+ __version__ = "2.0.0"
akashcli/auth.py ADDED
@@ -0,0 +1,51 @@
1
+ import httpx
2
+ import json
3
+ from typing import Optional
4
+ from .models import UserInfo
5
+ from .exceptions import AuthenticationError, NetworkError
6
+ from .config import ConfigManager
7
+ from .crypto import SecureVault
8
+
9
+ class AuthManager:
10
+ @staticmethod
11
+ def verify_remote_key(api_key: str, api_url: str) -> UserInfo:
12
+ """Calls the /api/verify endpoint to check key status."""
13
+ try:
14
+ with httpx.Client(base_url=api_url, timeout=10.0) as client:
15
+ response = client.get(
16
+ "/api/verify",
17
+ headers={"x-api-key": api_key, "Origin": "https://akash.cli"}
18
+ )
19
+
20
+ if response.status_code == 403:
21
+ raise AuthenticationError("API Key is invalid or locked.")
22
+
23
+ response.raise_for_status()
24
+ data = response.json()
25
+
26
+ return UserInfo(
27
+ owner=data["owner"],
28
+ quota_used=data["quota_used"],
29
+ quota_limit=data["quota_limit"],
30
+ allowed_domains=data.get("allowed_domains", "*")
31
+ )
32
+ except httpx.RequestError as e:
33
+ raise NetworkError(f"Could not connect to Akash Vault: {e}")
34
+
35
+ @staticmethod
36
+ def perform_login(api_key: str, api_url: str) -> UserInfo:
37
+ """Verifies the key and persists it to the encrypted config."""
38
+ user_info = AuthManager.verify_remote_key(api_key, api_url)
39
+
40
+ # Prepare data for local encrypted storage
41
+ config_data = {
42
+ "api_key": api_key,
43
+ "api_url": api_url,
44
+ "owner": user_info.owner
45
+ }
46
+
47
+ # Encrypt and save
48
+ encrypted_blob = SecureVault.encrypt(json.dumps(config_data))
49
+ ConfigManager.save(encrypted_blob)
50
+
51
+ return user_info
akashcli/cli.py ADDED
@@ -0,0 +1,184 @@
1
+ import typer
2
+ import json
3
+ import os
4
+ import subprocess
5
+ import webbrowser
6
+ import httpx
7
+ from typing import Optional
8
+ from pathlib import Path
9
+ from rich.console import Console
10
+ from rich.table import Table
11
+ from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, DownloadColumn, TransferSpeedColumn
12
+ from rich.panel import Panel
13
+
14
+ # Internal Imports
15
+ from .client import AkashClient
16
+ from .auth import AuthManager
17
+ from .config import ConfigManager
18
+ from .exceptions import AkashError
19
+ from .uploader import VaultUploader
20
+
21
+ # 🌟 Removed the 'gh' overrides and panels
22
+ app = typer.Typer(help="🛡️ Akash Vault Enterprise CLI", add_completion=True)
23
+ console = Console()
24
+
25
+ @app.command()
26
+ def login(
27
+ url: str = typer.Option("https://jstore.2bd.net", "--url", help="Custom Vault URL"),
28
+ key: str = typer.Option(..., prompt="🔑 Enter API Key", hide_input=True)
29
+ ):
30
+ """🔑 Link your device to the vault."""
31
+ try:
32
+ with console.status("[bold blue]Verifying identity..."):
33
+ user = AuthManager.perform_login(key, url)
34
+
35
+ console.print(f"[green]✓ Login Successful![/green] Owner: {user.owner}")
36
+ except Exception as e:
37
+ console.print(f"[bold red]✗ Authentication Failed:[/bold red] {e}")
38
+
39
+ @app.command()
40
+ def whoami():
41
+ """👤 Check current login and quota status."""
42
+ try:
43
+ client = AkashClient()
44
+ user = client.whoami()
45
+ console.print(f"Logged in as: [bold cyan]{user.owner}[/bold cyan]")
46
+ console.print(f"Storage Quota: [bold yellow]{user.quota_used}/{user.quota_limit}[/bold yellow]")
47
+ except Exception as e:
48
+ console.print(f"[red]Error:[/red] {e}")
49
+
50
+ @app.command()
51
+ def upload(
52
+ path: Optional[Path] = typer.Argument(None, help="Path to file"),
53
+ text: Optional[str] = typer.Option(None, "--text", "-t", help="Upload raw text content"),
54
+ name: Optional[str] = typer.Option(None, "--name", "-n", help="Custom filename"),
55
+ burn: bool = typer.Option(False, "--burn", help="Self-destruct after one view"),
56
+ expiry: int = typer.Option(0, "--expiry", "-e", help="Expiry in days (0 for forever)"),
57
+ password: Optional[str] = typer.Option(None, "--pass", "-p", help="Password protect file")
58
+ ):
59
+ """📤 Vault a single file or text snippet."""
60
+ try:
61
+ client = AkashClient()
62
+
63
+ with Progress(
64
+ SpinnerColumn(),
65
+ TextColumn("[progress.description]{task.description}"),
66
+ BarColumn(),
67
+ console=console,
68
+ transient=True
69
+ ) as progress:
70
+ task = progress.add_task(description="[cyan]Processing...", total=100)
71
+ def update_bar(current, total):
72
+ progress.update(task, completed=(current/total)*100)
73
+
74
+ if text:
75
+ res = client.upload_text(text, filename=name, burn=burn, expiry=expiry, password=password)
76
+ else:
77
+ if not path:
78
+ console.print("[red]Error:[/red] Provide a file path or use --text")
79
+ return
80
+ res = client.upload_file(str(path), burn=burn, expiry=expiry, password=password, progress_callback=update_bar)
81
+
82
+ console.print(f"[bold green]✓ Vaulted![/bold green] Code: [yellow]{res.file_code}[/yellow]")
83
+ except Exception as e:
84
+ console.print(f"[bold red]✗ Upload Failed:[/bold red] {e}")
85
+
86
+ @app.command()
87
+ def files(json_output: bool = typer.Option(False, "--json", help="Output in raw JSON")):
88
+ """📦 List and search files in your vault."""
89
+ try:
90
+ client = AkashClient()
91
+ file_list = client.list_files()
92
+
93
+ if json_output:
94
+ console.print_json(data=[f.__dict__ for f in file_list])
95
+ return
96
+
97
+ table = Table(title="📦 Encrypted Vault")
98
+ table.add_column("Filename", style="cyan")
99
+ table.add_column("Code", style="yellow")
100
+ table.add_column("Size", justify="right")
101
+
102
+ for f in file_list:
103
+ size_mb = f"{f.size / (1024*1024):.2f} MB"
104
+ table.add_row(f.name, f.code, size_mb)
105
+
106
+ console.print(table)
107
+ except Exception as e:
108
+ console.print(f"[red]Error:[/red] {e}")
109
+
110
+ @app.command()
111
+ def stream(code: str, vlc: bool = typer.Option(False, "--vlc", help="Open directly in VLC")):
112
+ """🎬 Get a secure streaming link."""
113
+ client = AkashClient()
114
+ try:
115
+ with httpx.Client(base_url=client.base_url) as c:
116
+ resp = c.get(f"/api/stream/generate/{code}", headers=client._get_headers())
117
+ resp.raise_for_status()
118
+ url = resp.json()['stream_url']
119
+
120
+ if vlc:
121
+ console.print(f"🎬 Launching VLC for: {code}")
122
+ subprocess.Popen(['vlc', url], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
123
+ else:
124
+ console.print(f"🎬 Secure URL: {url}")
125
+ except Exception as e:
126
+ console.print(f"[red]Error:[/red] {e}")
127
+
128
+ @app.command()
129
+ def delete(code: str):
130
+ """🗑️ Permanently remove a file."""
131
+ if typer.confirm(f"Are you sure you want to delete {code}?"):
132
+ try:
133
+ client = AkashClient()
134
+ client.delete_file(code)
135
+ console.print(f"[green]✓ Deleted {code}[/green]")
136
+ except Exception as e:
137
+ console.print(f"[red]Error:[/red] {e}")
138
+
139
+ @app.command()
140
+ def manage(
141
+ code: str,
142
+ name: Optional[str] = typer.Option(None, "--name"),
143
+ burn: Optional[bool] = typer.Option(None, "--burn/--no-burn"),
144
+ expiry: Optional[int] = typer.Option(None, "--expiry")
145
+ ):
146
+ """⚙️ Modify settings for an existing file."""
147
+ try:
148
+ client = AkashClient()
149
+ updates = {}
150
+ if name: updates['filename'] = name
151
+ if burn is not None: updates['burn_on_read'] = 1 if burn else 0
152
+ if expiry is not None: updates['expiry_days'] = expiry
153
+ client.update_file(code, **updates)
154
+ console.print(f"[green]✓ Updated {code}[/green]")
155
+ except Exception as e:
156
+ console.print(f"[red]Error:[/red] {e}")
157
+
158
+ @app.command()
159
+ def bulk(path: Path):
160
+ """🚀 Recursive folder vaulting."""
161
+ try:
162
+ client = AkashClient()
163
+ uploader = VaultUploader(client)
164
+ files = [p for p in path.glob("**/*") if p.is_file() and not p.name.startswith('.')]
165
+ for file in files:
166
+ console.print(f"Vaulting: {file.name}")
167
+ uploader.upload(file_path=str(file))
168
+ console.print("[green]✓ Batch complete.[/green]")
169
+ except Exception as e:
170
+ console.print(f"[red]Error:[/red] {e}")
171
+
172
+ @app.command()
173
+ def docs():
174
+ """📖 View the integration guide."""
175
+ webbrowser.open("https://github.com/juniorsir/akashcli")
176
+
177
+ @app.command()
178
+ def logout():
179
+ """🚪 Clear local credentials."""
180
+ ConfigManager.save(b"")
181
+ console.print("[yellow]Logged out.[/yellow]")
182
+
183
+ if __name__ == "__main__":
184
+ app()
akashcli/client.py ADDED
@@ -0,0 +1,74 @@
1
+ import httpx
2
+ import time
3
+ from typing import List, Optional
4
+ from .models import UploadResult, FileInfo, UserInfo
5
+ from .exceptions import *
6
+ from .config import ConfigManager
7
+
8
+ class AkashClient:
9
+ def __init__(self, api_key: Optional[str] = None, api_url: Optional[str] = None):
10
+ config = ConfigManager.load()
11
+ self.api_key = api_key or config.get("api_key")
12
+ self.base_url = (api_url or config.get("api_url", "https://jstore.2bd.net")).rstrip("/")
13
+
14
+ if not self.api_key:
15
+ raise AuthenticationError("No API Key found. Run 'akash login'")
16
+
17
+ def _get_headers(self, custom: dict = None):
18
+ h = {"x-api-key": self.api_key, "Origin": "https://akash.cli"}
19
+ if custom: h.update(custom)
20
+ return h
21
+
22
+ def whoami(self) -> UserInfo:
23
+ with httpx.Client(base_url=self.base_url) as client:
24
+ resp = client.get("/api/me", headers=self._get_headers())
25
+ if resp.status_code != 200: raise AuthenticationError("Invalid Key")
26
+ d = resp.json()
27
+ return UserInfo(d['owner'], d['quota']['used'], d['quota']['limit'], d['allowed_domains'])
28
+
29
+ def upload_file(self, path: str, burn: bool = False, expiry: int = 0) -> UploadResult:
30
+ files = {"file": open(path, "rb")}
31
+ headers = {
32
+ "x-vault-burn": "1" if burn else "0",
33
+ "x-vault-expiry": str(expiry)
34
+ }
35
+ with httpx.Client(base_url=self.base_url, timeout=None) as client:
36
+ resp = client.post("/api/upload", headers=self._get_headers(headers), files=files)
37
+ resp.raise_for_status()
38
+ d = resp.json()
39
+ return UploadResult(
40
+ d['status'], d['file_code'], d['links']['embed'],
41
+ d['links']['direct'], d['input_type'], d.get('dna', {})
42
+ )
43
+
44
+ def list_files(self) -> List[FileInfo]:
45
+ with httpx.Client(base_url=self.base_url) as client:
46
+ resp = client.get("/api/files", headers=self._get_headers())
47
+ return [FileInfo(f['name'], f['code'], f['size'], f['uploaded_at']) for f in resp.json()]
48
+
49
+ def delete_file(self, code: str):
50
+ with httpx.Client(base_url=self.base_url) as client:
51
+ client.delete(f"/api/files/{code}", headers=self._get_headers())
52
+
53
+ def upload_text(self, content: str, filename: str = None, burn: bool = False, expiry: int = 0, password: str = None):
54
+ from .uploader import VaultUploader
55
+ uploader = VaultUploader(self)
56
+ return uploader.upload(content=content, filename=filename, burn=burn, expiry=expiry, password=password)
57
+
58
+ def update_file(self, code: str, **kwargs) -> dict:
59
+ """Update file settings (filename, burn_on_read, expiry_days)."""
60
+ with httpx.Client(base_url=self.base_url) as client:
61
+ resp = client.patch(
62
+ f"/api/files/{code}",
63
+ headers=self._get_headers(),
64
+ json=kwargs
65
+ )
66
+ resp.raise_for_status()
67
+ return resp.json()
68
+
69
+ def get_stats(self) -> dict:
70
+ """Admin Only: Fetch global vault statistics."""
71
+ with httpx.Client(base_url=self.base_url) as client:
72
+ # Reuses the logic from your bot's show_stats
73
+ resp = client.get("/api/me", headers=self._get_headers())
74
+ return resp.json()
akashcli/config.py ADDED
@@ -0,0 +1,52 @@
1
+ import os
2
+ import pathlib
3
+ import platform
4
+
5
+ class ConfigManager:
6
+ APP_NAME = "Akash"
7
+
8
+ @staticmethod
9
+ def get_config_dir() -> pathlib.Path:
10
+ """Determines the OS-specific directory for config storage."""
11
+ home = pathlib.Path.home()
12
+ system = platform.system()
13
+
14
+ if system == "Windows":
15
+ path = pathlib.Path(os.getenv("APPDATA", home / "AppData/Roaming")) / ConfigManager.APP_NAME
16
+ elif system == "Darwin": # macOS
17
+ path = home / "Library/Application Support" / ConfigManager.APP_NAME
18
+ else: # Linux / Termux
19
+ path = home / ".akash"
20
+
21
+ path.mkdir(parents=True, exist_ok=True)
22
+ return path
23
+
24
+ @staticmethod
25
+ def get_config_path() -> pathlib.Path:
26
+ return ConfigManager.get_config_dir() / "config.enc"
27
+
28
+ @staticmethod
29
+ def save(encrypted_bytes: bytes):
30
+ """Writes the encrypted config to disk."""
31
+ path = ConfigManager.get_config_path()
32
+ with open(path, "wb") as f:
33
+ f.write(encrypted_bytes)
34
+
35
+ @staticmethod
36
+ def load() -> dict:
37
+ """Reads and decrypts the config file."""
38
+ from .crypto import SecureVault
39
+ import json
40
+
41
+ path = ConfigManager.get_config_path()
42
+ if not path.exists():
43
+ return {}
44
+
45
+ try:
46
+ with open(path, "rb") as f:
47
+ encrypted_data = f.read()
48
+
49
+ decrypted_str = SecureVault.decrypt(encrypted_data)
50
+ return json.loads(decrypted_str)
51
+ except Exception:
52
+ return {}
akashcli/crypto.py ADDED
@@ -0,0 +1,42 @@
1
+ import base64
2
+ import os
3
+ import json
4
+ from cryptography.fernet import Fernet
5
+ from cryptography.hazmat.primitives import hashes
6
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
7
+ from .utils import get_device_id
8
+
9
+ class SecureVault:
10
+ @staticmethod
11
+ def _get_fernet(salt: bytes) -> Fernet:
12
+ """Derives a deterministic key based on the hardware ID and a random salt."""
13
+ kdf = PBKDF2HMAC(
14
+ algorithm=hashes.SHA256(),
15
+ length=32,
16
+ salt=salt,
17
+ iterations=100000,
18
+ )
19
+ # Use hardware ID as the password for key derivation
20
+ key = base64.urlsafe_b64encode(kdf.derive(get_device_id().encode()))
21
+ return Fernet(key)
22
+
23
+ @staticmethod
24
+ def encrypt(data: str) -> bytes:
25
+ """Encrypts a string. Returns [16 bytes salt][ciphertext]."""
26
+ salt = os.urandom(16)
27
+ # 🌟 THE FIX: Call the helper via the Class name, not 'self'
28
+ f = SecureVault._get_fernet(salt)
29
+ return salt + f.encrypt(data.encode())
30
+
31
+ @staticmethod
32
+ def decrypt(token: bytes) -> str:
33
+ """Decrypts a token using the hardware-bound key."""
34
+ try:
35
+ if not token:
36
+ return "{}"
37
+ salt, ciphertext = token[:16], token[16:]
38
+ f = SecureVault._get_fernet(salt)
39
+ return f.decrypt(ciphertext).decode()
40
+ except Exception:
41
+ # If hardware ID changed or file is corrupt, return empty config
42
+ return "{}"
akashcli/exceptions.py ADDED
@@ -0,0 +1,27 @@
1
+ class AkashError(Exception):
2
+ """Base exception for all Akash CLI/SDK errors."""
3
+ pass
4
+
5
+ class AuthenticationError(AkashError):
6
+ """Raised when API Key is missing, invalid, or locked."""
7
+ pass
8
+
9
+ class QuotaExceeded(AkashError):
10
+ """Raised when the user's upload quota (429) has been reached."""
11
+ pass
12
+
13
+ class VaultFileNotFound(AkashError):
14
+ """Raised when a specific file code cannot be found in the vault."""
15
+ pass
16
+
17
+ class UploadFailed(AkashError):
18
+ """Raised when a file or text upload fails due to network or server issues."""
19
+ pass
20
+
21
+ class NetworkError(AkashError):
22
+ """Raised for general connection issues (timeout, DNS, etc.)."""
23
+ pass
24
+
25
+ class ConfigError(AkashError):
26
+ """Raised when the local configuration is corrupt or unreadable."""
27
+ pass
akashcli/models.py ADDED
@@ -0,0 +1,26 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional, Dict
3
+ from datetime import datetime
4
+
5
+ @dataclass
6
+ class UserInfo:
7
+ owner: str
8
+ quota_used: int
9
+ quota_limit: int
10
+ allowed_domains: str
11
+
12
+ @dataclass
13
+ class UploadResult:
14
+ status: str
15
+ file_code: str
16
+ embed_url: str
17
+ direct_url: str
18
+ input_type: str
19
+ dna: Dict[str, str]
20
+
21
+ @dataclass
22
+ class FileInfo:
23
+ name: str
24
+ code: str
25
+ size: int
26
+ uploaded_at: Optional[datetime] = None
akashcli/uploader.py ADDED
@@ -0,0 +1,123 @@
1
+ import os
2
+ import httpx, asyncio
3
+ from pathlib import Path
4
+ from typing import Optional, Callable, Dict, Any, List
5
+ from concurrent.futures import ThreadPoolExecutor
6
+ from .models import UploadResult
7
+ from .exceptions import UploadFailed, QuotaExceeded
8
+
9
+ class VaultUploader:
10
+ def __init__(self, client):
11
+ self.client = client # Reference to AkashClient for headers/url
12
+
13
+ def upload(
14
+ self,
15
+ file_path: Optional[str] = None,
16
+ content: Optional[str] = None,
17
+ filename: Optional[str] = None,
18
+ burn: bool = False,
19
+ expiry: int = 0,
20
+ password: Optional[str] = None,
21
+ progress_callback: Optional[Callable[[int, int], None]] = None
22
+ ) -> UploadResult:
23
+ """
24
+ Standardizes both File and Text uploads into a single stream.
25
+ progress_callback signature: (bytes_read, total_bytes)
26
+ """
27
+
28
+ # 1. Prepare Headers based on V3 Power-Control Specs
29
+ headers = {
30
+ "x-vault-burn": "1" if burn else "0",
31
+ "x-vault-expiry": str(expiry),
32
+ }
33
+ if password:
34
+ headers["x-vault-password"] = password
35
+
36
+ url = f"{self.client.base_url}/api/upload"
37
+
38
+ try:
39
+ if content is not None:
40
+ # --- TEXT SNIPPET MODE (JSON) ---
41
+ payload = {
42
+ "content": content,
43
+ "filename": filename or "snippet.txt"
44
+ }
45
+ resp = httpx.post(
46
+ url,
47
+ json=payload,
48
+ headers=self.client._get_headers(headers),
49
+ timeout=None
50
+ )
51
+ else:
52
+ # --- FILE MODE (Multipart) ---
53
+ if not file_path or not os.path.exists(file_path):
54
+ raise UploadFailed(f"File not found: {file_path}")
55
+
56
+ file_size = os.path.getsize(file_path)
57
+
58
+ # Setup custom stream to track progress
59
+ def file_generator():
60
+ with open(file_path, "rb") as f:
61
+ chunk_size = 1024 * 1024 # 1MB chunks
62
+ bytes_read = 0
63
+ while chunk := f.read(chunk_size):
64
+ bytes_read += len(chunk)
65
+ if progress_callback:
66
+ progress_callback(bytes_read, file_size)
67
+ yield chunk
68
+
69
+ files = {"file": (os.path.basename(file_path), file_generator())}
70
+
71
+ resp = httpx.post(
72
+ url,
73
+ files=files,
74
+ headers=self.client._get_headers(headers),
75
+ timeout=None
76
+ )
77
+
78
+ if resp.status_code == 429:
79
+ raise QuotaExceeded("Monthly upload quota reached.")
80
+
81
+ resp.raise_for_status()
82
+ d = resp.json()
83
+
84
+ return UploadResult(
85
+ status=d['status'],
86
+ file_code=d['file_code'],
87
+ embed_url=d['links']['embed'],
88
+ direct_url=d['links']['direct'],
89
+ input_type=d['input_type'],
90
+ dna=d.get('dna', {})
91
+ )
92
+
93
+ except httpx.HTTPStatusError as e:
94
+ raise UploadFailed(f"Server rejected upload ({e.response.status_code}): {e.response.text}")
95
+ except Exception as e:
96
+ raise UploadFailed(f"Unexpected error during upload: {e}")
97
+
98
+ def upload_directory(self, folder_path: str, max_workers: int = 3) -> List[UploadResult]:
99
+ """Recursively uploads all files in a folder using a thread pool."""
100
+ # Convert string path to Path object
101
+ root = Path(folder_path)
102
+ if not root.is_dir():
103
+ raise UploadFailed(f"Source is not a directory: {folder_path}")
104
+
105
+ # Find all files recursively (excluding hidden files/folders)
106
+ paths = [p for p in root.glob("**/*") if p.is_file() and not p.name.startswith('.')]
107
+ results = []
108
+
109
+ # Using ThreadPoolExecutor for parallel I/O bound tasks
110
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
111
+ # We wrap the self.upload calls into the executor
112
+ # Note: We don't pass a progress_callback here to avoid UI overlap
113
+ # (The CLI 'bulk' command will handle the UI instead)
114
+ futures = [executor.submit(self.upload, file_path=str(p)) for p in paths]
115
+
116
+ for f in futures:
117
+ try:
118
+ results.append(f.result())
119
+ except Exception as e:
120
+ # Log error but continue with other files
121
+ print(f"Error uploading a file in batch: {e}")
122
+
123
+ return results
akashcli/utils.py ADDED
@@ -0,0 +1,9 @@
1
+ import uuid
2
+ import hashlib
3
+ import platform
4
+
5
+ def get_device_id() -> str:
6
+ """Generates a unique hardware fingerprint."""
7
+ # Combine node (MAC address) and system info for a stable ID
8
+ raw_id = f"{uuid.getnode()}-{platform.node()}-{platform.processor()}"
9
+ return hashlib.sha256(raw_id.encode()).hexdigest()
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.4
2
+ Name: akashcli
3
+ Version: 3.0.0
4
+ Summary: Enterprise Developer SDK and CLI for Akash Vault
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: httpx
8
+ Requires-Dist: typer[all]
9
+ Requires-Dist: rich
10
+ Requires-Dist: cryptography
11
+
12
+ To get started with **AkashCLI**, your users should follow these steps. You can provide this guide as a `README.md` or a "Getting Started" message in your bot.
13
+
14
+ ---
15
+
16
+ # 🚀 Akash Vault: Getting Started Guide
17
+
18
+ **AkashCLI** is the professional command-line interface and SDK for interacting with your E2E encrypted vault. Follow these steps to install and secure your first files.
19
+
20
+ ## 1. Prerequisites
21
+ Ensure you have **Python 3.10+** and `pip` installed on your machine (Linux, Termux, Mac, or Windows).
22
+
23
+ ## 2. Installation
24
+ Since this is a custom package, users can install it directly from your source code or repository:
25
+
26
+ ```bash
27
+ # Clone the repository
28
+ git clone https://github.com/juniorsir/akashcli
29
+ cd akashcli
30
+
31
+ # Install in editable mode
32
+ pip install . --break-system-packages
33
+ ```
34
+
35
+ ---
36
+
37
+ ## 3. First-Time Setup (Authentication)
38
+ Before using the vault, you must link your device using an API Key generated from the [Akash Telegram Bot](https://t.me/EveryDL_bot).
39
+
40
+ 1. Open the Bot and go to **👤 My Account** -> **🔑 Generate API Key**.
41
+ 2. In your terminal, run:
42
+ ```bash
43
+ akash login
44
+ ```
45
+ 3. Enter your API Key when prompted. Your key will be **encrypted and bound to your hardware** automatically.
46
+
47
+ ---
48
+
49
+ ## 4. Daily Workflow Commands
50
+
51
+ ### 📤 Uploading Content
52
+ **Upload a file:**
53
+ ```bash
54
+ akash upload movie.mp4
55
+ ```
56
+
57
+ **Upload a secret text snippet:**
58
+ ```bash
59
+ akash upload --text "My secret server password is 1234" --name "passwords.txt"
60
+ ```
61
+
62
+ **Upload with security flags:**
63
+ ```bash
64
+ # Delete automatically after the first person views it
65
+ akash upload --burn secret_doc.pdf
66
+
67
+ # Set a 7-day expiry
68
+ akash upload --expiry 7 data_backup.zip
69
+ ```
70
+
71
+ ### 📦 Managing the Vault
72
+ **List your most recent files:**
73
+ ```bash
74
+ akash files
75
+ ```
76
+
77
+ **Get file details or secure links:**
78
+ ```bash
79
+ akash stream FILE_CODE
80
+ ```
81
+
82
+ **Delete a file permanently:**
83
+ ```bash
84
+ akash delete FILE_CODE
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 5. Professional Features
90
+
91
+ ### 🚀 Bulk Vaulting
92
+ Vault an entire folder of documents or images at once:
93
+ ```bash
94
+ akash bulk ./my_documents_folder
95
+ ```
96
+
97
+ ### 🎬 Native Streaming
98
+ Stream a vaulted video directly into **VLC Media Player** without downloading:
99
+ ```bash
100
+ akash stream FILE_CODE --vlc
101
+ ```
102
+
103
+ ### 📊 System Health
104
+ Check your remaining quota and vault status:
105
+ ```bash
106
+ akash admin
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 6. Using the SDK (For Developers)
112
+ If you are building your own Python scripts, you can import the library directly:
113
+
114
+ ```python
115
+ from akashcli import AkashClient
116
+
117
+ # Initialize client (uses your encrypted local config)
118
+ client = AkashClient()
119
+
120
+ # Vault a log file
121
+ result = client.upload_file("system.log", expiry=30)
122
+ print(f"File secured! Access it at: {result.embed_url}")
123
+
124
+ # List all your files programmatically
125
+ for file in client.list_files():
126
+ print(f"{file.name} -> {file.code}")
127
+ ```
128
+
129
+ ---
130
+
131
+ ## 💡 Troubleshooting
132
+ * **Access Denied?** Ensure you have set your API Key domains to `*` in the Telegram Bot under "Edit Domains."
133
+ * **VLC not opening?** Ensure the `vlc` command is available in your system's PATH.
134
+ * **Forgot your key?** Run `akash login` again to overwrite the old session.
135
+
136
+ ---
137
+ **🛡️ Powered by Akash Enterprise Core.**
@@ -0,0 +1,15 @@
1
+ akashcli/__init__.py,sha256=KfZTt-MEKFMfITPwtmrAVwR3SpFNxMD4aIp9HtUcKoQ,166
2
+ akashcli/auth.py,sha256=Z4T15Khzm5OZSmpi2bX5fY9lNQDJBQLNOI8AZP3Z8GQ,1877
3
+ akashcli/cli.py,sha256=9pVLjLUxdSMqg4eGfFy5i6X_M-9fJ-x-GqdTP4zHGvM,6813
4
+ akashcli/client.py,sha256=CiIjsenjmJJPTgNjoD0cREQzaHDBoM5vbSxFtT4JqDQ,3319
5
+ akashcli/config.py,sha256=tmygJ_Pub1X31y1atrhXL79w6M6ya7xgECfzP5tdEIE,1591
6
+ akashcli/crypto.py,sha256=qZrd5m9zciX91MrE7FCPVFwqu-OPyGnx2sg5QxIAvlU,1486
7
+ akashcli/exceptions.py,sha256=UgAKUghVHPt8MP9LwgzaypB1yLJYEu7PzThk55JasMo,786
8
+ akashcli/models.py,sha256=jTWAlm0L15zE5iSrDvLCFeidfp1eSyq75ITJgLmwoB4,470
9
+ akashcli/uploader.py,sha256=1sbofW67HZ2W5rR271foES6KqlTctm98z7-NWnK9oik,4818
10
+ akashcli/utils.py,sha256=gcpMgr1MYKW-lVN1EvlhB3_OFM-exYnoBkpmVDdTPAs,317
11
+ akashcli-3.0.0.dist-info/METADATA,sha256=PT3PMwFqhg6gzsWPB2oEwqcbFy-IMiRJiulUhiNAPz4,3407
12
+ akashcli-3.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
13
+ akashcli-3.0.0.dist-info/entry_points.txt,sha256=aLc6XROFswqJxANSF-7GDq3F4PaOy42quTIxhDwMSrc,43
14
+ akashcli-3.0.0.dist-info/top_level.txt,sha256=p49Zj2joNmEOddr96g8B6G7Rdiy1idOJtIv3gcv6T80,9
15
+ akashcli-3.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ akash = akashcli.cli:app
@@ -0,0 +1 @@
1
+ akashcli