akashcli 4.8.3__tar.gz → 4.8.5__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.3 → akashcli-4.8.5}/PKG-INFO +1 -1
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/__init__.py +1 -1
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/cli.py +258 -35
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/client.py +1 -1
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/storage.py +4 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/uploader.py +5 -1
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli.egg-info/PKG-INFO +1 -1
- {akashcli-4.8.3 → akashcli-4.8.5}/pyproject.toml +1 -1
- {akashcli-4.8.3 → akashcli-4.8.5}/README.md +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/_net.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/auth.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/config.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/crypto.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/env_manager.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/exceptions.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/exec.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/integrity.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/logger.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/models.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/p2p.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/packages.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/relay.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/restore.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/snapshot_crypto.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/terminal.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/updater.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli/utils.py +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli.egg-info/SOURCES.txt +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli.egg-info/dependency_links.txt +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli.egg-info/entry_points.txt +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli.egg-info/requires.txt +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/akashcli.egg-info/top_level.txt +0 -0
- {akashcli-4.8.3 → akashcli-4.8.5}/setup.cfg +0 -0
|
@@ -471,8 +471,14 @@ def cloud_upload(
|
|
|
471
471
|
password: Optional[str] = typer.Option(None, "--pass", "-p", help="Password-protect"),
|
|
472
472
|
):
|
|
473
473
|
"""Upload a file or text snippet."""
|
|
474
|
+
api_key, base = _resolve_client(require_key=False, silent=True)
|
|
475
|
+
if not api_key:
|
|
476
|
+
_err("Not logged in. You must be logged in to upload files.")
|
|
477
|
+
print(f" {_DM}Run {_C}akash login{_RS}{_DM} to log in, or set {_C}AKASH_API_KEY{_RS}{_DM}.{_RS}")
|
|
478
|
+
raise typer.Exit(1)
|
|
479
|
+
|
|
474
480
|
try:
|
|
475
|
-
client = AkashClient()
|
|
481
|
+
client = AkashClient(api_key=api_key, api_url=base)
|
|
476
482
|
|
|
477
483
|
def _cb(done, total):
|
|
478
484
|
bar = _progress_bar(done, total)
|
|
@@ -500,6 +506,19 @@ def cloud_upload(
|
|
|
500
506
|
raise typer.Exit(1)
|
|
501
507
|
|
|
502
508
|
|
|
509
|
+
@app.command("upload")
|
|
510
|
+
def app_upload(
|
|
511
|
+
path: Optional[Path] = typer.Argument(None, help="File to upload"),
|
|
512
|
+
text: Optional[str] = typer.Option(None, "--text", "-t", help="Upload raw text"),
|
|
513
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="Custom filename"),
|
|
514
|
+
burn: bool = typer.Option(False, "--burn", help="Delete after first download"),
|
|
515
|
+
expiry: int = typer.Option(0, "--expiry","-e",help="Expire in N days (0 = never)"),
|
|
516
|
+
password: Optional[str] = typer.Option(None, "--pass", "-p", help="Password-protect"),
|
|
517
|
+
):
|
|
518
|
+
"""Upload a file or text snippet (requires login)."""
|
|
519
|
+
return cloud_upload(path=path, text=text, name=name, burn=burn, expiry=expiry, password=password)
|
|
520
|
+
|
|
521
|
+
|
|
503
522
|
# ── akash cloud files ─────────────────────────────────────────────────────────
|
|
504
523
|
|
|
505
524
|
@cloud_app.command("files")
|
|
@@ -604,8 +623,14 @@ def cloud_manage(
|
|
|
604
623
|
@cloud_app.command("bulk")
|
|
605
624
|
def cloud_bulk(path: Path):
|
|
606
625
|
"""Recursively upload every file in a folder."""
|
|
626
|
+
api_key, base = _resolve_client(require_key=False, silent=True)
|
|
627
|
+
if not api_key:
|
|
628
|
+
_err("Not logged in. You must be logged in to upload files.")
|
|
629
|
+
print(f" {_DM}Run {_C}akash login{_RS}{_DM} to log in, or set {_C}AKASH_API_KEY{_RS}{_DM}.{_RS}")
|
|
630
|
+
raise typer.Exit(1)
|
|
631
|
+
|
|
607
632
|
try:
|
|
608
|
-
client = AkashClient()
|
|
633
|
+
client = AkashClient(api_key=api_key, api_url=base)
|
|
609
634
|
uploader = VaultUploader(client)
|
|
610
635
|
all_files = [p for p in path.glob("**/*") if p.is_file() and not p.name.startswith(".")]
|
|
611
636
|
for i, file in enumerate(all_files, 1):
|
|
@@ -2008,8 +2033,14 @@ def send(
|
|
|
2008
2033
|
_ok(f"encrypted {_dim(f'{size_kb:.1f} KB → {enc_kb:.1f} KB')}")
|
|
2009
2034
|
|
|
2010
2035
|
# ── 4. Upload envelope ────────────────────────────────────────────────────
|
|
2036
|
+
api_key, base = _resolve_client(require_key=False, silent=True)
|
|
2037
|
+
if not api_key:
|
|
2038
|
+
_err("Not logged in. You must be logged in to send files.")
|
|
2039
|
+
print(f" {_DM}Run {_C}akash login{_RS}{_DM} to log in, or set {_C}AKASH_API_KEY{_RS}{_DM}.{_RS}")
|
|
2040
|
+
raise typer.Exit(1)
|
|
2041
|
+
|
|
2011
2042
|
try:
|
|
2012
|
-
client = AkashClient()
|
|
2043
|
+
client = AkashClient(api_key=api_key, api_url=base)
|
|
2013
2044
|
except Exception as e:
|
|
2014
2045
|
_err(f"not logged in: {e}")
|
|
2015
2046
|
raise typer.Exit(1)
|
|
@@ -2086,8 +2117,14 @@ def inbox(
|
|
|
2086
2117
|
_err(f"failed to load identity: {e}")
|
|
2087
2118
|
raise typer.Exit(1)
|
|
2088
2119
|
|
|
2120
|
+
api_key, base = _resolve_client(require_key=False, silent=True)
|
|
2121
|
+
if not api_key:
|
|
2122
|
+
_err("Not logged in. You must be logged in to view your inbox.")
|
|
2123
|
+
print(f" {_DM}Run {_C}akash login{_RS}{_DM} to log in, or set {_C}AKASH_API_KEY{_RS}{_DM}.{_RS}")
|
|
2124
|
+
raise typer.Exit(1)
|
|
2125
|
+
|
|
2089
2126
|
try:
|
|
2090
|
-
client = AkashClient()
|
|
2127
|
+
client = AkashClient(api_key=api_key, api_url=base)
|
|
2091
2128
|
except Exception as e:
|
|
2092
2129
|
_err(f"not logged in: {e}")
|
|
2093
2130
|
raise typer.Exit(1)
|
|
@@ -2264,8 +2301,14 @@ def pipe(
|
|
|
2264
2301
|
from .logger import log_op
|
|
2265
2302
|
import io
|
|
2266
2303
|
|
|
2304
|
+
api_key, base = _resolve_client(require_key=False, silent=True)
|
|
2305
|
+
if not api_key:
|
|
2306
|
+
_err("Not logged in. You must be logged in to pipe data to the cloud.")
|
|
2307
|
+
print(f" {_DM}Run {_C}akash login{_RS}{_DM} to log in, or set {_C}AKASH_API_KEY{_RS}{_DM}.{_RS}")
|
|
2308
|
+
raise typer.Exit(1)
|
|
2309
|
+
|
|
2267
2310
|
try:
|
|
2268
|
-
client = AkashClient()
|
|
2311
|
+
client = AkashClient(api_key=api_key, api_url=base)
|
|
2269
2312
|
except Exception as e:
|
|
2270
2313
|
_err(f"not logged in: {e}")
|
|
2271
2314
|
raise typer.Exit(1)
|
|
@@ -2644,8 +2687,14 @@ def env_push(
|
|
|
2644
2687
|
from .logger import log_op
|
|
2645
2688
|
import tempfile as _tf
|
|
2646
2689
|
|
|
2690
|
+
api_key, base = _resolve_client(require_key=False, silent=True)
|
|
2691
|
+
if not api_key:
|
|
2692
|
+
_err("Not logged in. You must be logged in to push environment variables.")
|
|
2693
|
+
print(f" {_DM}Run {_C}akash login{_RS}{_DM} to log in, or set {_C}AKASH_API_KEY{_RS}{_DM}.{_RS}")
|
|
2694
|
+
raise typer.Exit(1)
|
|
2695
|
+
|
|
2647
2696
|
try:
|
|
2648
|
-
client = AkashClient()
|
|
2697
|
+
client = AkashClient(api_key=api_key, api_url=base)
|
|
2649
2698
|
except Exception as e:
|
|
2650
2699
|
_err(f"not logged in: {e}")
|
|
2651
2700
|
raise typer.Exit(1)
|
|
@@ -2708,8 +2757,14 @@ def env_pull(
|
|
|
2708
2757
|
from .env_manager import env_save_blob
|
|
2709
2758
|
from .logger import log_op
|
|
2710
2759
|
|
|
2760
|
+
api_key, base = _resolve_client(require_key=False, silent=True)
|
|
2761
|
+
if not api_key:
|
|
2762
|
+
_err("Not logged in. You must be logged in to pull environment variables.")
|
|
2763
|
+
print(f" {_DM}Run {_C}akash login{_RS}{_DM} to log in, or set {_C}AKASH_API_KEY{_RS}{_DM}.{_RS}")
|
|
2764
|
+
raise typer.Exit(1)
|
|
2765
|
+
|
|
2711
2766
|
try:
|
|
2712
|
-
client = AkashClient()
|
|
2767
|
+
client = AkashClient(api_key=api_key, api_url=base)
|
|
2713
2768
|
except Exception as e:
|
|
2714
2769
|
_err(f"not logged in: {e}")
|
|
2715
2770
|
raise typer.Exit(1)
|
|
@@ -2819,11 +2874,35 @@ def agent_list(raw: bool = typer.Option(False, "--json", help="Raw JSON output")
|
|
|
2819
2874
|
print(f" {_DM}{hdr_status}{hdr_id}{hdr_name}{hdr_cid}{hdr_scopes}{_RS}")
|
|
2820
2875
|
print(f" {_GR}{'─'*76}{_RS}")
|
|
2821
2876
|
|
|
2877
|
+
import datetime
|
|
2878
|
+
now_utc = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
|
2879
|
+
|
|
2822
2880
|
last_suffix = ""
|
|
2881
|
+
expired_count = 0
|
|
2823
2882
|
for pid, data in agents.items():
|
|
2824
2883
|
is_default = (pid == default_pid)
|
|
2825
|
-
|
|
2826
|
-
|
|
2884
|
+
|
|
2885
|
+
# Check expiration status
|
|
2886
|
+
is_expired = False
|
|
2887
|
+
exp_val = data.get("expires_at")
|
|
2888
|
+
if exp_val:
|
|
2889
|
+
try:
|
|
2890
|
+
exp_dt = datetime.datetime.fromisoformat(exp_val.replace("Z", "+00:00")).replace(tzinfo=None)
|
|
2891
|
+
if exp_dt < now_utc:
|
|
2892
|
+
is_expired = True
|
|
2893
|
+
except Exception:
|
|
2894
|
+
pass
|
|
2895
|
+
|
|
2896
|
+
if is_expired:
|
|
2897
|
+
expired_count += 1
|
|
2898
|
+
status_txt = "✗ expired"
|
|
2899
|
+
status_color = _R
|
|
2900
|
+
elif is_default:
|
|
2901
|
+
status_txt = "● active"
|
|
2902
|
+
status_color = _G
|
|
2903
|
+
else:
|
|
2904
|
+
status_txt = "○ idle"
|
|
2905
|
+
status_color = _DM
|
|
2827
2906
|
|
|
2828
2907
|
name = data.get("name", "Agent")
|
|
2829
2908
|
if len(name) > 14:
|
|
@@ -2856,12 +2935,16 @@ def agent_list(raw: bool = typer.Option(False, "--json", help="Raw JSON output")
|
|
|
2856
2935
|
print(f" {c_status}{c_id}{c_name}{c_cid}{c_scopes}")
|
|
2857
2936
|
|
|
2858
2937
|
print(f" {_GR}{'─'*76}{_RS}")
|
|
2859
|
-
|
|
2938
|
+
if expired_count > 0:
|
|
2939
|
+
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")
|
|
2940
|
+
else:
|
|
2941
|
+
_p()
|
|
2860
2942
|
print(f" {_W}Quick Launch:{_RS}")
|
|
2861
2943
|
print(f" • Use default profile : {_C}akash mcp serve{_RS}")
|
|
2862
2944
|
suffix_hint = f" {_dim('(or: --id ' + last_suffix + ')')}" if last_suffix else ""
|
|
2863
2945
|
print(f" • Use specific ID : {_C}akash mcp serve --id {default_pid}{_RS}{suffix_hint}")
|
|
2864
|
-
print(f" •
|
|
2946
|
+
print(f" • Clean idle profiles : {_C}akash mcp rm --all{_RS}")
|
|
2947
|
+
print(f" • Renew expiration : {_C}akash mcp renew {default_pid} --days 30{_RS}")
|
|
2865
2948
|
print(f" • Create new profile : {_C}akash mcp create -n Dev -r @coder{_RS}")
|
|
2866
2949
|
_p()
|
|
2867
2950
|
|
|
@@ -3001,6 +3084,12 @@ def agent_info(
|
|
|
3001
3084
|
status = d.get("status", "active") + " (live server)"
|
|
3002
3085
|
exp = d.get("expires_at", "unknown")[:19].replace("T", " ")
|
|
3003
3086
|
is_live = True
|
|
3087
|
+
if pid in all_a and d.get("expires_at"):
|
|
3088
|
+
all_a[pid]["expires_at"] = d.get("expires_at")
|
|
3089
|
+
try:
|
|
3090
|
+
agents_file.write_text(json.dumps(all_a, indent=2))
|
|
3091
|
+
except Exception:
|
|
3092
|
+
pass
|
|
3004
3093
|
elif r.status_code == 404:
|
|
3005
3094
|
status = "not found on server"
|
|
3006
3095
|
except Exception:
|
|
@@ -3075,42 +3164,175 @@ def agent_update(
|
|
|
3075
3164
|
else:
|
|
3076
3165
|
_err(f"Failed to update scopes: {res.text}")
|
|
3077
3166
|
|
|
3078
|
-
@agent_app.command("
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3167
|
+
@agent_app.command("rm")
|
|
3168
|
+
@agent_app.command("remove", hidden=True)
|
|
3169
|
+
@agent_app.command("delete", hidden=True)
|
|
3170
|
+
@agent_app.command("revoke", hidden=True)
|
|
3171
|
+
def agent_remove(
|
|
3172
|
+
target: Optional[str] = typer.Argument(None, help="Profile ID (e.g. 88d7), Client ID suffix (e.g. ddff), or name"),
|
|
3173
|
+
all_idle: bool = typer.Option(False, "--all", "-a", help="Remove all idle profiles, keeping active default"),
|
|
3174
|
+
expired_only: bool = typer.Option(False, "--expired", "-e", help="Remove all expired profiles"),
|
|
3175
|
+
yes: bool = typer.Option(False, "--yes", "-y", "--force", "-f", help="Skip confirmation prompt"),
|
|
3176
|
+
url: Optional[str] = typer.Option(None, "--url", help="Override server URL"),
|
|
3083
3177
|
):
|
|
3084
|
-
"""
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3178
|
+
"""Remove / clean up one or more MCP profile IDs locally and on gateway."""
|
|
3179
|
+
import json, datetime
|
|
3180
|
+
from pathlib import Path
|
|
3181
|
+
agents_file = Path.home() / ".akash" / "agents.json"
|
|
3182
|
+
if not agents_file.exists():
|
|
3183
|
+
_info("No saved profiles to remove.")
|
|
3184
|
+
return
|
|
3185
|
+
|
|
3186
|
+
try:
|
|
3187
|
+
agents = json.loads(agents_file.read_text(encoding="utf-8"))
|
|
3188
|
+
except Exception:
|
|
3189
|
+
agents = {}
|
|
3190
|
+
|
|
3191
|
+
if not agents:
|
|
3192
|
+
_info("No saved profiles to remove.")
|
|
3193
|
+
return
|
|
3194
|
+
|
|
3195
|
+
default_pid = next(reversed(list(agents.keys())))
|
|
3196
|
+
now_utc = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
|
3197
|
+
|
|
3198
|
+
targets_to_delete = []
|
|
3199
|
+
|
|
3200
|
+
if all_idle:
|
|
3201
|
+
for pid in list(agents.keys()):
|
|
3202
|
+
if pid != default_pid:
|
|
3203
|
+
targets_to_delete.append(pid)
|
|
3204
|
+
if not targets_to_delete:
|
|
3205
|
+
_info("No idle profiles to remove (only active profile exists).")
|
|
3206
|
+
return
|
|
3207
|
+
if not yes:
|
|
3208
|
+
try:
|
|
3209
|
+
sys.stdout.write(f" Remove {len(targets_to_delete)} idle profile(s)? [y/N]: ")
|
|
3210
|
+
sys.stdout.flush()
|
|
3211
|
+
ans = sys.stdin.readline().strip().lower()
|
|
3212
|
+
if ans not in ("y", "yes"):
|
|
3213
|
+
_info("Aborted.")
|
|
3214
|
+
return
|
|
3215
|
+
except (KeyboardInterrupt, EOFError):
|
|
3216
|
+
return
|
|
3217
|
+
|
|
3218
|
+
elif expired_only:
|
|
3219
|
+
for pid, d in list(agents.items()):
|
|
3220
|
+
exp_str = d.get("expires_at")
|
|
3221
|
+
if exp_str:
|
|
3222
|
+
try:
|
|
3223
|
+
exp_dt = datetime.datetime.fromisoformat(exp_str.replace("Z", "+00:00")).replace(tzinfo=None)
|
|
3224
|
+
if exp_dt < now_utc:
|
|
3225
|
+
targets_to_delete.append(pid)
|
|
3226
|
+
except Exception:
|
|
3227
|
+
pass
|
|
3228
|
+
if not targets_to_delete:
|
|
3229
|
+
_info("No expired profiles found to remove.")
|
|
3230
|
+
return
|
|
3231
|
+
|
|
3232
|
+
elif target:
|
|
3233
|
+
pid, cid, name = _resolve_agent_id(target)
|
|
3234
|
+
if pid not in agents:
|
|
3235
|
+
_err(f"Profile '{target}' not found in saved profiles.")
|
|
3236
|
+
raise typer.Exit(1)
|
|
3237
|
+
targets_to_delete.append(pid)
|
|
3238
|
+
|
|
3239
|
+
else:
|
|
3240
|
+
_err("Specify a Profile ID to remove (e.g. 'akash mcp rm 88d7'), or use '--all' / '--expired'.")
|
|
3241
|
+
_info(f"Run {_cy('akash mcp')} to view all IDs.")
|
|
3088
3242
|
raise typer.Exit(1)
|
|
3089
3243
|
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3244
|
+
_p()
|
|
3245
|
+
removed_count = 0
|
|
3246
|
+
for pid in targets_to_delete:
|
|
3247
|
+
d = agents.get(pid, {})
|
|
3248
|
+
name = d.get("name", "Agent")
|
|
3249
|
+
cid = d.get("client_id", "")
|
|
3250
|
+
suffix = cid[-4:] if len(cid) >= 4 else ""
|
|
3094
3251
|
|
|
3095
|
-
|
|
3096
|
-
|
|
3252
|
+
# Best-effort remote revocation on gateway
|
|
3253
|
+
target_url = url or d.get("url")
|
|
3254
|
+
_, base = _resolve_client(target_url, require_key=False, silent=True)
|
|
3255
|
+
if cid and base:
|
|
3256
|
+
try:
|
|
3257
|
+
httpx.delete(f"{base}/api/agent/app/{cid}", timeout=3.0)
|
|
3258
|
+
except Exception:
|
|
3259
|
+
pass
|
|
3260
|
+
|
|
3261
|
+
if pid in agents:
|
|
3262
|
+
del agents[pid]
|
|
3263
|
+
removed_count += 1
|
|
3264
|
+
print(f" {_G}✓{_RS} Removed profile {_W}{name}{_RS} ({_ye(pid)}{_dim(' · ...' + suffix if suffix else '')})")
|
|
3097
3265
|
|
|
3098
|
-
|
|
3266
|
+
try:
|
|
3267
|
+
agents_file.write_text(json.dumps(agents, indent=2))
|
|
3268
|
+
except Exception:
|
|
3269
|
+
pass
|
|
3270
|
+
|
|
3271
|
+
_p()
|
|
3272
|
+
_ok(f"Cleaned up {removed_count} MCP profile(s). {len(agents)} profile(s) remaining.")
|
|
3273
|
+
_p()
|
|
3274
|
+
|
|
3275
|
+
@agent_app.command("renew")
|
|
3276
|
+
def agent_renew(
|
|
3277
|
+
target: Optional[str] = typer.Argument(None, help="Profile ID, Client ID suffix, or name (defaults to active profile)"),
|
|
3278
|
+
days: int = typer.Option(7, "--days", "-d", help="Number of days to extend validity (default: 7)"),
|
|
3279
|
+
url: Optional[str] = typer.Option(None, "--url", help="Override server URL"),
|
|
3280
|
+
):
|
|
3281
|
+
"""Renew/extend the validity of an MCP profile / agent ID."""
|
|
3099
3282
|
import json
|
|
3100
3283
|
from pathlib import Path
|
|
3101
3284
|
agents_file = Path.home() / ".akash" / "agents.json"
|
|
3102
|
-
if agents_file.exists():
|
|
3285
|
+
if not agents_file.exists():
|
|
3286
|
+
_err("No saved profiles found.")
|
|
3287
|
+
raise typer.Exit(1)
|
|
3288
|
+
|
|
3289
|
+
try:
|
|
3290
|
+
agents = json.loads(agents_file.read_text(encoding="utf-8"))
|
|
3291
|
+
except Exception:
|
|
3292
|
+
agents = {}
|
|
3293
|
+
|
|
3294
|
+
if not agents:
|
|
3295
|
+
_err("No saved profiles found.")
|
|
3296
|
+
raise typer.Exit(1)
|
|
3297
|
+
|
|
3298
|
+
target_id = target
|
|
3299
|
+
if not target_id:
|
|
3300
|
+
target_id = next(reversed(list(agents.keys())))
|
|
3301
|
+
|
|
3302
|
+
pid, cid, name = _resolve_agent_id(target_id)
|
|
3303
|
+
cached = agents.get(pid, {})
|
|
3304
|
+
target_url = url or cached.get("url")
|
|
3305
|
+
_, base = _resolve_client(target_url, require_key=False, silent=True)
|
|
3306
|
+
|
|
3307
|
+
with _Spin(f"renewing MCP profile {pid} ({name}) for {days} days…"):
|
|
3103
3308
|
try:
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3309
|
+
res = httpx.patch(
|
|
3310
|
+
f"{base}/api/agent/app/{cid}",
|
|
3311
|
+
json={"renew": True, "ttl_days": days},
|
|
3312
|
+
timeout=8.0
|
|
3313
|
+
)
|
|
3314
|
+
except Exception as e:
|
|
3315
|
+
_err(f"Network error contacting gateway ({base}): {e}")
|
|
3316
|
+
raise typer.Exit(1)
|
|
3109
3317
|
|
|
3110
3318
|
if res.status_code == 200:
|
|
3111
|
-
|
|
3319
|
+
d = res.json()
|
|
3320
|
+
new_exp = d.get("expires_at", "")
|
|
3321
|
+
if pid in agents:
|
|
3322
|
+
agents[pid]["expires_at"] = new_exp
|
|
3323
|
+
try:
|
|
3324
|
+
agents_file.write_text(json.dumps(agents, indent=2))
|
|
3325
|
+
except Exception:
|
|
3326
|
+
pass
|
|
3327
|
+
_p()
|
|
3328
|
+
_ok(f"Successfully renewed profile {_ye(name)} ({_C}{pid}{_RS} · {_dim('...' + cid[-4:])}) for {_G}{days} days{_RS}!")
|
|
3329
|
+
if new_exp:
|
|
3330
|
+
exp_clean = new_exp[:19].replace("T", " ")
|
|
3331
|
+
print(f" {_DM}New Expiration : {_RS}{_cy(exp_clean)} UTC")
|
|
3332
|
+
_p()
|
|
3112
3333
|
else:
|
|
3113
|
-
|
|
3334
|
+
_err(f"Failed to renew profile on gateway: HTTP {res.status_code} - {res.text}")
|
|
3335
|
+
raise typer.Exit(1)
|
|
3114
3336
|
|
|
3115
3337
|
|
|
3116
3338
|
@agent_app.command("create")
|
|
@@ -3161,6 +3383,7 @@ def agent_create(
|
|
|
3161
3383
|
"client_secret": d["client_secret"],
|
|
3162
3384
|
"name": name,
|
|
3163
3385
|
"scopes": d.get("scopes", effective_scopes),
|
|
3386
|
+
"expires_at": d.get("expires_at"),
|
|
3164
3387
|
"url": base
|
|
3165
3388
|
}
|
|
3166
3389
|
agents_file.write_text(json.dumps(agents, indent=2))
|
|
@@ -20,7 +20,7 @@ class AkashClient:
|
|
|
20
20
|
_patch_dns(_up(self.base_url).hostname or "")
|
|
21
21
|
|
|
22
22
|
if not self.api_key:
|
|
23
|
-
raise AuthenticationError("
|
|
23
|
+
raise AuthenticationError("User is not logged in. An API key is required. Run 'akash login' or set AKASH_API_KEY env var.")
|
|
24
24
|
|
|
25
25
|
def _get_headers(self, custom: dict = None):
|
|
26
26
|
h = {"x-api-key": self.api_key, "Origin": "https://akash.cli"}
|
|
@@ -379,6 +379,10 @@ def upload_large_file(
|
|
|
379
379
|
progress_cb: Optional[Callable[[int, int], None]] = None,
|
|
380
380
|
) -> dict:
|
|
381
381
|
from .uploader import VaultUploader
|
|
382
|
+
from .exceptions import AuthenticationError
|
|
383
|
+
|
|
384
|
+
if not getattr(client, "api_key", None):
|
|
385
|
+
raise AuthenticationError("User is not logged in. An API key is required to upload files. Run 'akash login'.")
|
|
382
386
|
|
|
383
387
|
size = path.stat().st_size
|
|
384
388
|
digest = sha256_file(path)
|
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from typing import Optional, Callable, List
|
|
7
7
|
from concurrent.futures import ThreadPoolExecutor
|
|
8
8
|
from .models import UploadResult
|
|
9
|
-
from .exceptions import UploadFailed, QuotaExceeded
|
|
9
|
+
from .exceptions import UploadFailed, QuotaExceeded, AuthenticationError
|
|
10
10
|
|
|
11
11
|
class VaultUploader:
|
|
12
12
|
def __init__(self, client):
|
|
@@ -26,6 +26,8 @@ class VaultUploader:
|
|
|
26
26
|
Standardizes both File and Text uploads into a single stream.
|
|
27
27
|
progress_callback signature: (bytes_read, total_bytes)
|
|
28
28
|
"""
|
|
29
|
+
if not getattr(self.client, "api_key", None):
|
|
30
|
+
raise AuthenticationError("User is not logged in. An API key is required to upload files. Run 'akash login'.")
|
|
29
31
|
|
|
30
32
|
# 1. Prepare Headers based on V3 Power-Control Specs
|
|
31
33
|
headers = {
|
|
@@ -220,6 +222,8 @@ class VaultUploader:
|
|
|
220
222
|
|
|
221
223
|
def upload_directory(self, folder_path: str, max_workers: int = 3) -> List[UploadResult]:
|
|
222
224
|
"""Recursively uploads all files in a folder using a thread pool."""
|
|
225
|
+
if not getattr(self.client, "api_key", None):
|
|
226
|
+
raise AuthenticationError("User is not logged in. An API key is required to upload files. Run 'akash login'.")
|
|
223
227
|
# Convert string path to Path object
|
|
224
228
|
root = Path(folder_path)
|
|
225
229
|
if not root.is_dir():
|
|
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
|