primecli 0.5.6__tar.gz → 0.5.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.
- {primecli-0.5.6 → primecli-0.5.7}/PKG-INFO +1 -1
- {primecli-0.5.6 → primecli-0.5.7}/primecli/degenprime.py +48 -4
- {primecli-0.5.6 → primecli-0.5.7}/primecli/health_monitor.py +124 -64
- {primecli-0.5.6 → primecli-0.5.7}/primecli.egg-info/PKG-INFO +1 -1
- {primecli-0.5.6 → primecli-0.5.7}/pyproject.toml +1 -1
- {primecli-0.5.6 → primecli-0.5.7}/LICENSE +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/README.md +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/primecli/__init__.py +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/primecli/arbprime.py +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/primecli/deltaprime.py +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/primecli.egg-info/SOURCES.txt +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/primecli.egg-info/dependency_links.txt +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/primecli.egg-info/entry_points.txt +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/primecli.egg-info/requires.txt +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/primecli.egg-info/top_level.txt +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/setup.cfg +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/tests/test_cross_file_identity.py +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/tests/test_gas_pricing.py +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/tests/test_health_monitor.py +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/tests/test_paraswap_validator.py +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/tests/test_redstone_encoding.py +0 -0
- {primecli-0.5.6 → primecli-0.5.7}/tests/test_to_wei_units.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: primecli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.7
|
|
4
4
|
Summary: Agent-friendly CLI tools for the DeltaPrime (Avalanche + Arbitrum) and DegenPrime (Base) lending and leverage protocols. Preview-by-default; no Etherscan key required.
|
|
5
5
|
Author: Mnemosyne-quest contributors
|
|
6
6
|
License: MIT
|
|
@@ -137,6 +137,42 @@ ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
|
|
137
137
|
# read lazily so read-only commands that don't sign never need a key at all.
|
|
138
138
|
_CLI_KEY = None # set by the --key CLI flag in main()
|
|
139
139
|
|
|
140
|
+
# Named-wallet table shared with deltaprime/arbprime. Allows running via
|
|
141
|
+
# DEGENPRIME_AGENT=parakletos (or the fallback DELTAPRIME_AGENT) which is
|
|
142
|
+
# cleaner than passing raw keys through environment variables.
|
|
143
|
+
# Agent resolution also supports --as <agent> CLI flag.
|
|
144
|
+
AGENTS = {
|
|
145
|
+
"parakletos": ("/root/.openclaw/.env", "PARAKLETOS_EVM_PRIVATE_KEY"),
|
|
146
|
+
"paraklaudios": ("/root/paraklaudios/.credentials.env", "PARAKLAUDIOS_EVM_PRIVATE_KEY"),
|
|
147
|
+
}
|
|
148
|
+
_SELECTED_AGENT = None # set by the --as CLI flag in main()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _read_env_var(path, var):
|
|
152
|
+
"""Return the value of `var` from a KEY=VALUE env file, or None if absent."""
|
|
153
|
+
try:
|
|
154
|
+
for line in Path(path).read_text().splitlines():
|
|
155
|
+
s = line.strip()
|
|
156
|
+
if s.startswith(var + "="):
|
|
157
|
+
return s.split("=", 1)[1].strip().strip('"').strip("'")
|
|
158
|
+
except FileNotFoundError:
|
|
159
|
+
return None
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _agent_key(agent):
|
|
164
|
+
if agent not in AGENTS:
|
|
165
|
+
raise RuntimeError(
|
|
166
|
+
f"Unknown agent '{agent}'. Known agents: {', '.join(AGENTS)}. "
|
|
167
|
+
f"Or set DEGENPRIME_PRIVATE_KEY, or DEGENPRIME_KEY_FILE."
|
|
168
|
+
)
|
|
169
|
+
path, var = AGENTS[agent]
|
|
170
|
+
key = _read_env_var(path, var)
|
|
171
|
+
if not key:
|
|
172
|
+
raise RuntimeError(f"{var} not found in {path} (agent '{agent}').")
|
|
173
|
+
return key
|
|
174
|
+
|
|
175
|
+
|
|
140
176
|
# Core protocol addresses (verified on Base 2026-05-29).
|
|
141
177
|
FACTORY_PROXY = "0x5A6a0e2702cF4603a098C3Df01f3F0DF56115456" # SmartLoansFactory TUP
|
|
142
178
|
# Diamond beacon. Every Degen Account is a per-user proxy that delegates here, so the
|
|
@@ -265,10 +301,13 @@ def _set_gas_price(w3, tx_dict):
|
|
|
265
301
|
def resolve_private_key():
|
|
266
302
|
"""Resolve the signing key per the documented precedence:
|
|
267
303
|
1. --key <0xhex> CLI flag
|
|
268
|
-
2.
|
|
269
|
-
3.
|
|
270
|
-
4.
|
|
271
|
-
|
|
304
|
+
2. --as <agent> CLI flag
|
|
305
|
+
3. DEGENPRIME_PRIVATE_KEY env var
|
|
306
|
+
4. DEGENPRIME_KEY_FILE env var (path to a file containing the 0x key)
|
|
307
|
+
5. DELTAPRIME_PRIVATE_KEY / DELTAPRIME_KEY_FILE (same key, both chains)
|
|
308
|
+
6. DEGENPRIME_AGENT env var
|
|
309
|
+
7. DELTAPRIME_AGENT env var (fallback)
|
|
310
|
+
Raises with a clear message if none resolve."""
|
|
272
311
|
if _CLI_KEY:
|
|
273
312
|
return _CLI_KEY.strip()
|
|
274
313
|
for env_var in ("DEGENPRIME_PRIVATE_KEY", "DELTAPRIME_PRIVATE_KEY"):
|
|
@@ -282,6 +321,11 @@ def resolve_private_key():
|
|
|
282
321
|
return Path(key_file).read_text().strip()
|
|
283
322
|
except FileNotFoundError:
|
|
284
323
|
raise RuntimeError(f"{path_var} points at {key_file} but the file does not exist.")
|
|
324
|
+
# Named agent via env var
|
|
325
|
+
for ag in ("DEGENPRIME_AGENT", "DELTAPRIME_AGENT"):
|
|
326
|
+
agent = os.environ.get(ag)
|
|
327
|
+
if agent:
|
|
328
|
+
return _agent_key(agent)
|
|
285
329
|
raise RuntimeError(
|
|
286
330
|
"No signing key found. Set DEGENPRIME_PRIVATE_KEY (raw 0x... key) or "
|
|
287
331
|
"DEGENPRIME_KEY_FILE (path to a file with the key), or pass --key <0xhex>. "
|
|
@@ -37,22 +37,21 @@ TIER_MAX = {"basic": 5, "premium": 10}
|
|
|
37
37
|
# ════════════════════════════════════════════════════════════════════
|
|
38
38
|
|
|
39
39
|
def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
|
|
40
|
-
"""Compute
|
|
40
|
+
"""Compute health (0-100%) using the frontend formula from DeltaPrime docs.
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
Uses the cross-margin health formula (all assets assumed same borrowing power):
|
|
43
|
+
equity = total_supplied_usd - total_debt_usd
|
|
44
|
+
health_pct = 100 * (1 - debt / (max_mult * equity))
|
|
45
|
+
(0% = liquidation, 100% = no debt)
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
Background:
|
|
48
|
+
The frontend uses Pr = tier / (tier + 1) and computes:
|
|
49
|
+
health_pct = (Pr * supplied - debt) / (Pr * equity) * 100
|
|
50
|
+
which simplifies to: 100 * (1 - debt / (max_mult * equity)).
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
(1.0=liquidation
|
|
53
|
-
measurement (0%=liquidation, 50%=half borrowing power used, 100%=no debt).
|
|
54
|
-
|
|
55
|
-
Returns dict with health metrics or error.
|
|
52
|
+
DIFFERENT from the equity-based "health_pct" in defi --json / prime-summary
|
|
53
|
+
(which uses max_debt = equity * (tier - 1)).
|
|
54
|
+
The on-chain health_ratio (1.0=liquidation) is NOT used here.
|
|
56
55
|
"""
|
|
57
56
|
# Parse groups (DeltaPrime format) or flat format (DegenPrime)
|
|
58
57
|
groups = defi_data.get("groups", [])
|
|
@@ -64,22 +63,25 @@ def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
|
|
|
64
63
|
# Use precomputed health_pct from defi --json if available (primecli >= 0.5.4)
|
|
65
64
|
precomputed = g.get("health_pct")
|
|
66
65
|
if precomputed is not None:
|
|
67
|
-
#
|
|
66
|
+
# Override health_pct with frontend formula (ignores precomputed value)
|
|
68
67
|
supplied_usd = sum(s.get("usd", 0) or 0 for s in supplied)
|
|
69
68
|
debt_usd = sum(b.get("usd", 0) or 0 for b in borrowed)
|
|
70
|
-
equity = supplied_usd - debt_usd
|
|
69
|
+
equity = max(supplied_usd - debt_usd, 0.01)
|
|
71
70
|
raw_usdc = sum(s.get("usd", 0) for s in supplied if s.get("symbol") == "USDC")
|
|
72
71
|
symbols = [s.get("symbol", "") for s in supplied]
|
|
73
|
-
has_gmx =
|
|
72
|
+
has_gmx = sum(s.get("usd", 0) for s in supplied if "GM_" in s.get("symbol", "")) > 1.0
|
|
74
73
|
has_lb = any(sym in ("LB_AVAX_USDC", "LB_WAVAX_USDC", "JOE") or "TRADERJOE" in sym.upper() for sym in symbols)
|
|
75
74
|
has_aero = any("AERO" in sym.upper() or "CL_POSITION" in sym.upper() for sym in symbols)
|
|
75
|
+
# Frontend formula: health_pct = 100 * (1 - debt / (max_mult * equity))
|
|
76
|
+
fe_health = max(0.0, 100.0 * (1.0 - round(debt_usd, 2) / (max_mult * equity)))
|
|
77
|
+
fe_max_debt = round(max_mult * equity, 2)
|
|
76
78
|
return {
|
|
77
|
-
"health_pct":
|
|
79
|
+
"health_pct": round(fe_health, 1),
|
|
78
80
|
"health_ratio": round(health_ratio, 4),
|
|
79
81
|
"supplied_usd": round(supplied_usd, 2),
|
|
80
82
|
"debt_usd": round(debt_usd, 2),
|
|
81
83
|
"equity": round(equity, 2),
|
|
82
|
-
"max_debt": round(max(0,
|
|
84
|
+
"max_debt": round(max(0, max_mult * equity), 2),
|
|
83
85
|
"raw_usdc": round(raw_usdc, 2),
|
|
84
86
|
"has_gmx": has_gmx,
|
|
85
87
|
"has_lb": has_lb,
|
|
@@ -105,14 +107,14 @@ def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
|
|
|
105
107
|
"error": "equity near zero",
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
max_debt =
|
|
110
|
+
max_debt = round(max_mult * equity, 2) # frontend formula: max debt before liquidation
|
|
109
111
|
|
|
110
112
|
# Raw USDC in account
|
|
111
113
|
raw_usdc = sum(s.get("usd", 0) for s in supplied if s.get("symbol") == "USDC")
|
|
112
114
|
|
|
113
115
|
# Position type detection
|
|
114
116
|
symbols = [s.get("symbol", "") for s in supplied]
|
|
115
|
-
has_gmx =
|
|
117
|
+
has_gmx = sum(s.get("usd", 0) for s in supplied if "GM_" in s.get("symbol", "")) > 1.0
|
|
116
118
|
has_lb = any(
|
|
117
119
|
sym in ("LB_AVAX_USDC", "LB_WAVAX_USDC", "JOE")
|
|
118
120
|
or "TRADERJOE" in sym.upper()
|
|
@@ -120,13 +122,13 @@ def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
|
|
|
120
122
|
)
|
|
121
123
|
has_aero = any("AERO" in sym.upper() or "CL_POSITION" in sym.upper() for sym in symbols)
|
|
122
124
|
|
|
123
|
-
if max_debt > 0 and debt_usd >= 0:
|
|
124
|
-
health_pct = (
|
|
125
|
-
health_pct = max(0.0, min(100.0, health_pct))
|
|
125
|
+
if max_debt > 0.01 and debt_usd >= 0:
|
|
126
|
+
health_pct = max(0.0, 100.0 * (1.0 - round(debt_usd, 2) / max_debt))
|
|
126
127
|
else:
|
|
127
128
|
health_pct = 100.0
|
|
128
129
|
|
|
129
|
-
|
|
130
|
+
# Center target (50% health): target_debt = max_debt * 0.5
|
|
131
|
+
delta_debt = (max_debt * 0.5) - debt_usd
|
|
130
132
|
|
|
131
133
|
return {
|
|
132
134
|
"health_pct": round(health_pct, 1),
|
|
@@ -306,28 +308,32 @@ def run_tick(
|
|
|
306
308
|
max_mult = TIER_MAX.get("basic", 5)
|
|
307
309
|
tier = "basic"
|
|
308
310
|
else:
|
|
309
|
-
max_mult = TIER_MAX.get("
|
|
310
|
-
tier = "
|
|
311
|
+
max_mult = TIER_MAX.get("basic", 5)
|
|
312
|
+
tier = "basic"
|
|
311
313
|
except Exception:
|
|
312
|
-
max_mult =
|
|
313
|
-
tier = "
|
|
314
|
+
max_mult = 5
|
|
315
|
+
tier = "basic"
|
|
314
316
|
|
|
315
317
|
# 3. Compute health
|
|
316
318
|
health = compute_health(defi_data, max_mult)
|
|
317
319
|
health["tier"] = tier
|
|
318
320
|
if health.get("error") == "equity near zero":
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
321
|
+
# Only escalate if there's actual debt — an empty unfunded wallet is not an emergency
|
|
322
|
+
if health.get("debt_usd", 0) and health["debt_usd"] > 0.5:
|
|
323
|
+
write_escalation(state_dir, "equity-near-zero", {
|
|
324
|
+
"reason": "equity_near_zero",
|
|
325
|
+
"equity": health["equity"],
|
|
326
|
+
"debt": health["debt_usd"],
|
|
327
|
+
"health_pct": health["health_pct"],
|
|
328
|
+
"label": label,
|
|
329
|
+
})
|
|
330
|
+
result["mode"] = "escalated"
|
|
331
|
+
else:
|
|
332
|
+
result["action"] = "none (unfunded account)"
|
|
326
333
|
result.update(health)
|
|
327
|
-
result["mode"] = "escalated"
|
|
328
334
|
return result
|
|
329
335
|
|
|
330
|
-
# 4. Load strategy
|
|
336
|
+
# 4. Load strategy (position/market/side are optional hints now — auto-detected from defi_data)
|
|
331
337
|
strategy = load_strategy(strategy_path)
|
|
332
338
|
mode = strategy.get("mode", "observer")
|
|
333
339
|
health["mode"] = mode
|
|
@@ -390,7 +396,7 @@ def run_tick(
|
|
|
390
396
|
pct = health["health_pct"]
|
|
391
397
|
equity = health["equity"]
|
|
392
398
|
debt = health["debt_usd"]
|
|
393
|
-
raw_usdc = health
|
|
399
|
+
raw_usdc = health.get("raw_usdc", 0)
|
|
394
400
|
|
|
395
401
|
# ── Stop-loss: equity drawdown ──────────────────────────────
|
|
396
402
|
if stop_loss_drawdown > 0:
|
|
@@ -513,32 +519,86 @@ def run_tick(
|
|
|
513
519
|
result["error"] = f"borrow error: {e}"
|
|
514
520
|
return result
|
|
515
521
|
|
|
516
|
-
# Deploy into
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
# Partial state: borrowed but deposit failed
|
|
530
|
-
result["warning"] = f"borrow ok but deposit failed: {r.stderr[:200]}"
|
|
531
|
-
result["action"] = "partial (borrowed, deposit failed)"
|
|
532
|
-
except Exception as e:
|
|
533
|
-
result["error"] = f"gmx deposit error: {e}"
|
|
522
|
+
# Deploy into whatever positions are open (detected dynamically from defi_data)
|
|
523
|
+
has_gmx = health.get("has_gmx", False)
|
|
524
|
+
has_lb = health.get("has_lb", False)
|
|
525
|
+
has_aero = health.get("has_aero", False)
|
|
526
|
+
open_positions = []
|
|
527
|
+
if has_gmx: open_positions.append("gmx")
|
|
528
|
+
if has_lb: open_positions.append("lb")
|
|
529
|
+
if has_aero: open_positions.append("aero")
|
|
530
|
+
|
|
531
|
+
if not open_positions:
|
|
532
|
+
# No open positions — just borrow and leave as USDC (or deploy to default)
|
|
533
|
+
result["action"] = f"borrowed ${borrow_amt:.2f} (no positions to deploy into)"
|
|
534
|
+
cooldown_file.write_text(str(int(time.time())))
|
|
534
535
|
else:
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
536
|
+
# Split borrow amount proportionally across open positions
|
|
537
|
+
split_amt = borrow_amt / len(open_positions)
|
|
538
|
+
deployed_ok = 0
|
|
539
|
+
deployed_fail = 0
|
|
540
|
+
|
|
541
|
+
for pos_type in open_positions:
|
|
542
|
+
if pos_type == "gmx":
|
|
543
|
+
# Use market/side from strategy as hint, fall back to sensible defaults
|
|
544
|
+
mkt = strategy.get("market", "avax-usdc") if tool_path else "avax-usdc"
|
|
545
|
+
sd = strategy.get("side", "long") if tool_path else "long"
|
|
546
|
+
try:
|
|
547
|
+
r = subprocess.run(
|
|
548
|
+
[sys.executable, tool_path, "gmx-deposit",
|
|
549
|
+
"--market", mkt, "--amount", f"{split_amt:.2f}",
|
|
550
|
+
"--side", sd, "--fee-buffer", "1.5", "--execute"],
|
|
551
|
+
capture_output=True, text=True, timeout=120,
|
|
552
|
+
)
|
|
553
|
+
if r.returncode == 0:
|
|
554
|
+
deployed_ok += 1
|
|
555
|
+
else:
|
|
556
|
+
result["warning"] = f"gmx deposit failed: {r.stderr[:200]}"
|
|
557
|
+
deployed_fail += 1
|
|
558
|
+
except Exception as e:
|
|
559
|
+
result["error"] = f"gmx deposit error: {e}"
|
|
560
|
+
deployed_fail += 1
|
|
561
|
+
|
|
562
|
+
elif pos_type == "lb":
|
|
563
|
+
# LB deposits: use the pool from strategy hint, default to AVAX/USDC
|
|
564
|
+
lb_pool = strategy.get("lb_pool", "AVAX/USDC")
|
|
565
|
+
try:
|
|
566
|
+
r = subprocess.run(
|
|
567
|
+
[sys.executable, tool_path, "lb-deposit",
|
|
568
|
+
"--pool", lb_pool, "--amount", f"{split_amt:.2f}",
|
|
569
|
+
"--execute"],
|
|
570
|
+
capture_output=True, text=True, timeout=120,
|
|
571
|
+
)
|
|
572
|
+
if r.returncode == 0:
|
|
573
|
+
deployed_ok += 1
|
|
574
|
+
else:
|
|
575
|
+
result["warning"] = f"lb deposit failed: {r.stderr[:200]}"
|
|
576
|
+
deployed_fail += 1
|
|
577
|
+
except Exception as e:
|
|
578
|
+
result["error"] = f"lb deposit error: {e}"
|
|
579
|
+
deployed_fail += 1
|
|
580
|
+
|
|
581
|
+
elif pos_type == "aero":
|
|
582
|
+
try:
|
|
583
|
+
r = subprocess.run(
|
|
584
|
+
[sys.executable, tool_path, "aerodrome-deposit",
|
|
585
|
+
"--amount", f"{split_amt:.2f}", "--execute"],
|
|
586
|
+
capture_output=True, text=True, timeout=120,
|
|
587
|
+
)
|
|
588
|
+
if r.returncode == 0:
|
|
589
|
+
deployed_ok += 1
|
|
590
|
+
else:
|
|
591
|
+
result["warning"] = f"aerodrome deposit failed: {r.stderr[:200]}"
|
|
592
|
+
deployed_fail += 1
|
|
593
|
+
except Exception as e:
|
|
594
|
+
result["error"] = f"aerodrome deposit error: {e}"
|
|
595
|
+
deployed_fail += 1
|
|
596
|
+
|
|
597
|
+
if deployed_ok > 0:
|
|
598
|
+
cooldown_file.write_text(str(int(time.time())))
|
|
599
|
+
result["action"] = f"borrowed ${borrow_amt:.2f}, deployed ${split_amt:.2f} to {deployed_ok} position(s)"
|
|
600
|
+
else:
|
|
601
|
+
result["warning"] = f"borrow ok but all deposits failed"
|
|
542
602
|
|
|
543
603
|
return result
|
|
544
604
|
|
|
@@ -573,7 +633,7 @@ def cli():
|
|
|
573
633
|
[sys.executable, tool_path, "defi", "--json"],
|
|
574
634
|
capture_output=True, text=True, timeout=90,
|
|
575
635
|
)
|
|
576
|
-
tier = "
|
|
636
|
+
tier = "basic" # default
|
|
577
637
|
try:
|
|
578
638
|
t = subprocess.run(
|
|
579
639
|
[sys.executable, tool_path, "prime-tier"],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: primecli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.7
|
|
4
4
|
Summary: Agent-friendly CLI tools for the DeltaPrime (Avalanche + Arbitrum) and DegenPrime (Base) lending and leverage protocols. Preview-by-default; no Etherscan key required.
|
|
5
5
|
Author: Mnemosyne-quest contributors
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "primecli"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.7"
|
|
8
8
|
description = "Agent-friendly CLI tools for the DeltaPrime (Avalanche + Arbitrum) and DegenPrime (Base) lending and leverage protocols. Preview-by-default; no Etherscan key required."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
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
|