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.
- {akashcli-4.7.5 → akashcli-4.7.7}/PKG-INFO +1 -1
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/cli.py +59 -20
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/PKG-INFO +1 -1
- {akashcli-4.7.5 → akashcli-4.7.7}/pyproject.toml +1 -1
- {akashcli-4.7.5 → akashcli-4.7.7}/README.md +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/__init__.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/_net.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/auth.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/client.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/config.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/crypto.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/env_manager.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/exceptions.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/exec.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/integrity.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/logger.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/models.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/p2p.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/packages.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/relay.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/restore.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/snapshot_crypto.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/storage.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/terminal.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/updater.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/uploader.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli/utils.py +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/SOURCES.txt +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/dependency_links.txt +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/entry_points.txt +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/requires.txt +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/akashcli.egg-info/top_level.txt +0 -0
- {akashcli-4.7.5 → akashcli-4.7.7}/setup.cfg +0 -0
|
@@ -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
|
|
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
|
-
|
|
69
|
-
sys.stdout.write(
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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"
|
|
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
|
-
#
|
|
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"
|
|
3443
|
-
r = subprocess.run(
|
|
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"
|
|
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.
|
|
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"
|
|
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.
|
|
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"
|
|
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,
|
|
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
|
|
File without changes
|
|
File without changes
|