akashcli 4.7.0__tar.gz → 4.7.1__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 (32) hide show
  1. {akashcli-4.7.0 → akashcli-4.7.1}/PKG-INFO +1 -1
  2. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/__init__.py +1 -1
  3. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/cli.py +41 -227
  4. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli.egg-info/PKG-INFO +1 -1
  5. {akashcli-4.7.0 → akashcli-4.7.1}/pyproject.toml +1 -1
  6. {akashcli-4.7.0 → akashcli-4.7.1}/README.md +0 -0
  7. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/_net.py +0 -0
  8. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/auth.py +0 -0
  9. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/client.py +0 -0
  10. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/config.py +0 -0
  11. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/crypto.py +0 -0
  12. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/env_manager.py +0 -0
  13. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/exceptions.py +0 -0
  14. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/exec.py +0 -0
  15. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/integrity.py +0 -0
  16. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/logger.py +0 -0
  17. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/models.py +0 -0
  18. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/p2p.py +0 -0
  19. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/packages.py +0 -0
  20. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/relay.py +0 -0
  21. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/restore.py +0 -0
  22. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/snapshot_crypto.py +0 -0
  23. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/storage.py +0 -0
  24. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/terminal.py +0 -0
  25. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/uploader.py +0 -0
  26. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli/utils.py +0 -0
  27. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli.egg-info/SOURCES.txt +0 -0
  28. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli.egg-info/dependency_links.txt +0 -0
  29. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli.egg-info/entry_points.txt +0 -0
  30. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli.egg-info/requires.txt +0 -0
  31. {akashcli-4.7.0 → akashcli-4.7.1}/akashcli.egg-info/top_level.txt +0 -0
  32. {akashcli-4.7.0 → akashcli-4.7.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.7.0
3
+ Version: 4.7.1
4
4
  Summary: Akash CLI — secure remote shell relay, file vault, and device management
5
5
  License-Expression: MIT
6
6
  Keywords: akash,cli,relay,remote-shell,vault
@@ -2,7 +2,7 @@ try:
2
2
  from importlib.metadata import version
3
3
  __version__ = version("akashcli")
4
4
  except Exception:
5
- __version__ = "4.7.0"
5
+ __version__ = "4.7.1"
6
6
 
7
7
  AKASH_VERSION = __version__
8
8
 
@@ -204,40 +204,45 @@ def _print_help():
204
204
  C, G, Y, DM, RS = _C, _G, _Y, _DM, _RS
205
205
  lines = [
206
206
  "",
207
- f" {C}akash{RS} {DM}- encrypted cloud backup · remote terminal · AI tool vault{RS}",
207
+ f" {C}akash{RS} {DM}— encrypted cloud backup · remote shell · AI agent vault{RS}",
208
208
  "",
209
- f" {DM}AI & AGENTS{RS}",
210
- f" {C}mcp{RS} {DM}<serve|share|status>{RS} serve local tools to Codex, Claude & Cursor",
211
- f" {C}agent{RS} {DM}<create|list|info…>{RS} manage AI client credentials & scoped access",
209
+ f" {DM}ACCOUNT & SERVER{RS}",
210
+ f" {C}login{RS} {DM}[--label NAME] [--url URL]{RS} save your API key + name this device",
211
+ f" {C}whoami{RS} account quota, storage URL & device info",
212
+ f" {C}url{RS} {DM}[NEW_URL|--reset]{RS} view or switch configured storage server URL",
213
+ f" {C}logout{RS} clear saved credentials from this device",
212
214
  "",
213
215
  f" {DM}ENVIRONMENT BACKUP{RS}",
214
- f" {C}backup{RS} {DM}[--password ...]{RS} save terminal environment & dotfiles to cloud or local",
215
- f" {C}restore{RS} {DM}<CODE|PATH>{RS} restore terminal from cloud save or local archive",
216
- f" {C}pull{RS} {DM}<CODE>{RS} one-shot restore on fresh machine (no login needed)",
217
- f" {C}saves{RS} {DM}[--json]{RS} list your cloud-saved & local environments",
216
+ f" {C}backup{RS} {DM}[--label NAME] [--password PASS]{RS} save environment & packages to cloud or disk",
217
+ f" {C}restore{RS} {DM}<CODE|PATH> [--dry-run]{RS} restore terminal environment from cloud or archive",
218
+ f" {C}saves{RS} {DM}[--json]{RS} list cloud-saved and local environments",
219
+ f" {C}pull{RS} {DM}<CODE> [--target DIR]{RS} one-shot restore on fresh machine (no login needed)",
218
220
  "",
219
- f" {DM}REMOTE TERMINAL{RS}",
220
- f" {C}shell{RS} {DM}[NAME|--code CODE]{RS} open interactive shell (local, remote device, or code)",
221
- f" {C}run{RS} {DM}<CMD> [--device NAME]{RS} run command on server or enrolled device",
222
- f" {C}serve{RS} {DM}[--share]{RS} make this device reachable (or get one-time code)",
223
- f" {C}devices{RS} {DM}[--revoke NAME]{RS} list or manage enrolled devices",
221
+ f" {DM}REMOTE TERMINAL & SHELL{RS}",
222
+ f" {C}shell{RS} {DM}[DEVICE|--code CODE]{RS} open interactive shell (local, device, or code)",
223
+ f" {C}run{RS} {DM}<CMD> [--device NAME]{RS} run command on server or enrolled device",
224
+ f" {C}serve{RS} {DM}[--share] [--label NAME]{RS} make this device reachable (or get one-time code)",
225
+ f" {C}devices{RS} {DM}[--revoke NAME]{RS} list or manage enrolled devices",
224
226
  "",
225
- f" {DM}FILES & TRANSFERS{RS}",
226
- f" {C}cloud{RS} {DM}<upload|files|pipe|url…>{RS} cloud file vault, streaming, pipe & storage url",
227
- f" {C}send{RS} {DM}<USER> <FILE>{RS} end-to-end encrypted P2P file transfer",
228
- f" {C}inbox{RS} {DM}[CODE]{RS} receive & decrypt files sent to your identity",
229
- f" {C}id{RS} {DM}[--renew]{RS} view or renew your P2P identity fingerprint",
227
+ f" {DM}AI AGENTS & MCP{RS}",
228
+ f" {C}mcp{RS} {DM}<serve|share|create|list…>{RS} AI agent & MCP tool bridge (Codex, Claude, Cursor)",
230
229
  "",
231
- f" {DM}SYSTEM & SECURITY{RS}",
232
- f" {C}login{RS} {DM}[--label NAME]{RS} authenticate & name this device",
233
- f" {C}logout{RS} disconnect and clear credentials from this device",
234
- f" {C}whoami{RS} account quota, storage URL & device info",
235
- f" {C}url{RS} {DM}[NEW_URL|--reset]{RS} view or switch configured storage server URL",
236
- f" {C}version{RS} check CLI and server status & versions",
237
- f" {C}env{RS} {DM}<set|get|list…>{RS} encrypted local .env store",
238
- f" {C}check{RS} {DM}[--seal]{RS} verify source integrity & tamper detection",
230
+ f" {DM}FILES & CLOUD VAULT{RS}",
231
+ f" {C}cloud{RS} {DM}<upload|files|stream|delete…>{RS} cloud file vault & media streaming engine",
232
+ f" {C}pipe{RS} {DM}[--name NAME] [--burn]{RS} pipe stdin or command output directly to cloud vault",
239
233
  "",
240
- f" {DM}Run {RS}{C}akash <command> --help{RS}{DM} for options on any command.{RS}",
234
+ f" {DM}SECURE P2P TRANSFERS{RS}",
235
+ f" {C}send{RS} {DM}<USER> <FILE>{RS} end-to-end encrypted P2P file transfer",
236
+ f" {C}inbox{RS} {DM}[CODE]{RS} receive & decrypt files sent to your identity",
237
+ f" {C}id{RS} {DM}[--export|--add]{RS} show P2P fingerprint, export key, or add contacts",
238
+ "",
239
+ f" {DM}SECRETS & SYSTEM{RS}",
240
+ f" {C}env{RS} {DM}<set|get|list|export|push…>{RS} encrypted local .env store & cloud sync",
241
+ f" {C}log{RS} {DM}[--stats]{RS} view operation history & bandwidth summary",
242
+ f" {C}check{RS} {DM}[--seal]{RS} verify source integrity & tamper detection",
243
+ f" {C}version{RS} check CLI and server status & versions",
244
+ "",
245
+ f" {DM}Run {RS}{C}akash <command> --help{RS}{DM} for full options on any command.{RS}",
241
246
  "",
242
247
  ]
243
248
  nl = chr(10)
@@ -2670,8 +2675,10 @@ def env_pull(
2670
2675
  # AKASH AGENT & DEVICE BINDING COMMANDS
2671
2676
  # =============================================================================
2672
2677
 
2673
- agent_app = typer.Typer(help="Manage AI agents, client credentials & device approvals.")
2674
- app.add_typer(agent_app, name="agent", help="agent credentials · device binding · approvals")
2678
+ mcp_app = typer.Typer(help="Model Context Protocol (MCP) server & AI agent tool bridge.")
2679
+ agent_app = mcp_app
2680
+ app.add_typer(mcp_app, name="mcp", help="MCP server · AI agent tools · Claude/Cursor/Codex bridge")
2681
+ app.add_typer(mcp_app, name="agent", hidden=True, help="Alias for akash mcp")
2675
2682
 
2676
2683
  def _render_inspection_card(r: dict) -> None:
2677
2684
  print(f"\n {_C}╭─ ⚡ INCOMING AGENT DEVICE REGISTRATION ────────────────────────────{_RS}")
@@ -2983,30 +2990,6 @@ def agent_create(
2983
2990
  print(f" {_G}akash mcp serve --id {profile_id}{_RS}")
2984
2991
  _p()
2985
2992
 
2986
- @agent_app.command("update")
2987
- def agent_update(
2988
- client_id: str = typer.Argument(..., help="Client ID of the agent to update"),
2989
- scopes: str = typer.Option(..., "--scopes", "-s", help="New scopes or role preset (e.g., @coder, +exec:package_install)"),
2990
- url: Optional[str] = typer.Option(None, "--url", help="Override server URL"),
2991
- ):
2992
- """Update permissions/scopes for an existing AI Agent application."""
2993
- api_key, base = _resolve_client(url, require_key=False)
2994
- hdrs = {"Origin": "https://akash.cli", "x-device-id": _device_fingerprint()}
2995
- if api_key: hdrs["x-api-key"] = api_key
2996
-
2997
- with _Spin(f"updating permissions for {client_id}…"):
2998
- res = httpx.patch(
2999
- f"{base}/api/agent/app/{client_id}",
3000
- headers=hdrs,
3001
- json={"scopes": scopes},
3002
- timeout=10.0
3003
- )
3004
- if res.status_code == 200:
3005
- d = res.json()
3006
- _ok(f"Agent {_ye(client_id)} permissions updated to: {_cy(d.get('scopes', ''))}")
3007
- else:
3008
- _err(f"Failed to update scopes: {res.text}")
3009
-
3010
2993
  @agent_app.command("requests")
3011
2994
  def agent_requests(url: Optional[str] = typer.Option(None, "--url", help="Override server URL")):
3012
2995
  """List pending device registration requests."""
@@ -3094,64 +3077,11 @@ def agent_reject(request_id: str, url: Optional[str] = typer.Option(None, "--url
3094
3077
  else:
3095
3078
  _err(f"Rejection failed: {res.text}")
3096
3079
 
3097
- @app.command("guard")
3098
- def guard_console(url: Optional[str] = typer.Option(None, "--url", help="Override server URL")):
3099
- import time
3100
- """Interactive real-time terminal console that listens for agent access requests."""
3101
- api_key, base = _resolve_client(url, require_key=False)
3102
- hdrs = {"Origin": "https://akash.cli", "x-device-id": _device_fingerprint()}
3103
- if api_key: hdrs["x-api-key"] = api_key
3104
- print(f"\n {_G}◉{_RS} {_W}Akash Guard Console Online{_RS}")
3105
- print(f" {_DM}Listening for incoming agent device registrations (Ctrl-C to stop)…{_RS}\n")
3106
-
3107
- handled_requests = set()
3108
- while True:
3109
- try:
3110
- res = httpx.get(
3111
- f"{base}/api/agent/requests",
3112
- headers=hdrs,
3113
- timeout=5.0
3114
- )
3115
- if res.status_code == 200:
3116
- for r in res.json().get("requests", []):
3117
- rid = r["request_id"]
3118
- if r["status"] == "pending" and rid not in handled_requests:
3119
- _render_inspection_card(r)
3120
- try:
3121
- sys.stdout.write(f" Approve access for this device? [y/N]: ")
3122
- sys.stdout.flush()
3123
- choice = sys.stdin.readline().strip().lower()
3124
- except (KeyboardInterrupt, EOFError):
3125
- choice = "n"
3126
-
3127
- if choice == "y":
3128
- httpx.post(f"{base}/api/agent/requests/{rid}/approve", headers=hdrs, timeout=5)
3129
- _ok(f"Approved {rid} - Access token released to agent!\n")
3130
- else:
3131
- httpx.post(f"{base}/api/agent/requests/{rid}/reject", headers=hdrs, timeout=5)
3132
- _warn(f"Rejected {rid}.\n")
3133
- handled_requests.add(rid)
3134
- time.sleep(2.0)
3135
- except KeyboardInterrupt:
3136
- print(f"\n {_DM}Guard console stopped.{_RS}\n")
3137
- break
3138
- except Exception:
3139
- time.sleep(3.0)
3140
-
3141
3080
 
3142
3081
  # =============================================================================
3143
- # MODEL CONTEXT PROTOCOL (MCP) V6 POWER-SUITE
3082
+ # MODEL CONTEXT PROTOCOL (MCP) & AI AGENT ENGINE (MERGED)
3144
3083
  # =============================================================================
3145
3084
 
3146
- mcp_app = typer.Typer(help="Model Context Protocol (MCP) server for AI agents.")
3147
- app.add_typer(mcp_app, name="mcp", help="Model Context Protocol (MCP) bridge for AI agents")
3148
- mcp_app.command("guard", help="Terminal approval console for incoming agent requests")(guard_console)
3149
-
3150
- @mcp_app.command("list")
3151
- def mcp_list(raw: bool = typer.Option(False, "--json", help="Raw JSON output")):
3152
- """List all available Agent Profile IDs for MCP."""
3153
- agent_list(raw=raw)
3154
-
3155
3085
  _ASYNC_JOBS = {}
3156
3086
  _LAST_UNDO_DIR = Path.home() / ".akash" / "undo_vault"
3157
3087
 
@@ -3268,7 +3198,7 @@ def _parse_temp_duration(val: str) -> int:
3268
3198
  elif unit in ('d', 'day', 'days'): return num * 1440
3269
3199
  return num
3270
3200
 
3271
- @mcp_app.command("serve")
3201
+ @agent_app.command("serve")
3272
3202
  def mcp_serve(
3273
3203
  url: Optional[str] = typer.Option(None, "--url", help="Override server URL"),
3274
3204
  client_id: Optional[str] = typer.Option(None, "--client-id", help="Agent Client ID"),
@@ -3449,8 +3379,8 @@ def mcp_serve(
3449
3379
  print(f" {_GR}{now_t}{_RS} {_Y}⚠{_RS} {_dim(f'Worker disconnected ({e}). Reconnecting in {backoff:.1f}s (attempt {reconnect_attempts})…')}")
3450
3380
  time.sleep(backoff)
3451
3381
 
3452
- @mcp_app.command("share")
3453
- @mcp_app.command("url", hidden=True)
3382
+ @agent_app.command("share")
3383
+ @agent_app.command("url", hidden=True)
3454
3384
  def mcp_share(
3455
3385
  target: Optional[str] = typer.Argument(None, help="Profile ID (optional if only 1 profile exists)"),
3456
3386
  id: Optional[str] = typer.Option(None, "--id", "-i", help="Profile ID"),
@@ -3642,123 +3572,7 @@ def mcp_share(
3642
3572
  _p()
3643
3573
 
3644
3574
 
3645
- @mcp_app.command("url", hidden=True)
3646
- def mcp_share(
3647
- target: Optional[str] = typer.Argument(None, help="Profile ID (optional if only 1 profile exists)"),
3648
- id: Optional[str] = typer.Option(None, "--id", "-i", help="Profile ID"),
3649
- temp: Optional[str] = typer.Option(None, "--temp", "-t", help="Temporary short URL duration (e.g. '30m', '30 min', '1h')"),
3650
- codex: bool = typer.Option(False, "--codex", "-c", help="Show snippet only for Codex CLI/Desktop"),
3651
- claude: bool = typer.Option(False, "--claude", help="Show snippet only for Claude Desktop / Cursor"),
3652
- keys: bool = typer.Option(False, "--keys", "-k", help="Show raw Client ID and Secret"),
3653
- url: Optional[str] = typer.Option(None, "--url", help="Override server URL"),
3654
- ):
3655
- """Generate ready-to-copy MCP configuration for Codex, Claude, or Cursor on ANY device."""
3656
- import json, urllib.parse
3657
- from pathlib import Path
3658
-
3659
- agents_file = Path.home() / ".akash" / "agents.json"
3660
- if not agents_file.exists():
3661
- _err("No saved agent profiles found. Create one with: akash agent create")
3662
- raise typer.Exit(1)
3663
-
3664
- try:
3665
- agents = json.loads(agents_file.read_text(encoding="utf-8"))
3666
- except Exception: agents = {}
3667
-
3668
- target_id = id or target
3669
- if not target_id:
3670
- if len(agents) == 1:
3671
- target_id = next(iter(agents.keys()))
3672
- elif "af4f" in agents:
3673
- target_id = "af4f"
3674
- else:
3675
- _err("Multiple profiles found. Specify which one: akash mcp share <ID>")
3676
- raise typer.Exit(1)
3677
-
3678
- pid, cid, name = _resolve_agent_id(target_id)
3679
- profile = agents.get(pid, {})
3680
- csec = profile.get("client_secret", "")
3681
-
3682
- if not csec:
3683
- _err(f"No client_secret found for profile {pid}.")
3684
- raise typer.Exit(1)
3685
-
3686
- _, base = _resolve_client(url or profile.get("url"), require_key=False, silent=True)
3687
- dev_fp = _device_fingerprint()
3688
-
3689
- lan_ip = _get_lan_ip()
3690
- port = base.split(":")[-1] if ":" in base.replace("http://", "").replace("https://", "") else "8000"
3691
- wifi_base = f"http://{lan_ip}:{port}"
3692
-
3693
- if temp:
3694
- try:
3695
- ttl_min = _parse_temp_duration(temp)
3696
- except ValueError as ve:
3697
- _err(str(ve))
3698
- raise typer.Exit(1)
3699
-
3700
- with _Spin(f"generating {ttl_min}m short link…"):
3701
- try:
3702
- res = httpx.post(
3703
- f"{base}/api/agent/mcp/ticket",
3704
- json={"client_id": cid, "client_secret": csec, "device_fp": dev_fp, "ttl_minutes": ttl_min},
3705
- timeout=10.0
3706
- )
3707
- res.raise_for_status()
3708
- d = res.json()
3709
- tcode = d["ticket_code"]
3710
- local_url = f"{base}/mcp/t/{tcode}"
3711
- wifi_url = f"{wifi_base}/mcp/t/{tcode}"
3712
- mode_label = f"Temporary ({ttl_min} mins · Ephemeral)"
3713
- except Exception as e:
3714
- _err(f"Failed to issue temporary ticket: {e}")
3715
- raise typer.Exit(1)
3716
- else:
3717
- params = urllib.parse.urlencode({"cid": cid, "csec": csec, "dfp": dev_fp})
3718
- local_url = f"{base}/mcp?{params}"
3719
- wifi_url = f"{wifi_base}/mcp?{params}"
3720
- mode_label = "Permanent Small & Secure"
3721
-
3722
- _p()
3723
- print(" " + _C + "◈" + _RS + " " + _W + "Akash MCP · Quick Connect" + _RS)
3724
- print(" " + _GR + "──────────────────────────────────────────────────" + _RS)
3725
- print(" " + _DM + "Agent : " + _RS + _ye(name) + " " + _dim("(" + pid + ")"))
3726
- print(" " + _DM + "Mode : " + _RS + _C + mode_label + _RS)
3727
- if keys:
3728
- print(" " + _DM + "Client ID: " + _RS + _dim(cid))
3729
- print(" " + _DM + "Secret : " + _RS + _dim(csec))
3730
- print(" " + _DM + "Device FP: " + _RS + _dim(dev_fp))
3731
- print(" " + _GR + "──────────────────────────────────────────────────" + _RS)
3732
- _p()
3733
-
3734
- show_all = not codex and not claude
3735
-
3736
- if show_all or codex:
3737
- print(" " + _C + "For Codex CLI / Desktop" + _RS + " " + _dim("(~/.codex/config.toml)") + ":")
3738
- print(" " + _DM + "Option A: Same Device (Termux / Localhost)" + _RS)
3739
- print(" [mcp_servers.akash]")
3740
- print(' url = "' + local_url + '"')
3741
- if lan_ip != "127.0.0.1":
3742
- print()
3743
- print(" " + _DM + "Option B: Other Device (Over Wi-Fi)" + _RS)
3744
- print(" [mcp_servers.akash]")
3745
- print(' url = "' + wifi_url + '"')
3746
- print(" " + _GR + "──────────────────────────────────────────────────" + _RS)
3747
- _p()
3748
-
3749
- if show_all or claude:
3750
- print(" " + _C + "For Claude Desktop / Cursor" + _RS + " " + _dim("(config.json)") + ":")
3751
- claude_cfg = json.dumps({"mcpServers": {"akash": {"url": local_url}}}, indent=2)
3752
- for c_line in claude_cfg.splitlines():
3753
- print(" " + c_line)
3754
- print(" " + _GR + "──────────────────────────────────────────────────" + _RS)
3755
- _p()
3756
-
3757
- _info("Paste snippet into AI client, then keep running:")
3758
- _info(f" {_cy('akash mcp serve')}")
3759
- _p()
3760
-
3761
- @mcp_app.command("status")
3575
+ @agent_app.command("status")
3762
3576
  def mcp_status(
3763
3577
  url: Optional[str] = typer.Option(None, "--url", help="Override server URL")
3764
3578
  ):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.7.0
3
+ Version: 4.7.1
4
4
  Summary: Akash CLI — secure remote shell relay, file vault, and device management
5
5
  License-Expression: MIT
6
6
  Keywords: akash,cli,relay,remote-shell,vault
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "akashcli"
3
- version = "4.7.0"
3
+ version = "4.7.1"
4
4
  description = "Akash CLI — secure remote shell relay, file vault, and device management"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
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