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.
- {akashcli-4.6.6 → akashcli-4.6.7}/PKG-INFO +1 -1
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/__init__.py +1 -1
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/cli.py +58 -59
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/relay.py +1 -1
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/PKG-INFO +1 -1
- {akashcli-4.6.6 → akashcli-4.6.7}/pyproject.toml +1 -1
- {akashcli-4.6.6 → akashcli-4.6.7}/README.md +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/_net.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/auth.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/client.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/config.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/crypto.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/env_manager.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/exceptions.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/exec.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/integrity.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/logger.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/models.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/p2p.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/packages.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/restore.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/snapshot_crypto.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/storage.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/terminal.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/uploader.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli/utils.py +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/SOURCES.txt +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/dependency_links.txt +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/entry_points.txt +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/requires.txt +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/akashcli.egg-info/top_level.txt +0 -0
- {akashcli-4.6.6 → akashcli-4.6.7}/setup.cfg +0 -0
|
@@ -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
|
|
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=
|
|
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}
|
|
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`
|
|
142
|
-
# `cloud_app`
|
|
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
|
|
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}
|
|
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
|
|
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
|
-
|
|
300
|
-
|
|
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
|
|
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
|
|
515
|
-
50-500 MB
|
|
516
|
-
> 500 MB
|
|
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
|
|
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
|
|
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
|
|
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())})
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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())})
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
1152
|
+
help="Pairing code from `akash serve` - no login needed"),
|
|
1154
1153
|
):
|
|
1155
|
-
"""Open a shell
|
|
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
|
|
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
|
|
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
|
|
1284
|
+
_info("no devices yet - here's how to set up remote access:")
|
|
1286
1285
|
_p()
|
|
1287
|
-
print(f" {_DM}Step 1
|
|
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
|
|
1288
|
+
print(f" {_cy('akash serve')} {_DM}(keep running - makes it reachable){_RS}")
|
|
1290
1289
|
_p()
|
|
1291
|
-
print(f" {_DM}Step 2
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|