akashcli 4.8.7__tar.gz → 4.8.8__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.8.7 → akashcli-4.8.8}/PKG-INFO +1 -1
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/__init__.py +1 -1
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/cli.py +26 -26
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli.egg-info/PKG-INFO +1 -1
- {akashcli-4.8.7 → akashcli-4.8.8}/pyproject.toml +1 -1
- {akashcli-4.8.7 → akashcli-4.8.8}/README.md +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/_net.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/auth.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/client.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/config.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/crypto.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/env_manager.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/exceptions.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/exec.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/integrity.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/logger.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/models.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/p2p.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/packages.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/relay.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/restore.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/snapshot_crypto.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/storage.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/terminal.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/updater.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/uploader.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli/utils.py +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli.egg-info/SOURCES.txt +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli.egg-info/dependency_links.txt +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli.egg-info/entry_points.txt +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli.egg-info/requires.txt +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/akashcli.egg-info/top_level.txt +0 -0
- {akashcli-4.8.7 → akashcli-4.8.8}/setup.cfg +0 -0
|
@@ -136,7 +136,7 @@ def _resolve_client(url_override: str = None, require_key: bool = False, silent:
|
|
|
136
136
|
|
|
137
137
|
|
|
138
138
|
_GUEST_WARNING = (
|
|
139
|
-
f" {_Y}
|
|
139
|
+
f" {_Y}!{_RS} {_Y}Running as guest{_RS} - not logged in.\n"
|
|
140
140
|
f" {_DM}Features are limited and your data won't be saved to the cloud.\n"
|
|
141
141
|
f" Run {_RS}{_C}akash login{_RS}{_DM} to unlock full access.{_RS}"
|
|
142
142
|
)
|
|
@@ -243,7 +243,7 @@ def _print_help():
|
|
|
243
243
|
f" {C}devices{RS} {DM}[--revoke NAME]{RS} list or manage enrolled devices",
|
|
244
244
|
"",
|
|
245
245
|
f" {DM}AI AGENTS & MCP{RS}",
|
|
246
|
-
f" {C}mcp{RS} {DM}<serve|share|create|list…>{RS} AI agent & MCP tool bridge (
|
|
246
|
+
f" {C}mcp{RS} {DM}<serve|share|create|list…>{RS} AI agent & MCP tool bridge (Cursor, Claude, ChatGPT)",
|
|
247
247
|
"",
|
|
248
248
|
f" {DM}FILES & CLOUD VAULT{RS}",
|
|
249
249
|
f" {C}cloud{RS} {DM}<upload|files|stream|delete…>{RS} cloud file vault & media streaming engine",
|
|
@@ -359,7 +359,7 @@ def update():
|
|
|
359
359
|
_p()
|
|
360
360
|
return
|
|
361
361
|
|
|
362
|
-
print(f" {_Y}
|
|
362
|
+
print(f" {_Y}◈{_RS} New version available: {_DM}{__version__}{_RS} {_C}→{_RS} {_G}{latest}{_RS}")
|
|
363
363
|
with _Spin(f"updating akashcli to v{latest}…"):
|
|
364
364
|
cmd = [sys.executable, "-m", "pip", "install", "-U", "--no-cache-dir", f"akashcli=={latest}", "--break-system-packages"]
|
|
365
365
|
res = subprocess.run(cmd, capture_output=True, text=True)
|
|
@@ -464,10 +464,10 @@ def cmd_url(
|
|
|
464
464
|
_info("To reset URL : akash url --reset")
|
|
465
465
|
_p()
|
|
466
466
|
def _storage_discontinued_notice():
|
|
467
|
-
print(f"\n{_R}
|
|
467
|
+
print(f"\n{_R}!️ [SERVICE DISCONTINUED] Cloud storage & upload services are disabled.{_RS}")
|
|
468
468
|
print(f" {_DM}Public cloud uploading has been discontinued due to lack of funds and hosting costs.{_RS}")
|
|
469
469
|
print(f" {_DM}We are actively seeking sponsors to help restore community storage infrastructure.{_RS}\n")
|
|
470
|
-
print(f" {_C}
|
|
470
|
+
print(f" {_C}Sponsoring the project:{_RS}")
|
|
471
471
|
print(f" Run {_YE}akash sponsor{_RS} or visit {_CY}https://github.com/juniorsir/akashcli#sponsorship{_RS}\n")
|
|
472
472
|
raise typer.Exit(1)
|
|
473
473
|
|
|
@@ -476,7 +476,7 @@ def _storage_discontinued_notice():
|
|
|
476
476
|
def cmd_sponsor():
|
|
477
477
|
"""Information on sponsoring Akash CLI to restore storage services."""
|
|
478
478
|
print(f"\n{_C}╔═══════════════════════════════════════════════════════════════╗{_RS}")
|
|
479
|
-
print(f"{_C}║
|
|
479
|
+
print(f"{_C}║ SPONSOR AKASH CLI INFRASTRUCTURE ║{_RS}")
|
|
480
480
|
print(f"{_C}╚═══════════════════════════════════════════════════════════════╝{_RS}\n")
|
|
481
481
|
print(f" {_B}Status Update:{_RS}")
|
|
482
482
|
print(f" Public cloud storage and file uploading services have been")
|
|
@@ -692,7 +692,7 @@ def _interactive_explorer(vfs, root_name="/"):
|
|
|
692
692
|
sys.stdout.write(f" {_GR}────────────────────────────────────────────────────────────{_RS}\r\n")
|
|
693
693
|
|
|
694
694
|
full_path = "".join([n[1] + ("/" if n[1] != "/" and not n[1].endswith("/") else "") for n in path_stack])
|
|
695
|
-
sys.stdout.write(f"
|
|
695
|
+
sys.stdout.write(f" [DIR] {_C}{full_path}{_RS}\r\n")
|
|
696
696
|
sys.stdout.write(f" {_GR}────────────────────────────────────────────────────────────{_RS}\r\n")
|
|
697
697
|
|
|
698
698
|
if not keys:
|
|
@@ -704,7 +704,7 @@ def _interactive_explorer(vfs, root_name="/"):
|
|
|
704
704
|
child = children[k]
|
|
705
705
|
is_sel = (actual_idx == selected_idx)
|
|
706
706
|
cursor = f"{_G}> {_RS}" if is_sel else " "
|
|
707
|
-
icon = "
|
|
707
|
+
icon = "[DIR]" if child["type"] == "dir" else "[FILE]"
|
|
708
708
|
|
|
709
709
|
display_name = k[:35] + ("…" if len(k) > 35 else "")
|
|
710
710
|
if child["type"] == "dir":
|
|
@@ -2704,11 +2704,11 @@ def env_pull(
|
|
|
2704
2704
|
|
|
2705
2705
|
mcp_app = typer.Typer(help="Model Context Protocol (MCP) server & AI agent tool bridge.")
|
|
2706
2706
|
agent_app = mcp_app
|
|
2707
|
-
app.add_typer(mcp_app, name="mcp", help="MCP server · AI agent tools · Claude/Cursor/
|
|
2707
|
+
app.add_typer(mcp_app, name="mcp", help="MCP server · AI agent tools · Claude/Cursor/ChatGPT bridge")
|
|
2708
2708
|
app.add_typer(mcp_app, name="agent", hidden=True, help="Alias for akash mcp")
|
|
2709
2709
|
|
|
2710
2710
|
def _render_inspection_card(r: dict) -> None:
|
|
2711
|
-
print(f"\n {_C}╭─
|
|
2711
|
+
print(f"\n {_C}╭─ ◈ INCOMING AGENT DEVICE REGISTRATION ────────────────────────────{_RS}")
|
|
2712
2712
|
print(f" {_GR}│{_RS} {_DM}Request ID :{_RS} {_ye(r['request_id'])}")
|
|
2713
2713
|
print(f" {_GR}│{_RS} {_DM}Client Name :{_RS} {_W}{r['client_name']}{_RS}")
|
|
2714
2714
|
print(f" {_GR}│{_RS} {_DM}Client ID :{_RS} {_dim(r['client_id'])}")
|
|
@@ -2868,7 +2868,7 @@ def agent_list(raw: bool = typer.Option(False, "--json", help="Raw JSON output")
|
|
|
2868
2868
|
|
|
2869
2869
|
print(f" {_GR}{'─'*76}{_RS}")
|
|
2870
2870
|
if expired_count > 0:
|
|
2871
|
-
print(f" {_Y}
|
|
2871
|
+
print(f" {_Y}◈{_RS} {_W}{expired_count} profile(s) expired.{_RS} {_DM}Run {_cy('akash mcp renew <id>')} to extend or {_cy('akash mcp rm --expired')} to clean up.{_RS}\n")
|
|
2872
2872
|
else:
|
|
2873
2873
|
_p()
|
|
2874
2874
|
print(f" {_W}Quick Launch:{_RS}")
|
|
@@ -3036,7 +3036,7 @@ def agent_info(
|
|
|
3036
3036
|
mcp_url = f"{base}/mcp?{params}"
|
|
3037
3037
|
|
|
3038
3038
|
_p()
|
|
3039
|
-
print(f" {_C}╭─
|
|
3039
|
+
print(f" {_C}╭─ AGENT PERMISSION DETAILS: {_W}{name}{_RS} {_dim('(' + pid + ')')}")
|
|
3040
3040
|
print(f" {_GR}│{_RS} {_DM}Profile ID :{_RS} {_ye(pid)}")
|
|
3041
3041
|
print(f" {_GR}│{_RS} {_DM}Client ID :{_RS} {_dim(cid)}")
|
|
3042
3042
|
st_col = _G if "active" in status.lower() else (_Y if "pending" in status.lower() else _R)
|
|
@@ -3614,7 +3614,7 @@ def _handle_tool_read_file(args: dict, scopes: str) -> tuple[str, bool]:
|
|
|
3614
3614
|
try:
|
|
3615
3615
|
target_path.relative_to(cwd)
|
|
3616
3616
|
except ValueError:
|
|
3617
|
-
return f"
|
|
3617
|
+
return f"[BLOCKED] Permission denied by scope 'fs:cwd_only': Path '{path_str}' is outside working directory ({cwd}).", True
|
|
3618
3618
|
|
|
3619
3619
|
if not target_path.exists():
|
|
3620
3620
|
return f"Error: File '{path_str}' not found.", True
|
|
@@ -3667,13 +3667,13 @@ def _handle_tool_write_file(args: dict, scopes: str) -> tuple[str, bool]:
|
|
|
3667
3667
|
scopes_list = [s.strip().lower() for s in scopes.split(",") if s.strip()]
|
|
3668
3668
|
if "*" not in scopes_list and "@full" not in scopes_list:
|
|
3669
3669
|
if "exec:read_only" in scopes_list or "@viewer" in scopes_list:
|
|
3670
|
-
return "
|
|
3670
|
+
return "[BLOCKED] Permission denied: Agent is in read-only mode ('exec:read_only'). File writing is blocked.", True
|
|
3671
3671
|
if "fs:cwd_only" in scopes_list:
|
|
3672
3672
|
cwd = Path.cwd().resolve()
|
|
3673
3673
|
try:
|
|
3674
3674
|
target_path.relative_to(cwd)
|
|
3675
3675
|
except ValueError:
|
|
3676
|
-
return f"
|
|
3676
|
+
return f"[BLOCKED] Permission denied by scope 'fs:cwd_only': Path '{path_str}' is outside working directory ({cwd}).", True
|
|
3677
3677
|
|
|
3678
3678
|
if target_path.exists() and not overwrite:
|
|
3679
3679
|
return f"Error: File '{path_str}' already exists and overwrite is set to False.", True
|
|
@@ -3704,7 +3704,7 @@ def _handle_tool_list_dir(args: dict, scopes: str) -> tuple[str, bool]:
|
|
|
3704
3704
|
try:
|
|
3705
3705
|
target_path.relative_to(cwd)
|
|
3706
3706
|
except ValueError:
|
|
3707
|
-
return f"
|
|
3707
|
+
return f"[BLOCKED] Permission denied by scope 'fs:cwd_only': Path '{path_str}' is outside working directory ({cwd}).", True
|
|
3708
3708
|
|
|
3709
3709
|
if not target_path.exists():
|
|
3710
3710
|
return f"Error: Path '{path_str}' not found.", True
|
|
@@ -3731,18 +3731,18 @@ def _handle_tool_list_dir(args: dict, scopes: str) -> tuple[str, bool]:
|
|
|
3731
3731
|
ptr = "└── " if is_last else "├── "
|
|
3732
3732
|
sub_pfx = " " if is_last else "│ "
|
|
3733
3733
|
if entry.is_dir():
|
|
3734
|
-
output_lines.append(f"{prefix}{ptr}
|
|
3734
|
+
output_lines.append(f"{prefix}{ptr}[DIR] {entry.name}/")
|
|
3735
3735
|
if cur_depth < depth and not entry.is_symlink():
|
|
3736
3736
|
_walk(entry, cur_depth + 1, prefix + sub_pfx)
|
|
3737
3737
|
elif entry.is_symlink():
|
|
3738
|
-
output_lines.append(f"{prefix}{ptr}
|
|
3738
|
+
output_lines.append(f"{prefix}{ptr}[LINK] {entry.name} -> {os.readlink(entry)}")
|
|
3739
3739
|
else:
|
|
3740
3740
|
try:
|
|
3741
3741
|
sz = entry.stat().st_size
|
|
3742
3742
|
sz_str = f"{sz / 1024:.1f}KB" if sz >= 1024 else f"{sz}B"
|
|
3743
3743
|
except Exception:
|
|
3744
3744
|
sz_str = "?"
|
|
3745
|
-
output_lines.append(f"{prefix}{ptr}
|
|
3745
|
+
output_lines.append(f"{prefix}{ptr}[FILE] {entry.name} ({sz_str})")
|
|
3746
3746
|
if len(filtered) > 100:
|
|
3747
3747
|
output_lines.append(f"{prefix}... [{len(filtered) - 100} more items omitted]")
|
|
3748
3748
|
|
|
@@ -3852,7 +3852,7 @@ def mcp_serve(
|
|
|
3852
3852
|
if ack.get("type") == "pending_approval":
|
|
3853
3853
|
req_id = ack.get("request_id", "?")
|
|
3854
3854
|
sys.stdout.write(chr(13) + chr(27) + "[2K"); sys.stdout.flush()
|
|
3855
|
-
print("\n " + _Y + "
|
|
3855
|
+
print("\n " + _Y + "◈" + _RS + " " + _W + "New device registration pending:" + _RS)
|
|
3856
3856
|
print(" " + _DM + "Request ID : " + _RS + _ye(req_id))
|
|
3857
3857
|
print(" " + _DM + "Device FP : " + _RS + _dim(dev_fp))
|
|
3858
3858
|
print()
|
|
@@ -3908,7 +3908,7 @@ def mcp_serve(
|
|
|
3908
3908
|
"type": "tool_response",
|
|
3909
3909
|
"id": call_id,
|
|
3910
3910
|
"result": {
|
|
3911
|
-
"content": [{"type": "text", "text": f"
|
|
3911
|
+
"content": [{"type": "text", "text": f"[BLOCKED] Security Policy Block: {sec_reason}\nCommand: {cmd}"}],
|
|
3912
3912
|
"isError": True
|
|
3913
3913
|
}
|
|
3914
3914
|
}))
|
|
@@ -3957,11 +3957,11 @@ def mcp_serve(
|
|
|
3957
3957
|
except Exception:
|
|
3958
3958
|
raw_out = ""
|
|
3959
3959
|
elapsed_ms = 25000.0
|
|
3960
|
-
raw_out = raw_out or "
|
|
3960
|
+
raw_out = raw_out or "[BLOCKED] Command timed out after 25 seconds."
|
|
3961
3961
|
returncode = 124
|
|
3962
3962
|
except Exception as cmd_err:
|
|
3963
3963
|
elapsed_ms = (time.monotonic() - t0) * 1000
|
|
3964
|
-
raw_out = f"
|
|
3964
|
+
raw_out = f"[BLOCKED] Command execution error: {cmd_err}"
|
|
3965
3965
|
returncode = 1
|
|
3966
3966
|
|
|
3967
3967
|
bounded_out = _smart_truncate_output(raw_out)
|
|
@@ -3973,8 +3973,8 @@ def mcp_serve(
|
|
|
3973
3973
|
|
|
3974
3974
|
if returncode != 0:
|
|
3975
3975
|
res_content = bounded_out or f"Command exited with status code {returncode} (no output)"
|
|
3976
|
-
if not res_content.startswith("
|
|
3977
|
-
res_content = f"
|
|
3976
|
+
if not res_content.startswith("[BLOCKED]"):
|
|
3977
|
+
res_content = f"[BLOCKED] Command execution failed (exit code {returncode}):\n{res_content}"
|
|
3978
3978
|
else:
|
|
3979
3979
|
res_content = bounded_out or "(command completed successfully with no output)"
|
|
3980
3980
|
|
|
@@ -4081,7 +4081,7 @@ def mcp_serve(
|
|
|
4081
4081
|
reconnect_attempts += 1
|
|
4082
4082
|
backoff = min(1.2 * (1.5 ** min(reconnect_attempts, 6)), 30.0) + random.uniform(0.1, 0.4)
|
|
4083
4083
|
now_t = datetime.datetime.now().strftime("%H:%M:%S")
|
|
4084
|
-
print(f" {_GR}{now_t}{_RS} {_Y}
|
|
4084
|
+
print(f" {_GR}{now_t}{_RS} {_Y}!{_RS} {_dim(f'Worker disconnected ({e}). Reconnecting in {backoff:.1f}s (attempt {reconnect_attempts})…')}")
|
|
4085
4085
|
time.sleep(backoff)
|
|
4086
4086
|
|
|
4087
4087
|
@agent_app.command("share")
|
|
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
|