akashcli 4.7.5__tar.gz → 4.7.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 (33) hide show
  1. {akashcli-4.7.5 → akashcli-4.7.7}/PKG-INFO +1 -1
  2. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/cli.py +59 -20
  3. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/PKG-INFO +1 -1
  4. {akashcli-4.7.5 → akashcli-4.7.7}/pyproject.toml +1 -1
  5. {akashcli-4.7.5 → akashcli-4.7.7}/README.md +0 -0
  6. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/__init__.py +0 -0
  7. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/_net.py +0 -0
  8. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/auth.py +0 -0
  9. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/client.py +0 -0
  10. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/config.py +0 -0
  11. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/crypto.py +0 -0
  12. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/env_manager.py +0 -0
  13. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/exceptions.py +0 -0
  14. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/exec.py +0 -0
  15. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/integrity.py +0 -0
  16. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/logger.py +0 -0
  17. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/models.py +0 -0
  18. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/p2p.py +0 -0
  19. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/packages.py +0 -0
  20. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/relay.py +0 -0
  21. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/restore.py +0 -0
  22. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/snapshot_crypto.py +0 -0
  23. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/storage.py +0 -0
  24. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/terminal.py +0 -0
  25. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/updater.py +0 -0
  26. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/uploader.py +0 -0
  27. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/utils.py +0 -0
  28. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/SOURCES.txt +0 -0
  29. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/dependency_links.txt +0 -0
  30. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/entry_points.txt +0 -0
  31. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/requires.txt +0 -0
  32. {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/top_level.txt +0 -0
  33. {akashcli-4.7.5 → akashcli-4.7.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.7.5
3
+ Version: 4.7.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
@@ -47,11 +47,12 @@ def _gr(s: str) -> str: return f"{_GR}{s}{_RS}"
47
47
 
48
48
  # ── Spinner (single-line, no flicker) ────────────────────────────────────────
49
49
  class _Spin:
50
- """Minimal inline spinner with instant frame-0 rendering, smooth 350ms visibility, and clean cleanup."""
50
+ """Minimal inline spinner with instant frame-0 rendering, smooth visibility, and clean cleanup."""
51
51
  _FRAMES = ("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏")
52
52
 
53
- def __init__(self, label: str, min_seconds: float = 0.35, disabled: bool = False):
53
+ def __init__(self, label: str, min_seconds: float = 0.35, disabled: bool = False, prefix: str = ""):
54
54
  self._label = label
55
+ self._prefix = prefix
55
56
  self._disabled = disabled or (not sys.stdout.isatty())
56
57
  self._min_seconds = min_seconds
57
58
  self._i = 0
@@ -61,22 +62,28 @@ class _Spin:
61
62
 
62
63
  def __enter__(self):
63
64
  if self._disabled:
65
+ pfx = self._prefix + " " if self._prefix else " "
66
+ sys.stdout.write(f"{pfx}{_C}▶{_RS} {_DM}{self._label}…{_RS}\n")
67
+ sys.stdout.flush()
64
68
  return self
65
69
  import threading, time
66
70
  self._active = True
67
71
  self._t0 = time.monotonic()
68
- cr = chr(13)
69
- sys.stdout.write(cr + " " + _C + self._FRAMES[0] + _RS + " " + _DM + self._label + _RS + " ")
72
+ pfx = self._prefix + " " if self._prefix else " "
73
+ sys.stdout.write(f"\r{pfx}{_C}{self._FRAMES[0]}{_RS} {_DM}{self._label}{_RS} ")
70
74
  sys.stdout.flush()
71
75
 
72
76
  def _spin():
73
77
  time.sleep(0.04)
74
78
  while self._active:
75
79
  f = self._FRAMES[self._i % len(self._FRAMES)]
76
- sys.stdout.write(chr(13) + " " + _C + f + _RS + " " + _DM + self._label + _RS + " ")
80
+ now = time.monotonic()
81
+ dur = now - self._t0
82
+ dur_str = f" {_dim(f'({dur:.1f}s)')}" if dur >= 0.8 else ""
83
+ sys.stdout.write(f"\r\033[2K{pfx}{_C}{f}{_RS} {_DM}{self._label}{_RS}{dur_str} ")
77
84
  sys.stdout.flush()
78
85
  self._i += 1
79
- time.sleep(0.07)
86
+ time.sleep(0.06)
80
87
 
81
88
  self._thread = threading.Thread(target=_spin, daemon=True)
82
89
  self._thread.start()
@@ -89,13 +96,13 @@ class _Spin:
89
96
  if self._disabled:
90
97
  return
91
98
  import time
99
+ self._active = False
100
+ if self._thread:
101
+ self._thread.join(timeout=0.3)
92
102
  elapsed = time.monotonic() - self._t0
93
103
  if elapsed < self._min_seconds:
94
104
  time.sleep(self._min_seconds - elapsed)
95
- self._active = False
96
- if self._thread:
97
- self._thread.join(timeout=0.2)
98
- sys.stdout.write(chr(13) + chr(27) + "[2K"); sys.stdout.flush()
105
+ sys.stdout.write("\r\033[2K")
99
106
  sys.stdout.flush()
100
107
 
101
108
  def _resolve_client(url_override: str = None, require_key: bool = False, silent: bool = False):
@@ -3247,6 +3254,18 @@ def _sanitize_agent_command(cmd: str, scopes: str = "*") -> tuple[bool, str]:
3247
3254
  if not cmd or not cmd.strip(): return False, "Empty command."
3248
3255
  clean_cmd = cmd.strip()
3249
3256
 
3257
+ # Guard against interactive commands that hang waiting for human input in automated agents
3258
+ interactive_patterns = (
3259
+ r"^\s*(nano|vi|vim|htop|top|less|more|man)\b",
3260
+ r"\bgit\s+commit\s*$",
3261
+ r"^\s*python[0-9.]*\s*$",
3262
+ r"^\s*node\s*$",
3263
+ r"^\s*cat\s*$"
3264
+ )
3265
+ for pat in interactive_patterns:
3266
+ if re.search(pat, clean_cmd):
3267
+ return False, "Interactive command requiring terminal input is not supported in automated execution."
3268
+
3250
3269
  # Unconditional critical security guard (Always enforced regardless of scopes)
3251
3270
  for pat, reason in CRITICAL_DENY_PATTERNS:
3252
3271
  if re.search(pat, clean_cmd):
@@ -3420,6 +3439,7 @@ def mcp_serve(
3420
3439
  args = frame.get("arguments", {})
3421
3440
  t0 = time.monotonic()
3422
3441
  now_str = datetime.datetime.now().strftime("%H:%M:%S")
3442
+ ts_pfx = f" {_GR}{now_str}{_RS}"
3423
3443
 
3424
3444
  if t_name == "akash_exec_command":
3425
3445
  cmd = args.get("command", "")
@@ -3427,7 +3447,7 @@ def mcp_serve(
3427
3447
 
3428
3448
  is_safe, sec_reason = _sanitize_agent_command(cmd, scopes=cached.get("scopes", "*"))
3429
3449
  if not is_safe:
3430
- print(f" {_GR}{now_str}{_RS} {_R}🚨 Blocked{_RS} -> {disp_cmd} {_dim('(' + sec_reason + ')')}")
3450
+ print(f"{ts_pfx} {_R}🚨 Blocked{_RS} -> {disp_cmd} {_dim('(' + sec_reason + ')')}")
3431
3451
  ws.send(json.dumps({
3432
3452
  "type": "tool_response",
3433
3453
  "id": call_id,
@@ -3437,23 +3457,42 @@ def mcp_serve(
3437
3457
  if any(k in cmd for k in ["touch", "mkdir", "mv", "cp", "sed", ">", ">>", "python", "npm", "pip"]):
3438
3458
  _save_undo_checkpoint()
3439
3459
 
3440
- # Instant frame-0 animated spinner with timeout protection
3460
+ # Auto-add non-interactive flags if missing
3461
+ exec_cmd = cmd
3462
+ if re.search(r"\bping\s+", exec_cmd) and "-c" not in exec_cmd:
3463
+ exec_cmd += " -c 4"
3464
+ if re.search(r"\bapt(-get)?\s+(install|upgrade|remove)\b", exec_cmd) and "-y" not in exec_cmd:
3465
+ exec_cmd += " -y"
3466
+
3467
+ # Instant frame-0 animated spinner with live visibility and timeout protection
3441
3468
  try:
3442
- with _Spin(f"executing: {disp_cmd}", min_seconds=0.1):
3443
- r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=60)
3469
+ with _Spin(f"exec: {disp_cmd}", min_seconds=0.25, prefix=ts_pfx):
3470
+ r = subprocess.run(
3471
+ exec_cmd,
3472
+ shell=True,
3473
+ capture_output=True,
3474
+ text=True,
3475
+ timeout=60,
3476
+ stdin=subprocess.DEVNULL,
3477
+ errors="replace"
3478
+ )
3444
3479
  elapsed_ms = (time.monotonic() - t0) * 1000
3445
- raw_out = r.stdout + r.stderr
3480
+ raw_out = (r.stdout or "") + (r.stderr or "")
3446
3481
  returncode = r.returncode
3447
3482
  except subprocess.TimeoutExpired:
3448
3483
  elapsed_ms = 60000.0
3449
3484
  raw_out = "⛔ Command timed out after 60 seconds."
3450
3485
  returncode = 124
3486
+ except Exception as cmd_err:
3487
+ elapsed_ms = (time.monotonic() - t0) * 1000
3488
+ raw_out = f"⛔ Command execution error: {cmd_err}"
3489
+ returncode = 1
3451
3490
 
3452
3491
  bounded_out = _smart_truncate_output(raw_out)
3453
3492
 
3454
3493
  ec_color = _G if returncode == 0 else _R
3455
3494
  ec_icon = f"{_G}✓{_RS}" if returncode == 0 else f"{_R}✗{_RS}"
3456
- print(f" {_GR}{now_str}{_RS} {ec_icon} {ec_color}exec:{_RS} {disp_cmd} {ec_color}[{returncode}]{_RS} {_dim(f'{elapsed_ms:.1f}ms')}")
3495
+ print(f"{ts_pfx} {ec_icon} {ec_color}exec:{_RS} {disp_cmd} {ec_color}[{returncode}]{_RS} {_dim(f'{elapsed_ms:.1f}ms')}")
3457
3496
 
3458
3497
  ws.send(json.dumps({
3459
3498
  "type": "tool_response",
@@ -3461,10 +3500,10 @@ def mcp_serve(
3461
3500
  "result": {"content": [{"type": "text", "text": bounded_out or "(no output)"}], "isError": returncode != 0}
3462
3501
  }))
3463
3502
  elif t_name == "akash_undo":
3464
- with _Spin("undo: reverting workspace to checkpoint", min_seconds=0.15):
3503
+ with _Spin("undo: reverting workspace to checkpoint", min_seconds=0.25, prefix=ts_pfx):
3465
3504
  ok, msg_txt = _restore_undo_checkpoint()
3466
3505
  icon = f"{_G}✓{_RS}" if ok else f"{_R}✗{_RS}"
3467
- print(f" {_GR}{now_str}{_RS} {icon} {(_G if ok else _R)}undo:{_RS} {_dim(msg_txt)}")
3506
+ print(f"{ts_pfx} {icon} {(_G if ok else _R)}undo:{_RS} {_dim(msg_txt)}")
3468
3507
  ws.send(json.dumps({
3469
3508
  "type": "tool_response",
3470
3509
  "id": call_id,
@@ -3474,10 +3513,10 @@ def mcp_serve(
3474
3513
  elif t_name == "akash_checkpoint_create":
3475
3514
  label = args.get("label", "agent_checkpoint")
3476
3515
  from .terminal import build_snapshot
3477
- with _Spin(f"checkpoint: {label} (packing workspace)", min_seconds=0.15):
3516
+ with _Spin(f"checkpoint: {label} (packing workspace)", min_seconds=0.25, prefix=ts_pfx):
3478
3517
  _, manifest, _ = build_snapshot(label=label)
3479
3518
  elapsed_ms = (time.monotonic() - t0) * 1000
3480
- print(f" {_GR}{now_str}{_RS} {_C}📸 checkpoint:{_RS} {_dim(label)} ({len(manifest.get('files', []))} files) {_dim(f'{elapsed_ms:.1f}ms')}")
3519
+ print(f"{ts_pfx} {_C}📸 checkpoint:{_RS} {_dim(label)} ({len(manifest.get('files', []))} files) {_dim(f'{elapsed_ms:.1f}ms')}")
3481
3520
  ws.send(json.dumps({
3482
3521
  "type": "tool_response",
3483
3522
  "id": call_id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.7.5
3
+ Version: 4.7.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.7.5"
3
+ version = "4.7.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
File without changes
File without changes
File without changes