akashcli 4.6.5__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.5 → akashcli-4.6.7}/PKG-INFO +1 -1
  2. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/__init__.py +1 -1
  3. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/cli.py +69 -62
  4. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/relay.py +1 -1
  5. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/PKG-INFO +1 -1
  6. {akashcli-4.6.5 → akashcli-4.6.7}/pyproject.toml +1 -1
  7. {akashcli-4.6.5 → akashcli-4.6.7}/README.md +0 -0
  8. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/_net.py +0 -0
  9. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/auth.py +0 -0
  10. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/client.py +0 -0
  11. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/config.py +0 -0
  12. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/crypto.py +0 -0
  13. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/env_manager.py +0 -0
  14. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/exceptions.py +0 -0
  15. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/exec.py +0 -0
  16. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/integrity.py +0 -0
  17. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/logger.py +0 -0
  18. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/models.py +0 -0
  19. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/p2p.py +0 -0
  20. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/packages.py +0 -0
  21. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/restore.py +0 -0
  22. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/snapshot_crypto.py +0 -0
  23. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/storage.py +0 -0
  24. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/terminal.py +0 -0
  25. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/uploader.py +0 -0
  26. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/utils.py +0 -0
  27. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/SOURCES.txt +0 -0
  28. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/dependency_links.txt +0 -0
  29. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/entry_points.txt +0 -0
  30. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/requires.txt +0 -0
  31. {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/top_level.txt +0 -0
  32. {akashcli-4.6.5 → 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.5
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.5"
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):
@@ -155,27 +157,35 @@ app = typer.Typer(
155
157
  no_args_is_help=False,
156
158
  )
157
159
 
160
+ def raise_exit():
161
+ raise typer.Exit(0)
162
+
163
+ def _print_version(value: bool):
164
+ if value:
165
+ from . import __version__
166
+ print(f"akashcli v{__version__}")
167
+ raise typer.Exit(0)
168
+
158
169
  @app.callback(invoke_without_command=True)
159
170
  def _main(ctx: typer.Context,
160
171
  version: bool = typer.Option(False, "--version", "-v", is_eager=True, expose_value=False,
161
- callback=lambda v: (print("akashcli v1.5.0"), raise_exit()) if v else None,
172
+ callback=_print_version,
162
173
  help="Show version and exit."),
163
174
  help: bool = typer.Option(False, "--help", "-h", is_eager=True, expose_value=False,
164
175
  callback=lambda v: (_print_help(), raise_exit()) if v else None,
165
176
  help="Show this message and exit.")):
166
- """akash — your encrypted terminal cloud."""
177
+ """akash - your encrypted terminal cloud."""
167
178
  if ctx.invoked_subcommand is None:
168
179
  _print_help()
169
180
  raise typer.Exit(0)
170
181
 
171
- def raise_exit():
172
- raise typer.Exit(0)
182
+
173
183
 
174
184
  def _print_help():
175
185
  C, G, Y, DM, RS = _C, _G, _Y, _DM, _RS
176
186
  lines = [
177
187
  f"",
178
- 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}",
179
189
  f"",
180
190
  f" {DM}ACCOUNT{RS}",
181
191
  f" {C}login{RS} {DM}[--label NAME]{RS} save your API key + name this device",
@@ -186,7 +196,7 @@ def _print_help():
186
196
  f" {C}backup{RS} save this terminal environment to the cloud",
187
197
  f" {C}restore{RS} restore environment from the cloud",
188
198
  f" {C}saves{RS} list your saved environments",
189
- 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",
190
200
  f"",
191
201
  f" {DM}REMOTE SHELL{RS}",
192
202
  f" {C}shell{RS} {DM}[--device NAME]{RS} open a shell here, on a device, or local",
@@ -286,19 +296,16 @@ def version(url: Optional[str] = typer.Option(None, "--url", help="Server URL to
286
296
 
287
297
  @app.command()
288
298
  def whoami():
289
- """Show current account and storage usage."""
299
+ """Show current account and storage usage with animation."""
290
300
  try:
291
- client = AkashClient()
292
- user = client.whoami()
301
+ with _Spin("checking account credentials…"):
302
+ client = AkashClient()
303
+ user = client.whoami()
293
304
  _ok(f"account {_cy(user.owner)}")
294
305
  _info(f"storage {_ye(str(user.quota_used))}/{_ye(str(user.quota_limit))}")
295
306
  except Exception as e:
296
307
  _err(str(e))
297
308
  raise typer.Exit(1)
298
-
299
-
300
- # ── akash cloud upload ────────────────────────────────────────────────────────
301
-
302
309
  @cloud_app.command("upload")
303
310
  def cloud_upload(
304
311
  path: Optional[Path] = typer.Argument(None, help="File to upload"),
@@ -477,7 +484,7 @@ def _print_reload_hint(meta: dict):
477
484
  shell = meta.get("shell", os.getenv("SHELL", "")) if meta else os.getenv("SHELL", "")
478
485
  shell_name = Path(shell).name if shell else "bash"
479
486
  _p()
480
- print(f" {_G}↻{_RS} terminal restored — apply config now:")
487
+ print(f" {_G}↻{_RS} terminal restored - apply config now:")
481
488
  if "zsh" in shell_name:
482
489
  _info("exec zsh reload zsh entirely")
483
490
  _info("source ~/.zshrc reload config only")
@@ -503,9 +510,9 @@ def backup(
503
510
  """Save your terminal environment to the cloud.
504
511
 
505
512
  Files from --include are tiered by size:
506
- < 50 MB — packed inline into the archive
507
- 50-500 MB — uploaded separately; reference stored in akash.largefiles
508
- > 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)
509
516
 
510
517
  Package manifest (pip, apt, npm …) is recorded and reinstalls on pull.
511
518
  """
@@ -566,7 +573,7 @@ def backup(
566
573
  deduped = [e for e in lf_entries if e.get("deduped")]
567
574
  uploaded = [e for e in lf_entries if not e.get("deduped")]
568
575
  if deduped:
569
- _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")
570
577
  if uploaded:
571
578
  from .storage import human_size as _hs
572
579
  total_bytes = sum(e.get("size", 0) for e in uploaded)
@@ -587,7 +594,7 @@ def backup(
587
594
 
588
595
  has_pkgs = manifest.get("has_packages", False)
589
596
  if has_pkgs:
590
- _ok("package manifest saved — will reinstall on pull")
597
+ _ok("package manifest saved - will reinstall on pull")
591
598
  elif no_pkgs:
592
599
  _info("package manifest skipped (--no-pkgs)")
593
600
 
@@ -677,11 +684,11 @@ def restore(
677
684
  meta = manifest.get("meta", {})
678
685
 
679
686
  if dry_run:
680
- _info(f"dry run — nothing written label: {snap_label}")
687
+ _info(f"dry run - nothing written label: {snap_label}")
681
688
  pkg_manifest = result.get("pkg_manifest")
682
689
  if pkg_manifest:
683
690
  total_pkgs = sum(len(v) for v in pkg_manifest.values())
684
- _info(f"packages: {total_pkgs} ({', '.join(pkg_manifest.keys())}) — would reinstall")
691
+ _info(f"packages: {total_pkgs} ({', '.join(pkg_manifest.keys())}) - would reinstall")
685
692
  _p()
686
693
  print(f" {_DM}files that would be restored:{_RS}")
687
694
  for p in result["restored"]:
@@ -720,7 +727,7 @@ def saves(raw: bool = typer.Option(False, "--json", help="Raw JSON output")):
720
727
  snaps = list_snapshots(client)
721
728
 
722
729
  if not snaps:
723
- _info("no saves found — run `akash backup` to create one")
730
+ _info("no saves found - run `akash backup` to create one")
724
731
  return
725
732
 
726
733
  if raw:
@@ -766,7 +773,7 @@ def pull(
766
773
  password: Optional[str] = typer.Option(None, "--password","-p",help="Password for encrypted saves"),
767
774
  no_pkgs: bool = typer.Option(False,"--no-pkgs", help="Skip package reinstall"),
768
775
  ):
769
- """One-shot restore — no login needed. Works on a fresh machine.
776
+ """One-shot restore - no login needed. Works on a fresh machine.
770
777
 
771
778
  Downloads your save, decrypts it, restores dotfiles, and reinstalls
772
779
  packages automatically. Use --no-pkgs to restore dotfiles only.
@@ -811,7 +818,7 @@ def pull(
811
818
 
812
819
  _ok(f"found {_cy(filename)} {_dim(f'{size_kb:.1f} KB')}")
813
820
  if tier_info.get("device_locked"):
814
- _info("device-locked save — verifying machine fingerprint…")
821
+ _info("device-locked save - verifying machine fingerprint…")
815
822
 
816
823
  # ── Download ──────────────────────────────────────────────────────────────
817
824
  chunks: list = []
@@ -895,10 +902,10 @@ def pull(
895
902
  meta = manifest.get("meta", {})
896
903
 
897
904
  if dry_run:
898
- _info(f"dry run — nothing written label: {snap_label}")
905
+ _info(f"dry run - nothing written label: {snap_label}")
899
906
  if _bs_pkg_manifest:
900
907
  total_pkgs = sum(len(v) for v in _bs_pkg_manifest.values())
901
- _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")
902
909
  _p()
903
910
  for p in restored_list:
904
911
  _info(p)
@@ -920,7 +927,7 @@ def pull(
920
927
  def check(
921
928
  seal: bool = typer.Option(False, "--seal", help="Re-seal the integrity manifest"),
922
929
  ):
923
- """Verify file integrity — or re-seal with --seal.
930
+ """Verify file integrity - or re-seal with --seal.
924
931
 
925
932
  Examples:
926
933
  akash check verify all source files are untampered
@@ -937,10 +944,10 @@ def check(
937
944
  from .integrity import status as _status
938
945
  s = _status()
939
946
  if not s["sealed"]:
940
- _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")
941
948
  return
942
949
  if s["ok"]:
943
- _ok(f"integrity OK — {s['files']} files verified")
950
+ _ok(f"integrity OK - {s['files']} files verified")
944
951
  else:
945
952
  _err("tamper detected!")
946
953
  print(s["message"], file=sys.stderr)
@@ -968,7 +975,7 @@ def run(
968
975
  device: Optional[str] = typer.Option(None, "--device", "-d",
969
976
  help="Run on a device (set up with: akash serve --label NAME)"),
970
977
  ):
971
- """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.
972
979
 
973
980
  No login needed:
974
981
  akash run "df -h" runs locally
@@ -1032,7 +1039,7 @@ def run(
1032
1039
 
1033
1040
  if reauth:
1034
1041
  _clear_cached_token()
1035
- _info("cleared cached token — re-authenticating…")
1042
+ _info("cleared cached token - re-authenticating…")
1036
1043
 
1037
1044
  with _Spin("authenticating…"):
1038
1045
  token = acquire_exec_token(api_key, base)
@@ -1142,9 +1149,9 @@ def shell(
1142
1149
  device: Optional[str] = typer.Option(None, "--device", "-d",
1143
1150
  help="Device label to connect to (set up with: akash serve --label NAME)"),
1144
1151
  code: Optional[str] = typer.Option(None, "--code", "-c",
1145
- help="Pairing code from `akash serve` — no login needed"),
1152
+ help="Pairing code from `akash serve` - no login needed"),
1146
1153
  ):
1147
- """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.
1148
1155
 
1149
1156
  No login needed:
1150
1157
  akash shell open a local bash shell
@@ -1221,7 +1228,7 @@ def shell(
1221
1228
  # ── Server shell ──────────────────────────────────────────────────
1222
1229
  if reauth:
1223
1230
  _clear_cached_token()
1224
- _info("cleared cached token — re-authenticating…")
1231
+ _info("cleared cached token - re-authenticating…")
1225
1232
 
1226
1233
  with _Spin("authenticating…"):
1227
1234
  token = acquire_exec_token(api_key, base)
@@ -1251,7 +1258,7 @@ def devices(
1251
1258
  authorize: Optional[str] = typer.Option(None, "--authorize", "-a", help="Grant server execution access to device"),
1252
1259
  raw: bool = typer.Option(False, "--json", help="Raw JSON output"),
1253
1260
  ):
1254
- """List enrolled devices — or revoke one.
1261
+ """List enrolled devices - or revoke one.
1255
1262
 
1256
1263
  Each device you run `akash serve` on appears here.
1257
1264
  Connect from anywhere with: akash shell --device NAME
@@ -1274,13 +1281,13 @@ def devices(
1274
1281
 
1275
1282
  if not api_key:
1276
1283
  _p()
1277
- _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:")
1278
1285
  _p()
1279
- 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}")
1280
1287
  print(f" {_cy('akash login --label termux')} {_DM}(name this device){_RS}")
1281
- 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}")
1282
1289
  _p()
1283
- print(f" {_DM}Step 2 — from anywhere:{_RS}")
1290
+ print(f" {_DM}Step 2 - from anywhere:{_RS}")
1284
1291
  print(f" {_cy('akash shell --device termux')} {_DM}(open a shell on it){_RS}")
1285
1292
  print(f" {_cy('akash run --device termux \"cmd\"')} {_DM}(run a command on it){_RS}")
1286
1293
  _p()
@@ -1369,7 +1376,7 @@ def serve(
1369
1376
  stop: bool = typer.Option(False, "--stop", help="Stop background serve daemon"),
1370
1377
  share: bool = typer.Option(False, "--share", help="Guest mode: get a one-time code, no login needed"),
1371
1378
  ):
1372
- """Make this device reachable for remote shell — with or without login.
1379
+ """Make this device reachable for remote shell - with or without login.
1373
1380
 
1374
1381
  No login needed (one-time pairing code):
1375
1382
  akash serve --share prints a code, share it with anyone
@@ -1607,7 +1614,7 @@ def send(
1607
1614
  tags = make_tags(ident["fingerprint"], recip_fp)
1608
1615
  client.update_file(result.file_code, tags=tags)
1609
1616
  except Exception:
1610
- pass # Tags are best-effort — file is already uploaded
1617
+ pass # Tags are best-effort - file is already uploaded
1611
1618
 
1612
1619
  # ── 6. Report ─────────────────────────────────────────────────────────────
1613
1620
  log_op("send", ok=True, code=result.file_code, to=recip_fp, file=file.name)
@@ -1679,7 +1686,7 @@ def inbox(
1679
1686
  p2p_files.append((f, p))
1680
1687
 
1681
1688
  if not p2p_files:
1682
- _info("inbox empty — no P2P files addressed to your identity")
1689
+ _info("inbox empty - no P2P files addressed to your identity")
1683
1690
  _info(f"your fingerprint: {_ye(ident['fingerprint'])}")
1684
1691
  return
1685
1692
 
@@ -1754,7 +1761,7 @@ def _decrypt_one(code: str, ident: dict, client, out: Optional[Path],
1754
1761
 
1755
1762
  # ── Decrypt ───────────────────────────────────────────────────────────────
1756
1763
  if not is_p2p_envelope(raw):
1757
- _err("this file is not a P2P envelope — cannot decrypt")
1764
+ _err("this file is not a P2P envelope - cannot decrypt")
1758
1765
  return
1759
1766
 
1760
1767
  try:
@@ -1847,12 +1854,12 @@ def pipe(
1847
1854
  else:
1848
1855
  import sys as _sys
1849
1856
  if _sys.stdin.isatty():
1850
- _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")
1851
1858
  raise typer.Exit(1)
1852
1859
  data = _sys.stdin.buffer.read()
1853
1860
 
1854
1861
  if not data:
1855
- _err("empty input — nothing to upload")
1862
+ _err("empty input - nothing to upload")
1856
1863
  raise typer.Exit(1)
1857
1864
 
1858
1865
  size_kb = len(data) / 1024
@@ -1908,7 +1915,7 @@ def cmd_log(
1908
1915
  raw: bool = typer.Option(False, "--json", help="Raw JSON output"),
1909
1916
  stats: bool = typer.Option(False, "--stats", "-s", help="Show summary instead of list"),
1910
1917
  ):
1911
- """Show operation history — or a summary with --stats.
1918
+ """Show operation history - or a summary with --stats.
1912
1919
 
1913
1920
  Examples:
1914
1921
  akash log last 20 operations
@@ -1922,7 +1929,7 @@ def cmd_log(
1922
1929
  if stats:
1923
1930
  s = log_summary()
1924
1931
  if s["total"] == 0:
1925
- _info("no log entries yet — run some commands first")
1932
+ _info("no log entries yet - run some commands first")
1926
1933
  return
1927
1934
  _p()
1928
1935
  print(f" {_C}◈{_RS} operation history")
@@ -1978,7 +1985,7 @@ def cmd_log(
1978
1985
  # ── akash env ─────────────────────────────────────────────────────────────────
1979
1986
 
1980
1987
  env_app = typer.Typer(
1981
- help="Encrypted local .env store — set, get, push, pull.",
1988
+ help="Encrypted local .env store - set, get, push, pull.",
1982
1989
  rich_markup_mode=None,
1983
1990
  )
1984
1991
  app.add_typer(env_app, name="env", help="set · get · push · pull encrypted .env variables")
@@ -2003,14 +2010,14 @@ def env_list(name: Optional[str] = typer.Argument(None, help="Env name to list k
2003
2010
  _p()
2004
2011
  max_k = max(len(k) for k in data)
2005
2012
  for k, v in sorted(data.items()):
2006
- # Redact long secrets — show first 4 chars then ***
2013
+ # Redact long secrets - show first 4 chars then ***
2007
2014
  disp = v if len(v) <= 6 else v[:4] + "***"
2008
2015
  print(f" {_DM}{k:<{max_k}}{_RS} {disp}")
2009
2016
  _p()
2010
2017
  else:
2011
2018
  envs = list_envs()
2012
2019
  if not envs:
2013
- _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")
2014
2021
  return
2015
2022
  _p()
2016
2023
  for e in envs:
@@ -2198,7 +2205,7 @@ def env_push(
2198
2205
  ):
2199
2206
  """Upload an encrypted env backup to the cloud.
2200
2207
 
2201
- 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.
2202
2209
  Share the code with your team, then they run: akash env pull <CODE> <NAME>
2203
2210
 
2204
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.5"
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.5
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.5"
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