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.
- {akashcli-4.6.5 → akashcli-4.6.7}/PKG-INFO +1 -1
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/__init__.py +1 -1
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/cli.py +69 -62
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/relay.py +1 -1
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/PKG-INFO +1 -1
- {akashcli-4.6.5 → akashcli-4.6.7}/pyproject.toml +1 -1
- {akashcli-4.6.5 → akashcli-4.6.7}/README.md +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/_net.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/auth.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/client.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/config.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/crypto.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/env_manager.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/exceptions.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/exec.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/integrity.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/logger.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/models.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/p2p.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/packages.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/restore.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/snapshot_crypto.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/storage.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/terminal.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/uploader.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli/utils.py +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/SOURCES.txt +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/dependency_links.txt +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/entry_points.txt +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/requires.txt +0 -0
- {akashcli-4.6.5 → akashcli-4.6.7}/akashcli.egg-info/top_level.txt +0 -0
- {akashcli-4.6.5 → 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):
|
|
@@ -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=
|
|
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
|
|
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
|
-
|
|
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}
|
|
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
|
|
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
|
-
|
|
292
|
-
|
|
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
|
|
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
|
|
507
|
-
50-500 MB
|
|
508
|
-
> 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)
|
|
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
|
|
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
|
|
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
|
|
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())})
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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())})
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
1152
|
+
help="Pairing code from `akash serve` - no login needed"),
|
|
1146
1153
|
):
|
|
1147
|
-
"""Open a shell
|
|
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
|
|
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
|
|
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
|
|
1284
|
+
_info("no devices yet - here's how to set up remote access:")
|
|
1278
1285
|
_p()
|
|
1279
|
-
print(f" {_DM}Step 1
|
|
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
|
|
1288
|
+
print(f" {_cy('akash serve')} {_DM}(keep running - makes it reachable){_RS}")
|
|
1282
1289
|
_p()
|
|
1283
|
-
print(f" {_DM}Step 2
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|