akashcli 4.3.2__tar.gz → 4.4.0__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.3.2 → akashcli-4.4.0}/PKG-INFO +1 -1
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/cli.py +121 -95
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/PKG-INFO +1 -1
- {akashcli-4.3.2 → akashcli-4.4.0}/pyproject.toml +1 -1
- {akashcli-4.3.2 → akashcli-4.4.0}/README.md +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/__init__.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/auth.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/client.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/config.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/crypto.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/env_manager.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/exceptions.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/exec.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/integrity.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/logger.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/models.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/p2p.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/packages.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/relay.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/restore.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/snapshot_crypto.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/storage.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/terminal.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/uploader.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/utils.py +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/SOURCES.txt +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/dependency_links.txt +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/entry_points.txt +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/requires.txt +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/top_level.txt +0 -0
- {akashcli-4.3.2 → akashcli-4.4.0}/setup.cfg +0 -0
|
@@ -142,22 +142,72 @@ def _upload_with_progress(client: AkashClient, tmp_path: str, password=None) ->
|
|
|
142
142
|
# `cloud_app` — file management (akash cloud upload/files/stream/delete/manage/bulk)
|
|
143
143
|
|
|
144
144
|
app = typer.Typer(
|
|
145
|
-
|
|
146
|
-
"akash — encrypted cloud backup for your terminal.\n\n"
|
|
147
|
-
"Quick start:\n"
|
|
148
|
-
" akash login connect your API key\n"
|
|
149
|
-
" akash backup save & sync your terminal\n"
|
|
150
|
-
" akash pull <CODE> restore on a fresh machine\n\n"
|
|
151
|
-
"Run akash cloud --help for file sync commands."
|
|
152
|
-
),
|
|
145
|
+
invoke_without_command=True,
|
|
153
146
|
rich_markup_mode=None,
|
|
154
147
|
add_completion=True,
|
|
148
|
+
no_args_is_help=False,
|
|
155
149
|
)
|
|
150
|
+
|
|
151
|
+
@app.callback(invoke_without_command=True)
|
|
152
|
+
def _main(ctx: typer.Context,
|
|
153
|
+
help: bool = typer.Option(False, "--help", "-h", is_eager=True, expose_value=False,
|
|
154
|
+
callback=lambda v: (_print_help(), raise_exit()) if v else None,
|
|
155
|
+
help="Show this message and exit.")):
|
|
156
|
+
"""akash — your encrypted terminal cloud."""
|
|
157
|
+
if ctx.invoked_subcommand is None:
|
|
158
|
+
_print_help()
|
|
159
|
+
raise typer.Exit(0)
|
|
160
|
+
|
|
161
|
+
def raise_exit():
|
|
162
|
+
raise typer.Exit(0)
|
|
163
|
+
|
|
164
|
+
def _print_help():
|
|
165
|
+
C, G, Y, DM, RS = _C, _G, _Y, _DM, _RS
|
|
166
|
+
lines = [
|
|
167
|
+
f"",
|
|
168
|
+
f" {C}akash{RS} {DM}— encrypted cloud backup · remote shell · file vault{RS}",
|
|
169
|
+
f"",
|
|
170
|
+
f" {DM}ACCOUNT{RS}",
|
|
171
|
+
f" {C}login{RS} {DM}[--label NAME]{RS} save your API key + name this device",
|
|
172
|
+
f" {C}whoami{RS} show account info and storage quota",
|
|
173
|
+
f" {C}logout{RS} clear saved credentials",
|
|
174
|
+
f"",
|
|
175
|
+
f" {DM}TERMINAL BACKUP{RS}",
|
|
176
|
+
f" {C}backup{RS} save this terminal environment to the cloud",
|
|
177
|
+
f" {C}restore{RS} restore environment from the cloud",
|
|
178
|
+
f" {C}saves{RS} list your saved environments",
|
|
179
|
+
f" {C}pull{RS} {DM}<CODE>{RS} restore on any machine — no login needed",
|
|
180
|
+
f"",
|
|
181
|
+
f" {DM}REMOTE SHELL{RS}",
|
|
182
|
+
f" {C}shell{RS} {DM}[--device NAME]{RS} open a shell here, or on another device",
|
|
183
|
+
f" {C}run{RS} {DM}<CMD> [--device NAME]{RS} run a command locally or on a device",
|
|
184
|
+
f" {C}serve{RS} {DM}[--label NAME]{RS} make this device reachable for shell/run",
|
|
185
|
+
f" {C}devices{RS} {DM}[--revoke NAME]{RS} list or revoke enrolled devices",
|
|
186
|
+
f"",
|
|
187
|
+
f" {DM}FILES{RS}",
|
|
188
|
+
f" {C}pipe{RS} {DM}[--run CMD]{RS} upload stdin or a command's output",
|
|
189
|
+
f" {C}cloud{RS} {DM}<upload|files|stream…>{RS} manage individual cloud files",
|
|
190
|
+
f"",
|
|
191
|
+
f" {DM}SECURE TRANSFER{RS}",
|
|
192
|
+
f" {C}send{RS} {DM}<USER> <FILE>{RS} encrypt and send a file to another user",
|
|
193
|
+
f" {C}inbox{RS} receive and decrypt files sent to you",
|
|
194
|
+
f" {C}id{RS} show your P2P identity / manage contacts",
|
|
195
|
+
f"",
|
|
196
|
+
f" {DM}OTHER{RS}",
|
|
197
|
+
f" {C}env{RS} {DM}<set|get|list…>{RS} encrypted local .env manager",
|
|
198
|
+
f" {C}log{RS} {DM}[--stats]{RS} operation history (--stats for summary)",
|
|
199
|
+
f" {C}check{RS} {DM}[--seal]{RS} verify file integrity (--seal to re-seal)",
|
|
200
|
+
f"",
|
|
201
|
+
f" {DM}Run {RS}{C}akash <command> --help{RS}{DM} for full options on any command.{RS}",
|
|
202
|
+
f"",
|
|
203
|
+
]
|
|
204
|
+
print("\n".join(lines))
|
|
205
|
+
|
|
156
206
|
cloud_app = typer.Typer(
|
|
157
|
-
help="
|
|
207
|
+
help="Manage individual cloud files.",
|
|
158
208
|
rich_markup_mode=None,
|
|
159
209
|
)
|
|
160
|
-
app.add_typer(cloud_app, name="cloud", help="
|
|
210
|
+
app.add_typer(cloud_app, name="cloud", help="upload · stream · delete individual files")
|
|
161
211
|
|
|
162
212
|
|
|
163
213
|
# ── Integrity check ───────────────────────────────────────────────────────────
|
|
@@ -865,44 +915,37 @@ def pull(
|
|
|
865
915
|
_print_reload_hint(meta)
|
|
866
916
|
|
|
867
917
|
|
|
868
|
-
# ── akash seal
|
|
918
|
+
# ── akash check (seal + integrity merged) ────────────────────────────────────
|
|
869
919
|
|
|
870
920
|
@app.command()
|
|
871
|
-
def
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
hashes = _seal()
|
|
876
|
-
_ok(f"hashed {len(hashes)} source files")
|
|
877
|
-
_ok(f"manifest → {_dim(str(_manifest_path()))}")
|
|
878
|
-
_info("tamper detection active for backup, restore, and pull")
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
# ── akash integrity ───────────────────────────────────────────────────────────
|
|
921
|
+
def check(
|
|
922
|
+
seal: bool = typer.Option(False, "--seal", help="Re-seal the integrity manifest"),
|
|
923
|
+
):
|
|
924
|
+
"""Verify file integrity — or re-seal with --seal.
|
|
882
925
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
926
|
+
Examples:
|
|
927
|
+
akash check verify all source files are untampered
|
|
928
|
+
akash check --seal hash source files and write a new manifest
|
|
929
|
+
"""
|
|
930
|
+
if seal:
|
|
931
|
+
from .integrity import seal as _seal, _manifest_path
|
|
932
|
+
_info("sealing source integrity…")
|
|
933
|
+
hashes = _seal()
|
|
934
|
+
_ok(f"hashed {len(hashes)} source files")
|
|
935
|
+
_ok(f"manifest → {_dim(str(_manifest_path()))}")
|
|
936
|
+
_info("tamper detection active for backup, restore, and pull")
|
|
894
937
|
else:
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
938
|
+
from .integrity import status as _status
|
|
939
|
+
s = _status()
|
|
940
|
+
if not s["sealed"]:
|
|
941
|
+
_warn(f"not sealed yet — run {_cy('akash check --seal')} to enable tamper detection")
|
|
942
|
+
return
|
|
943
|
+
if s["ok"]:
|
|
944
|
+
_ok(f"integrity OK — {s['files']} files verified")
|
|
945
|
+
else:
|
|
946
|
+
_err("tamper detected!")
|
|
947
|
+
print(s["message"], file=sys.stderr)
|
|
948
|
+
raise typer.Exit(2)
|
|
906
949
|
|
|
907
950
|
|
|
908
951
|
# ── akash logout ─────────────────────────────────────────────────────────────
|
|
@@ -1764,26 +1807,51 @@ def pipe(
|
|
|
1764
1807
|
_p()
|
|
1765
1808
|
|
|
1766
1809
|
|
|
1767
|
-
# ── akash log
|
|
1810
|
+
# ── akash log (+ --stats) ─────────────────────────────────────────────────────
|
|
1768
1811
|
|
|
1769
1812
|
@app.command(name="log")
|
|
1770
1813
|
def cmd_log(
|
|
1771
|
-
last: int = typer.Option(20,
|
|
1772
|
-
op: Optional[str] = typer.Option(None,"--op", "-o", help="Filter by operation type"),
|
|
1773
|
-
raw: bool = typer.Option(False,"--json",
|
|
1814
|
+
last: int = typer.Option(20, "--last", "-n", help="Show last N entries"),
|
|
1815
|
+
op: Optional[str] = typer.Option(None, "--op", "-o", help="Filter by operation type"),
|
|
1816
|
+
raw: bool = typer.Option(False, "--json", help="Raw JSON output"),
|
|
1817
|
+
stats: bool = typer.Option(False, "--stats", "-s", help="Show summary instead of list"),
|
|
1774
1818
|
):
|
|
1775
|
-
"""Show operation history
|
|
1819
|
+
"""Show operation history — or a summary with --stats.
|
|
1776
1820
|
|
|
1777
1821
|
Examples:
|
|
1778
1822
|
akash log last 20 operations
|
|
1823
|
+
akash log --stats summary with counts per operation
|
|
1779
1824
|
akash log --last 50 last 50 operations
|
|
1780
1825
|
akash log --op backup only backup operations
|
|
1781
1826
|
akash log --json raw JSON (pipe to jq)
|
|
1782
1827
|
"""
|
|
1783
|
-
from .logger import read_log
|
|
1828
|
+
from .logger import read_log, log_summary
|
|
1784
1829
|
|
|
1785
|
-
|
|
1830
|
+
if stats:
|
|
1831
|
+
s = log_summary()
|
|
1832
|
+
if s["total"] == 0:
|
|
1833
|
+
_info("no log entries yet — run some commands first")
|
|
1834
|
+
return
|
|
1835
|
+
_p()
|
|
1836
|
+
print(f" {_C}◈{_RS} operation history")
|
|
1837
|
+
_p()
|
|
1838
|
+
_info(f"total {_ye(str(s['total']))} "
|
|
1839
|
+
f"({_G}{s['ok']} ok{_RS}" +
|
|
1840
|
+
(f" {_R}{s['fail']} failed{_RS}" if s["fail"] else "") + ")")
|
|
1841
|
+
_p()
|
|
1842
|
+
for oper, count in sorted(s["by_op"].items(), key=lambda x: -x[1]):
|
|
1843
|
+
bar = "█" * min(count * 2, 24)
|
|
1844
|
+
print(f" {_C}{oper:<14}{_RS} {_DM}{bar}{_RS} {count}")
|
|
1845
|
+
_p()
|
|
1846
|
+
uploads = read_log(last_n=100, op_filter=None)
|
|
1847
|
+
recent = [e for e in uploads if e.get("op") in ("backup", "pipe", "send", "cloud_upload") and e.get("code")]
|
|
1848
|
+
if recent:
|
|
1849
|
+
lu = recent[-1]
|
|
1850
|
+
print(f" {_DM}last upload {_RS}{_ye(str(lu.get('code','?')))} {_dim(str(lu.get('ts',''))[:16].replace('T',' '))}")
|
|
1851
|
+
_p()
|
|
1852
|
+
return
|
|
1786
1853
|
|
|
1854
|
+
entries = read_log(last_n=last, op_filter=op)
|
|
1787
1855
|
if not entries:
|
|
1788
1856
|
_info("no log entries yet")
|
|
1789
1857
|
return
|
|
@@ -1798,11 +1866,7 @@ def cmd_log(
|
|
|
1798
1866
|
oper = e.get("op", "?")
|
|
1799
1867
|
ok = e.get("ok", True)
|
|
1800
1868
|
icon = f"{_G}✓{_RS}" if ok else f"{_R}✗{_RS}"
|
|
1801
|
-
|
|
1802
|
-
# Build a short detail string from the payload
|
|
1803
1869
|
detail_parts = []
|
|
1804
|
-
for skip in ("ts", "op", "ok", "error"):
|
|
1805
|
-
pass
|
|
1806
1870
|
for k, v in e.items():
|
|
1807
1871
|
if k in ("ts", "op", "ok"):
|
|
1808
1872
|
continue
|
|
@@ -1814,56 +1878,18 @@ def cmd_log(
|
|
|
1814
1878
|
detail_parts.append(f"{k}={_dim(str(v))}")
|
|
1815
1879
|
else:
|
|
1816
1880
|
detail_parts.append(f"{k}={_dim(str(v)[:40])}")
|
|
1817
|
-
|
|
1818
1881
|
detail = " ".join(detail_parts[:4])
|
|
1819
1882
|
print(f" {icon} {_GR}{ts}{_RS} {_C}{oper:<12}{_RS} {detail}")
|
|
1820
1883
|
_p()
|
|
1821
1884
|
|
|
1822
1885
|
|
|
1823
|
-
# ── akash stats ───────────────────────────────────────────────────────────────
|
|
1824
|
-
|
|
1825
|
-
@app.command()
|
|
1826
|
-
def stats():
|
|
1827
|
-
"""Show a summary of your akash operation history."""
|
|
1828
|
-
from .logger import log_summary, read_log
|
|
1829
|
-
|
|
1830
|
-
s = log_summary()
|
|
1831
|
-
if s["total"] == 0:
|
|
1832
|
-
_info("no log entries yet — run some commands first")
|
|
1833
|
-
return
|
|
1834
|
-
|
|
1835
|
-
_p()
|
|
1836
|
-
print(f" {_C}◈{_RS} operation history")
|
|
1837
|
-
_p()
|
|
1838
|
-
_info(f"total ops {_ye(str(s['total']))}")
|
|
1839
|
-
_info(f"succeeded {_G}{s['ok']}{_RS}")
|
|
1840
|
-
if s["fail"]:
|
|
1841
|
-
_info(f"failed {_R}{s['fail']}{_RS}")
|
|
1842
|
-
_p()
|
|
1843
|
-
print(f" {_DM}by operation:{_RS}")
|
|
1844
|
-
for op, count in sorted(s["by_op"].items(), key=lambda x: -x[1]):
|
|
1845
|
-
bar_w = min(count * 2, 20)
|
|
1846
|
-
bar = "█" * bar_w
|
|
1847
|
-
print(f" {_C}{op:<14}{_RS} {_dim(bar)} {count}")
|
|
1848
|
-
_p()
|
|
1849
|
-
|
|
1850
|
-
# Last upload code
|
|
1851
|
-
uploads = read_log(last_n=100, op_filter=None)
|
|
1852
|
-
recent = [e for e in uploads if e.get("op") in ("backup", "pipe", "send", "cloud_upload") and e.get("code")]
|
|
1853
|
-
if recent:
|
|
1854
|
-
last_up = recent[-1]
|
|
1855
|
-
print(f" {_DM}last upload {_RS}{_ye(str(last_up.get('code', '?')))} "
|
|
1856
|
-
f"{_dim(str(last_up.get('ts', ''))[:16].replace('T',' '))}")
|
|
1857
|
-
_p()
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
1886
|
# ── akash env ─────────────────────────────────────────────────────────────────
|
|
1861
1887
|
|
|
1862
1888
|
env_app = typer.Typer(
|
|
1863
|
-
help="
|
|
1889
|
+
help="Encrypted local .env store — set, get, push, pull.",
|
|
1864
1890
|
rich_markup_mode=None,
|
|
1865
1891
|
)
|
|
1866
|
-
app.add_typer(env_app, name="env", help="
|
|
1892
|
+
app.add_typer(env_app, name="env", help="set · get · push · pull encrypted .env variables")
|
|
1867
1893
|
|
|
1868
1894
|
|
|
1869
1895
|
@env_app.command("list")
|
|
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
|
|
File without changes
|