akashcli 4.6.6__tar.gz → 4.6.7__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.6.6 → akashcli-4.6.7}/PKG-INFO +1 -1
  2. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/__init__.py +1 -1
  3. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/cli.py +58 -59
  4. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/relay.py +1 -1
  5. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/PKG-INFO +1 -1
  6. {akashcli-4.6.6 → akashcli-4.6.7}/pyproject.toml +1 -1
  7. {akashcli-4.6.6 → akashcli-4.6.7}/README.md +0 -0
  8. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/_net.py +0 -0
  9. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/auth.py +0 -0
  10. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/client.py +0 -0
  11. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/config.py +0 -0
  12. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/crypto.py +0 -0
  13. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/env_manager.py +0 -0
  14. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/exceptions.py +0 -0
  15. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/exec.py +0 -0
  16. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/integrity.py +0 -0
  17. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/logger.py +0 -0
  18. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/models.py +0 -0
  19. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/p2p.py +0 -0
  20. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/packages.py +0 -0
  21. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/restore.py +0 -0
  22. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/snapshot_crypto.py +0 -0
  23. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/storage.py +0 -0
  24. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/terminal.py +0 -0
  25. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/uploader.py +0 -0
  26. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/utils.py +0 -0
  27. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/SOURCES.txt +0 -0
  28. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/dependency_links.txt +0 -0
  29. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/entry_points.txt +0 -0
  30. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/requires.txt +0 -0
  31. {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/top_level.txt +0 -0
  32. {akashcli-4.6.6 → akashcli-4.6.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.6.6
3
+ Version: 4.6.7
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,6 +2,6 @@ try:
2
2
  from importlib.metadata import version
3
3
  __version__ = version("akashcli")
4
4
  except Exception:
5
- __version__ = "4.6.6"
5
+ __version__ = "4.6.7"
6
6
 
7
7
  AKASH_VERSION = __version__
@@ -40,7 +40,7 @@ def _gr(s: str) -> str: return f"{_GR}{s}{_RS}"
40
40
 
41
41
  # ── Spinner (single-line, no flicker) ────────────────────────────────────────
42
42
  class _Spin:
43
- """Minimal inline spinner — uses \r, no alternate screen, no deps."""
43
+ """Minimal inline spinner - uses \r, no alternate screen, no deps."""
44
44
  _FRAMES = ("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏")
45
45
 
46
46
  def __init__(self, label: str):
@@ -77,13 +77,12 @@ class _Spin:
77
77
  def _resolve_client(url_override: str = None, require_key: bool = False):
78
78
  """
79
79
  Return (api_key_or_None, base_url).
80
-
81
- require_key=True — hard-fail with a clear message if no key found
82
- (used by serve/devices which cannot work without server auth).
83
- require_key=False — return None for api_key if missing; caller shows warning
84
- and can offer a local/offline fallback.
80
+ require_key=True - hard-fail with a clear message if no key found
81
+ require_key=False - return None for api_key if missing; caller shows warning
85
82
  """
86
83
  import os as _os
84
+ from urllib.parse import urlparse as _up
85
+ from ._net import patch_dns_globally as _patch_dns
87
86
  config = ConfigManager.load()
88
87
  base = (
89
88
  url_override
@@ -91,6 +90,11 @@ def _resolve_client(url_override: str = None, require_key: bool = False):
91
90
  or config.get("api_url", "https://jstore.2bd.net")
92
91
  ).rstrip("/")
93
92
 
93
+ host = _up(base).hostname or ""
94
+ if host:
95
+ try: _patch_dns(host)
96
+ except Exception: pass
97
+
94
98
  api_key = _os.getenv("AKASH_API_KEY") or config.get("api_key") or None
95
99
 
96
100
  if not api_key and require_key:
@@ -99,10 +103,8 @@ def _resolve_client(url_override: str = None, require_key: bool = False):
99
103
  raise typer.Exit(1)
100
104
 
101
105
  return api_key, base
102
-
103
-
104
106
  _GUEST_WARNING = (
105
- f" {_Y}⚠{_RS} {_Y}Running as guest{_RS} — not logged in.\n"
107
+ f" {_Y}⚠{_RS} {_Y}Running as guest{_RS} - not logged in.\n"
106
108
  f" {_DM}Features are limited and your data won't be saved to the cloud.\n"
107
109
  f" Run {_RS}{_C}akash login{_RS}{_DM} to unlock full access.{_RS}"
108
110
  )
@@ -138,8 +140,8 @@ def _upload_with_progress(client: AkashClient, tmp_path: str, password=None) ->
138
140
 
139
141
 
140
142
  # ── Two-tier app structure ────────────────────────────────────────────────────
141
- # `app` — primary commands (backup / restore / pull / run / shell …)
142
- # `cloud_app` — file management (akash cloud upload/files/stream/delete/manage/bulk)
143
+ # `app` - primary commands (backup / restore / pull / run / shell …)
144
+ # `cloud_app` - file management (akash cloud upload/files/stream/delete/manage/bulk)
143
145
 
144
146
 
145
147
  def _version_callback(value: bool):
@@ -172,7 +174,7 @@ def _main(ctx: typer.Context,
172
174
  help: bool = typer.Option(False, "--help", "-h", is_eager=True, expose_value=False,
173
175
  callback=lambda v: (_print_help(), raise_exit()) if v else None,
174
176
  help="Show this message and exit.")):
175
- """akash — your encrypted terminal cloud."""
177
+ """akash - your encrypted terminal cloud."""
176
178
  if ctx.invoked_subcommand is None:
177
179
  _print_help()
178
180
  raise typer.Exit(0)
@@ -183,7 +185,7 @@ def _print_help():
183
185
  C, G, Y, DM, RS = _C, _G, _Y, _DM, _RS
184
186
  lines = [
185
187
  f"",
186
- f" {C}akash{RS} {DM}— encrypted cloud backup · remote shell · file vault{RS}",
188
+ f" {C}akash{RS} {DM}- encrypted cloud backup · remote shell · file vault{RS}",
187
189
  f"",
188
190
  f" {DM}ACCOUNT{RS}",
189
191
  f" {C}login{RS} {DM}[--label NAME]{RS} save your API key + name this device",
@@ -194,7 +196,7 @@ def _print_help():
194
196
  f" {C}backup{RS} save this terminal environment to the cloud",
195
197
  f" {C}restore{RS} restore environment from the cloud",
196
198
  f" {C}saves{RS} list your saved environments",
197
- f" {C}pull{RS} {DM}<CODE>{RS} restore on any machine — no login needed",
199
+ f" {C}pull{RS} {DM}<CODE>{RS} restore on any machine - no login needed",
198
200
  f"",
199
201
  f" {DM}REMOTE SHELL{RS}",
200
202
  f" {C}shell{RS} {DM}[--device NAME]{RS} open a shell here, on a device, or local",
@@ -294,19 +296,16 @@ def version(url: Optional[str] = typer.Option(None, "--url", help="Server URL to
294
296
 
295
297
  @app.command()
296
298
  def whoami():
297
- """Show current account and storage usage."""
299
+ """Show current account and storage usage with animation."""
298
300
  try:
299
- client = AkashClient()
300
- user = client.whoami()
301
+ with _Spin("checking account credentials…"):
302
+ client = AkashClient()
303
+ user = client.whoami()
301
304
  _ok(f"account {_cy(user.owner)}")
302
305
  _info(f"storage {_ye(str(user.quota_used))}/{_ye(str(user.quota_limit))}")
303
306
  except Exception as e:
304
307
  _err(str(e))
305
308
  raise typer.Exit(1)
306
-
307
-
308
- # ── akash cloud upload ────────────────────────────────────────────────────────
309
-
310
309
  @cloud_app.command("upload")
311
310
  def cloud_upload(
312
311
  path: Optional[Path] = typer.Argument(None, help="File to upload"),
@@ -485,7 +484,7 @@ def _print_reload_hint(meta: dict):
485
484
  shell = meta.get("shell", os.getenv("SHELL", "")) if meta else os.getenv("SHELL", "")
486
485
  shell_name = Path(shell).name if shell else "bash"
487
486
  _p()
488
- print(f" {_G}↻{_RS} terminal restored — apply config now:")
487
+ print(f" {_G}↻{_RS} terminal restored - apply config now:")
489
488
  if "zsh" in shell_name:
490
489
  _info("exec zsh reload zsh entirely")
491
490
  _info("source ~/.zshrc reload config only")
@@ -511,9 +510,9 @@ def backup(
511
510
  """Save your terminal environment to the cloud.
512
511
 
513
512
  Files from --include are tiered by size:
514
- < 50 MB — packed inline into the archive
515
- 50-500 MB — uploaded separately; reference stored in akash.largefiles
516
- > 500 MB — skipped (use --large-files to override)
513
+ < 50 MB - packed inline into the archive
514
+ 50-500 MB - uploaded separately; reference stored in akash.largefiles
515
+ > 500 MB - skipped (use --large-files to override)
517
516
 
518
517
  Package manifest (pip, apt, npm …) is recorded and reinstalls on pull.
519
518
  """
@@ -574,7 +573,7 @@ def backup(
574
573
  deduped = [e for e in lf_entries if e.get("deduped")]
575
574
  uploaded = [e for e in lf_entries if not e.get("deduped")]
576
575
  if deduped:
577
- _ok(f"{len(deduped)} large file(s) already in cloud — skipped upload")
576
+ _ok(f"{len(deduped)} large file(s) already in cloud - skipped upload")
578
577
  if uploaded:
579
578
  from .storage import human_size as _hs
580
579
  total_bytes = sum(e.get("size", 0) for e in uploaded)
@@ -595,7 +594,7 @@ def backup(
595
594
 
596
595
  has_pkgs = manifest.get("has_packages", False)
597
596
  if has_pkgs:
598
- _ok("package manifest saved — will reinstall on pull")
597
+ _ok("package manifest saved - will reinstall on pull")
599
598
  elif no_pkgs:
600
599
  _info("package manifest skipped (--no-pkgs)")
601
600
 
@@ -685,11 +684,11 @@ def restore(
685
684
  meta = manifest.get("meta", {})
686
685
 
687
686
  if dry_run:
688
- _info(f"dry run — nothing written label: {snap_label}")
687
+ _info(f"dry run - nothing written label: {snap_label}")
689
688
  pkg_manifest = result.get("pkg_manifest")
690
689
  if pkg_manifest:
691
690
  total_pkgs = sum(len(v) for v in pkg_manifest.values())
692
- _info(f"packages: {total_pkgs} ({', '.join(pkg_manifest.keys())}) — would reinstall")
691
+ _info(f"packages: {total_pkgs} ({', '.join(pkg_manifest.keys())}) - would reinstall")
693
692
  _p()
694
693
  print(f" {_DM}files that would be restored:{_RS}")
695
694
  for p in result["restored"]:
@@ -728,7 +727,7 @@ def saves(raw: bool = typer.Option(False, "--json", help="Raw JSON output")):
728
727
  snaps = list_snapshots(client)
729
728
 
730
729
  if not snaps:
731
- _info("no saves found — run `akash backup` to create one")
730
+ _info("no saves found - run `akash backup` to create one")
732
731
  return
733
732
 
734
733
  if raw:
@@ -774,7 +773,7 @@ def pull(
774
773
  password: Optional[str] = typer.Option(None, "--password","-p",help="Password for encrypted saves"),
775
774
  no_pkgs: bool = typer.Option(False,"--no-pkgs", help="Skip package reinstall"),
776
775
  ):
777
- """One-shot restore — no login needed. Works on a fresh machine.
776
+ """One-shot restore - no login needed. Works on a fresh machine.
778
777
 
779
778
  Downloads your save, decrypts it, restores dotfiles, and reinstalls
780
779
  packages automatically. Use --no-pkgs to restore dotfiles only.
@@ -819,7 +818,7 @@ def pull(
819
818
 
820
819
  _ok(f"found {_cy(filename)} {_dim(f'{size_kb:.1f} KB')}")
821
820
  if tier_info.get("device_locked"):
822
- _info("device-locked save — verifying machine fingerprint…")
821
+ _info("device-locked save - verifying machine fingerprint…")
823
822
 
824
823
  # ── Download ──────────────────────────────────────────────────────────────
825
824
  chunks: list = []
@@ -903,10 +902,10 @@ def pull(
903
902
  meta = manifest.get("meta", {})
904
903
 
905
904
  if dry_run:
906
- _info(f"dry run — nothing written label: {snap_label}")
905
+ _info(f"dry run - nothing written label: {snap_label}")
907
906
  if _bs_pkg_manifest:
908
907
  total_pkgs = sum(len(v) for v in _bs_pkg_manifest.values())
909
- _info(f"packages: {total_pkgs} ({', '.join(_bs_pkg_manifest.keys())}) — would reinstall")
908
+ _info(f"packages: {total_pkgs} ({', '.join(_bs_pkg_manifest.keys())}) - would reinstall")
910
909
  _p()
911
910
  for p in restored_list:
912
911
  _info(p)
@@ -928,7 +927,7 @@ def pull(
928
927
  def check(
929
928
  seal: bool = typer.Option(False, "--seal", help="Re-seal the integrity manifest"),
930
929
  ):
931
- """Verify file integrity — or re-seal with --seal.
930
+ """Verify file integrity - or re-seal with --seal.
932
931
 
933
932
  Examples:
934
933
  akash check verify all source files are untampered
@@ -945,10 +944,10 @@ def check(
945
944
  from .integrity import status as _status
946
945
  s = _status()
947
946
  if not s["sealed"]:
948
- _warn(f"not sealed yet — run {_cy('akash check --seal')} to enable tamper detection")
947
+ _warn(f"not sealed yet - run {_cy('akash check --seal')} to enable tamper detection")
949
948
  return
950
949
  if s["ok"]:
951
- _ok(f"integrity OK — {s['files']} files verified")
950
+ _ok(f"integrity OK - {s['files']} files verified")
952
951
  else:
953
952
  _err("tamper detected!")
954
953
  print(s["message"], file=sys.stderr)
@@ -976,7 +975,7 @@ def run(
976
975
  device: Optional[str] = typer.Option(None, "--device", "-d",
977
976
  help="Run on a device (set up with: akash serve --label NAME)"),
978
977
  ):
979
- """Run a command — locally, on the server, or on an enrolled device.
978
+ """Run a command - locally, on the server, or on an enrolled device.
980
979
 
981
980
  No login needed:
982
981
  akash run "df -h" runs locally
@@ -1040,7 +1039,7 @@ def run(
1040
1039
 
1041
1040
  if reauth:
1042
1041
  _clear_cached_token()
1043
- _info("cleared cached token — re-authenticating…")
1042
+ _info("cleared cached token - re-authenticating…")
1044
1043
 
1045
1044
  with _Spin("authenticating…"):
1046
1045
  token = acquire_exec_token(api_key, base)
@@ -1150,9 +1149,9 @@ def shell(
1150
1149
  device: Optional[str] = typer.Option(None, "--device", "-d",
1151
1150
  help="Device label to connect to (set up with: akash serve --label NAME)"),
1152
1151
  code: Optional[str] = typer.Option(None, "--code", "-c",
1153
- help="Pairing code from `akash serve` — no login needed"),
1152
+ help="Pairing code from `akash serve` - no login needed"),
1154
1153
  ):
1155
- """Open a shell — local, on a device, or via a one-time pairing code.
1154
+ """Open a shell - local, on a device, or via a one-time pairing code.
1156
1155
 
1157
1156
  No login needed:
1158
1157
  akash shell open a local bash shell
@@ -1229,7 +1228,7 @@ def shell(
1229
1228
  # ── Server shell ──────────────────────────────────────────────────
1230
1229
  if reauth:
1231
1230
  _clear_cached_token()
1232
- _info("cleared cached token — re-authenticating…")
1231
+ _info("cleared cached token - re-authenticating…")
1233
1232
 
1234
1233
  with _Spin("authenticating…"):
1235
1234
  token = acquire_exec_token(api_key, base)
@@ -1259,7 +1258,7 @@ def devices(
1259
1258
  authorize: Optional[str] = typer.Option(None, "--authorize", "-a", help="Grant server execution access to device"),
1260
1259
  raw: bool = typer.Option(False, "--json", help="Raw JSON output"),
1261
1260
  ):
1262
- """List enrolled devices — or revoke one.
1261
+ """List enrolled devices - or revoke one.
1263
1262
 
1264
1263
  Each device you run `akash serve` on appears here.
1265
1264
  Connect from anywhere with: akash shell --device NAME
@@ -1282,13 +1281,13 @@ def devices(
1282
1281
 
1283
1282
  if not api_key:
1284
1283
  _p()
1285
- _info("no devices yet — here's how to set up remote access:")
1284
+ _info("no devices yet - here's how to set up remote access:")
1286
1285
  _p()
1287
- print(f" {_DM}Step 1 — on each device you want to reach:{_RS}")
1286
+ print(f" {_DM}Step 1 - on each device you want to reach:{_RS}")
1288
1287
  print(f" {_cy('akash login --label termux')} {_DM}(name this device){_RS}")
1289
- print(f" {_cy('akash serve')} {_DM}(keep running — makes it reachable){_RS}")
1288
+ print(f" {_cy('akash serve')} {_DM}(keep running - makes it reachable){_RS}")
1290
1289
  _p()
1291
- print(f" {_DM}Step 2 — from anywhere:{_RS}")
1290
+ print(f" {_DM}Step 2 - from anywhere:{_RS}")
1292
1291
  print(f" {_cy('akash shell --device termux')} {_DM}(open a shell on it){_RS}")
1293
1292
  print(f" {_cy('akash run --device termux \"cmd\"')} {_DM}(run a command on it){_RS}")
1294
1293
  _p()
@@ -1377,7 +1376,7 @@ def serve(
1377
1376
  stop: bool = typer.Option(False, "--stop", help="Stop background serve daemon"),
1378
1377
  share: bool = typer.Option(False, "--share", help="Guest mode: get a one-time code, no login needed"),
1379
1378
  ):
1380
- """Make this device reachable for remote shell — with or without login.
1379
+ """Make this device reachable for remote shell - with or without login.
1381
1380
 
1382
1381
  No login needed (one-time pairing code):
1383
1382
  akash serve --share prints a code, share it with anyone
@@ -1615,7 +1614,7 @@ def send(
1615
1614
  tags = make_tags(ident["fingerprint"], recip_fp)
1616
1615
  client.update_file(result.file_code, tags=tags)
1617
1616
  except Exception:
1618
- pass # Tags are best-effort — file is already uploaded
1617
+ pass # Tags are best-effort - file is already uploaded
1619
1618
 
1620
1619
  # ── 6. Report ─────────────────────────────────────────────────────────────
1621
1620
  log_op("send", ok=True, code=result.file_code, to=recip_fp, file=file.name)
@@ -1687,7 +1686,7 @@ def inbox(
1687
1686
  p2p_files.append((f, p))
1688
1687
 
1689
1688
  if not p2p_files:
1690
- _info("inbox empty — no P2P files addressed to your identity")
1689
+ _info("inbox empty - no P2P files addressed to your identity")
1691
1690
  _info(f"your fingerprint: {_ye(ident['fingerprint'])}")
1692
1691
  return
1693
1692
 
@@ -1762,7 +1761,7 @@ def _decrypt_one(code: str, ident: dict, client, out: Optional[Path],
1762
1761
 
1763
1762
  # ── Decrypt ───────────────────────────────────────────────────────────────
1764
1763
  if not is_p2p_envelope(raw):
1765
- _err("this file is not a P2P envelope — cannot decrypt")
1764
+ _err("this file is not a P2P envelope - cannot decrypt")
1766
1765
  return
1767
1766
 
1768
1767
  try:
@@ -1855,12 +1854,12 @@ def pipe(
1855
1854
  else:
1856
1855
  import sys as _sys
1857
1856
  if _sys.stdin.isatty():
1858
- _err("nothing to pipe — use --run or pipe stdin: echo 'hello' | akash pipe")
1857
+ _err("nothing to pipe - use --run or pipe stdin: echo 'hello' | akash pipe")
1859
1858
  raise typer.Exit(1)
1860
1859
  data = _sys.stdin.buffer.read()
1861
1860
 
1862
1861
  if not data:
1863
- _err("empty input — nothing to upload")
1862
+ _err("empty input - nothing to upload")
1864
1863
  raise typer.Exit(1)
1865
1864
 
1866
1865
  size_kb = len(data) / 1024
@@ -1916,7 +1915,7 @@ def cmd_log(
1916
1915
  raw: bool = typer.Option(False, "--json", help="Raw JSON output"),
1917
1916
  stats: bool = typer.Option(False, "--stats", "-s", help="Show summary instead of list"),
1918
1917
  ):
1919
- """Show operation history — or a summary with --stats.
1918
+ """Show operation history - or a summary with --stats.
1920
1919
 
1921
1920
  Examples:
1922
1921
  akash log last 20 operations
@@ -1930,7 +1929,7 @@ def cmd_log(
1930
1929
  if stats:
1931
1930
  s = log_summary()
1932
1931
  if s["total"] == 0:
1933
- _info("no log entries yet — run some commands first")
1932
+ _info("no log entries yet - run some commands first")
1934
1933
  return
1935
1934
  _p()
1936
1935
  print(f" {_C}◈{_RS} operation history")
@@ -1986,7 +1985,7 @@ def cmd_log(
1986
1985
  # ── akash env ─────────────────────────────────────────────────────────────────
1987
1986
 
1988
1987
  env_app = typer.Typer(
1989
- help="Encrypted local .env store — set, get, push, pull.",
1988
+ help="Encrypted local .env store - set, get, push, pull.",
1990
1989
  rich_markup_mode=None,
1991
1990
  )
1992
1991
  app.add_typer(env_app, name="env", help="set · get · push · pull encrypted .env variables")
@@ -2011,14 +2010,14 @@ def env_list(name: Optional[str] = typer.Argument(None, help="Env name to list k
2011
2010
  _p()
2012
2011
  max_k = max(len(k) for k in data)
2013
2012
  for k, v in sorted(data.items()):
2014
- # Redact long secrets — show first 4 chars then ***
2013
+ # Redact long secrets - show first 4 chars then ***
2015
2014
  disp = v if len(v) <= 6 else v[:4] + "***"
2016
2015
  print(f" {_DM}{k:<{max_k}}{_RS} {disp}")
2017
2016
  _p()
2018
2017
  else:
2019
2018
  envs = list_envs()
2020
2019
  if not envs:
2021
- _info("no env stores yet — create with: akash env set <NAME> KEY=VALUE")
2020
+ _info("no env stores yet - create with: akash env set <NAME> KEY=VALUE")
2022
2021
  return
2023
2022
  _p()
2024
2023
  for e in envs:
@@ -2206,7 +2205,7 @@ def env_push(
2206
2205
  ):
2207
2206
  """Upload an encrypted env backup to the cloud.
2208
2207
 
2209
- The backup is machine-encrypted — only the same machine key can decrypt it.
2208
+ The backup is machine-encrypted - only the same machine key can decrypt it.
2210
2209
  Share the code with your team, then they run: akash env pull <CODE> <NAME>
2211
2210
 
2212
2211
  Example:
@@ -25,7 +25,7 @@ Wire protocol (all JSON frames over the relay WebSocket):
25
25
 
26
26
  from __future__ import annotations
27
27
 
28
- AKASH_VERSION = "4.6.6"
28
+ AKASH_VERSION = "4.6.7"
29
29
 
30
30
  import base64
31
31
  import json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.6.6
3
+ Version: 4.6.7
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.6.6"
3
+ version = "4.6.7"
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