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.
Files changed (31) hide show
  1. {akashcli-4.3.2 → akashcli-4.4.0}/PKG-INFO +1 -1
  2. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/cli.py +121 -95
  3. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/PKG-INFO +1 -1
  4. {akashcli-4.3.2 → akashcli-4.4.0}/pyproject.toml +1 -1
  5. {akashcli-4.3.2 → akashcli-4.4.0}/README.md +0 -0
  6. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/__init__.py +0 -0
  7. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/auth.py +0 -0
  8. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/client.py +0 -0
  9. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/config.py +0 -0
  10. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/crypto.py +0 -0
  11. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/env_manager.py +0 -0
  12. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/exceptions.py +0 -0
  13. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/exec.py +0 -0
  14. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/integrity.py +0 -0
  15. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/logger.py +0 -0
  16. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/models.py +0 -0
  17. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/p2p.py +0 -0
  18. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/packages.py +0 -0
  19. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/relay.py +0 -0
  20. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/restore.py +0 -0
  21. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/snapshot_crypto.py +0 -0
  22. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/storage.py +0 -0
  23. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/terminal.py +0 -0
  24. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/uploader.py +0 -0
  25. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli/utils.py +0 -0
  26. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/SOURCES.txt +0 -0
  27. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/dependency_links.txt +0 -0
  28. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/entry_points.txt +0 -0
  29. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/requires.txt +0 -0
  30. {akashcli-4.3.2 → akashcli-4.4.0}/akashcli.egg-info/top_level.txt +0 -0
  31. {akashcli-4.3.2 → akashcli-4.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.3.2
3
+ Version: 4.4.0
4
4
  Summary: Akash CLI — secure remote shell relay, file vault, and device management
5
5
  License: MIT
6
6
  Keywords: akash,cli,relay,remote-shell,vault
@@ -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
- help=(
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="akash cloud — upload, stream, manage individual files.",
207
+ help="Manage individual cloud files.",
158
208
  rich_markup_mode=None,
159
209
  )
160
- app.add_typer(cloud_app, name="cloud", help="File sync ops (upload, stream, delete…)")
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 seal():
872
- """Hash all source files and write a signed integrity manifest."""
873
- from .integrity import seal as _seal, _manifest_path
874
- _info("sealing source integrity…")
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
- @app.command()
884
- def integrity():
885
- """Check all installed source files match the sealed manifest."""
886
- from .integrity import status as _status
887
- s = _status()
888
- if not s["sealed"]:
889
- _warn(f"not sealed: {s['message']}")
890
- _info("run `akash seal` to enable tamper detection")
891
- return
892
- if s["ok"]:
893
- _ok(f"integrity OK — {s['files']} files verified")
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
- _err("tamper detected!")
896
- print(s["message"], file=sys.stderr)
897
- raise typer.Exit(2)
898
-
899
-
900
- # ── akash docs ────────────────────────────────────────────────────────────────
901
-
902
- @app.command()
903
- def docs():
904
- """Open the documentation."""
905
- webbrowser.open("https://github.com/juniorsir/akashcli")
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, "--last", "-n", help="Show last N entries"),
1772
- op: Optional[str] = typer.Option(None,"--op", "-o", help="Filter by operation type"),
1773
- raw: bool = typer.Option(False,"--json", help="Raw JSON output"),
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 from ~/.akash/log.jsonl.
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
- entries = read_log(last_n=last, op_filter=op)
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="akash env — encrypted local .env file manager.",
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="Encrypted .env manager (set/get/list/push/pull)")
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")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.3.2
3
+ Version: 4.4.0
4
4
  Summary: Akash CLI — secure remote shell relay, file vault, and device management
5
5
  License: MIT
6
6
  Keywords: akash,cli,relay,remote-shell,vault
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "akashcli"
3
- version = "4.3.2"
3
+ version = "4.4.0"
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
File without changes