akashcli 4.7.4__tar.gz → 4.7.6__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.4 → akashcli-4.7.6}/PKG-INFO +1 -1
  2. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/cli.py +49 -23
  3. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli.egg-info/PKG-INFO +1 -1
  4. {akashcli-4.7.4 → akashcli-4.7.6}/pyproject.toml +1 -1
  5. {akashcli-4.7.4 → akashcli-4.7.6}/README.md +0 -0
  6. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/__init__.py +0 -0
  7. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/_net.py +0 -0
  8. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/auth.py +0 -0
  9. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/client.py +0 -0
  10. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/config.py +0 -0
  11. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/crypto.py +0 -0
  12. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/env_manager.py +0 -0
  13. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/exceptions.py +0 -0
  14. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/exec.py +0 -0
  15. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/integrity.py +0 -0
  16. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/logger.py +0 -0
  17. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/models.py +0 -0
  18. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/p2p.py +0 -0
  19. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/packages.py +0 -0
  20. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/relay.py +0 -0
  21. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/restore.py +0 -0
  22. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/snapshot_crypto.py +0 -0
  23. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/storage.py +0 -0
  24. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/terminal.py +0 -0
  25. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/updater.py +0 -0
  26. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/uploader.py +0 -0
  27. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli/utils.py +0 -0
  28. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli.egg-info/SOURCES.txt +0 -0
  29. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli.egg-info/dependency_links.txt +0 -0
  30. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli.egg-info/entry_points.txt +0 -0
  31. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli.egg-info/requires.txt +0 -0
  32. {akashcli-4.7.4 → akashcli-4.7.6}/akashcli.egg-info/top_level.txt +0 -0
  33. {akashcli-4.7.4 → akashcli-4.7.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.7.4
3
+ Version: 4.7.6
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
@@ -65,18 +66,21 @@ class _Spin:
65
66
  import threading, time
66
67
  self._active = True
67
68
  self._t0 = time.monotonic()
68
- cr = chr(13)
69
- sys.stdout.write(cr + " " + _C + self._FRAMES[0] + _RS + " " + _DM + self._label + _RS + " ")
69
+ pfx = self._prefix + " " if self._prefix else " "
70
+ sys.stdout.write(f"\r{pfx}{_C}{self._FRAMES[0]}{_RS} {_DM}{self._label}{_RS} ")
70
71
  sys.stdout.flush()
71
72
 
72
73
  def _spin():
73
74
  time.sleep(0.04)
74
75
  while self._active:
75
76
  f = self._FRAMES[self._i % len(self._FRAMES)]
76
- sys.stdout.write(chr(13) + " " + _C + f + _RS + " " + _DM + self._label + _RS + " ")
77
+ now = time.monotonic()
78
+ dur = now - self._t0
79
+ dur_str = f" {_dim(f'({dur:.1f}s)')}" if dur >= 1.0 else ""
80
+ sys.stdout.write(f"\r\033[2K{pfx}{_C}{f}{_RS} {_DM}{self._label}{_RS}{dur_str} ")
77
81
  sys.stdout.flush()
78
82
  self._i += 1
79
- time.sleep(0.07)
83
+ time.sleep(0.06)
80
84
 
81
85
  self._thread = threading.Thread(target=_spin, daemon=True)
82
86
  self._thread.start()
@@ -95,7 +99,7 @@ class _Spin:
95
99
  self._active = False
96
100
  if self._thread:
97
101
  self._thread.join(timeout=0.2)
98
- sys.stdout.write(chr(13) + chr(27) + "[2K"); sys.stdout.flush()
102
+ sys.stdout.write("\r\033[2K")
99
103
  sys.stdout.flush()
100
104
 
101
105
  def _resolve_client(url_override: str = None, require_key: bool = False, silent: bool = False):
@@ -2792,7 +2796,7 @@ def agent_list(raw: bool = typer.Option(False, "--json", help="Raw JSON output")
2792
2796
 
2793
2797
 
2794
2798
  def _resolve_agent_id(target_id: str) -> tuple[str, str, str]:
2795
- """Resolves short Profile ID (e.g. fbf6) or Client ID into (profile_id, client_id, agent_name)."""
2799
+ """Resolves short Profile ID (e.g. aa23), Client ID, suffix, or name into (profile_id, client_id, agent_name)."""
2796
2800
  import json
2797
2801
  from pathlib import Path
2798
2802
  agents_file = Path.home() / ".akash" / "agents.json"
@@ -2801,15 +2805,32 @@ def _resolve_agent_id(target_id: str) -> tuple[str, str, str]:
2801
2805
  try: agents = json.loads(agents_file.read_text())
2802
2806
  except Exception: pass
2803
2807
 
2804
- # 1. Match by Profile ID (fbf6)
2808
+ if not target_id:
2809
+ if agents:
2810
+ first_pid = next(reversed(list(agents.keys())))
2811
+ return first_pid, agents[first_pid]["client_id"], agents[first_pid].get("name", "Agent")
2812
+ return "", "", "Agent"
2813
+
2814
+ # 1. Match by Profile ID (e.g. aa23)
2805
2815
  if target_id in agents:
2806
2816
  return target_id, agents[target_id]["client_id"], agents[target_id].get("name", "Agent")
2807
2817
 
2808
- # 2. Match by Client ID (akash_client_...)
2818
+ # 2. Match by exact Client ID
2809
2819
  for pid, d in agents.items():
2810
2820
  if d.get("client_id") == target_id:
2811
2821
  return pid, target_id, d.get("name", "Agent")
2812
2822
 
2823
+ # 3. Match by Client ID suffix (e.g. d06f) or prefix
2824
+ for pid, d in agents.items():
2825
+ cid = d.get("client_id", "")
2826
+ if cid.endswith(target_id) or cid.startswith(target_id) or (len(target_id) >= 4 and target_id in cid):
2827
+ return pid, cid, d.get("name", "Agent")
2828
+
2829
+ # 4. Match by Name (case-insensitive)
2830
+ for pid, d in agents.items():
2831
+ if d.get("name", "").lower() == target_id.lower():
2832
+ return pid, d.get("client_id", ""), d.get("name", "Agent")
2833
+
2813
2834
  return target_id, target_id, "Agent"
2814
2835
 
2815
2836
 
@@ -3310,15 +3331,19 @@ def mcp_serve(
3310
3331
  except Exception: agents = {}
3311
3332
 
3312
3333
  if not profile_id and not client_id and agents:
3313
- profile_id = "af4f" if "af4f" in agents else next(iter(agents.keys()))
3334
+ profile_id = next(reversed(list(agents.keys())))
3314
3335
 
3315
- cached = agents.get(profile_id or "", {})
3316
- cid = client_id or cached.get("client_id") or os.getenv("AKASH_CLIENT_ID")
3336
+ resolved_pid, resolved_cid, pname = _resolve_agent_id(profile_id or client_id or "")
3337
+ cached = agents.get(resolved_pid, {})
3338
+ cid = client_id or resolved_cid or cached.get("client_id") or os.getenv("AKASH_CLIENT_ID")
3317
3339
  csec = client_secret or cached.get("client_secret") or os.getenv("AKASH_CLIENT_SECRET")
3318
- pname = cached.get("name", "AI Agent")
3340
+ pname = cached.get("name", pname)
3341
+ profile_id = resolved_pid or profile_id
3319
3342
 
3320
3343
  if not cid or not csec:
3321
- _err("Missing credentials. Provide --id <ID> or create one: akash agent create")
3344
+ avail = ", ".join([f"{k} ({v.get('name', 'Agent')})" for k, v in agents.items()]) if agents else "none"
3345
+ _err(f"Missing credentials for '{profile_id or client_id or 'default'}'. Available profiles: {avail}")
3346
+ _err("Provide a valid --id <ID> or create one: akash mcp create -n Dev -r @coder")
3322
3347
  raise typer.Exit(1)
3323
3348
 
3324
3349
  _, base = _resolve_client(url or cached.get("url"), require_key=False, silent=True)
@@ -3399,6 +3424,7 @@ def mcp_serve(
3399
3424
  args = frame.get("arguments", {})
3400
3425
  t0 = time.monotonic()
3401
3426
  now_str = datetime.datetime.now().strftime("%H:%M:%S")
3427
+ ts_pfx = f" {_GR}{now_str}{_RS}"
3402
3428
 
3403
3429
  if t_name == "akash_exec_command":
3404
3430
  cmd = args.get("command", "")
@@ -3406,7 +3432,7 @@ def mcp_serve(
3406
3432
 
3407
3433
  is_safe, sec_reason = _sanitize_agent_command(cmd, scopes=cached.get("scopes", "*"))
3408
3434
  if not is_safe:
3409
- print(f" {_GR}{now_str}{_RS} {_R}🚨 Blocked{_RS} -> {disp_cmd} {_dim('(' + sec_reason + ')')}")
3435
+ print(f"{ts_pfx} {_R}🚨 Blocked{_RS} -> {disp_cmd} {_dim('(' + sec_reason + ')')}")
3410
3436
  ws.send(json.dumps({
3411
3437
  "type": "tool_response",
3412
3438
  "id": call_id,
@@ -3416,9 +3442,9 @@ def mcp_serve(
3416
3442
  if any(k in cmd for k in ["touch", "mkdir", "mv", "cp", "sed", ">", ">>", "python", "npm", "pip"]):
3417
3443
  _save_undo_checkpoint()
3418
3444
 
3419
- # Instant frame-0 animated spinner with timeout protection
3445
+ # Instant frame-0 animated spinner with live visibility
3420
3446
  try:
3421
- with _Spin(f"executing: {disp_cmd}", min_seconds=0.1):
3447
+ with _Spin(f"exec: {disp_cmd}", min_seconds=0.25, prefix=ts_pfx):
3422
3448
  r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=60)
3423
3449
  elapsed_ms = (time.monotonic() - t0) * 1000
3424
3450
  raw_out = r.stdout + r.stderr
@@ -3432,7 +3458,7 @@ def mcp_serve(
3432
3458
 
3433
3459
  ec_color = _G if returncode == 0 else _R
3434
3460
  ec_icon = f"{_G}✓{_RS}" if returncode == 0 else f"{_R}✗{_RS}"
3435
- print(f" {_GR}{now_str}{_RS} {ec_icon} {ec_color}exec:{_RS} {disp_cmd} {ec_color}[{returncode}]{_RS} {_dim(f'{elapsed_ms:.1f}ms')}")
3461
+ print(f"{ts_pfx} {ec_icon} {ec_color}exec:{_RS} {disp_cmd} {ec_color}[{returncode}]{_RS} {_dim(f'{elapsed_ms:.1f}ms')}")
3436
3462
 
3437
3463
  ws.send(json.dumps({
3438
3464
  "type": "tool_response",
@@ -3440,10 +3466,10 @@ def mcp_serve(
3440
3466
  "result": {"content": [{"type": "text", "text": bounded_out or "(no output)"}], "isError": returncode != 0}
3441
3467
  }))
3442
3468
  elif t_name == "akash_undo":
3443
- with _Spin("undo: reverting workspace to checkpoint", min_seconds=0.15):
3469
+ with _Spin("undo: reverting workspace to checkpoint", min_seconds=0.25, prefix=ts_pfx):
3444
3470
  ok, msg_txt = _restore_undo_checkpoint()
3445
3471
  icon = f"{_G}✓{_RS}" if ok else f"{_R}✗{_RS}"
3446
- print(f" {_GR}{now_str}{_RS} {icon} {(_G if ok else _R)}undo:{_RS} {_dim(msg_txt)}")
3472
+ print(f"{ts_pfx} {icon} {(_G if ok else _R)}undo:{_RS} {_dim(msg_txt)}")
3447
3473
  ws.send(json.dumps({
3448
3474
  "type": "tool_response",
3449
3475
  "id": call_id,
@@ -3453,10 +3479,10 @@ def mcp_serve(
3453
3479
  elif t_name == "akash_checkpoint_create":
3454
3480
  label = args.get("label", "agent_checkpoint")
3455
3481
  from .terminal import build_snapshot
3456
- with _Spin(f"checkpoint: {label} (packing workspace)", min_seconds=0.15):
3482
+ with _Spin(f"checkpoint: {label} (packing workspace)", min_seconds=0.25, prefix=ts_pfx):
3457
3483
  _, manifest, _ = build_snapshot(label=label)
3458
3484
  elapsed_ms = (time.monotonic() - t0) * 1000
3459
- print(f" {_GR}{now_str}{_RS} {_C}📸 checkpoint:{_RS} {_dim(label)} ({len(manifest.get('files', []))} files) {_dim(f'{elapsed_ms:.1f}ms')}")
3485
+ print(f"{ts_pfx} {_C}📸 checkpoint:{_RS} {_dim(label)} ({len(manifest.get('files', []))} files) {_dim(f'{elapsed_ms:.1f}ms')}")
3460
3486
  ws.send(json.dumps({
3461
3487
  "type": "tool_response",
3462
3488
  "id": call_id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akashcli
3
- Version: 4.7.4
3
+ Version: 4.7.6
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.4"
3
+ version = "4.7.6"
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