cartha-cli 1.0.0__py3-none-any.whl

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.
cartha_cli/verifier.py ADDED
@@ -0,0 +1,342 @@
1
+ """HTTP helpers for interacting with the Cartha verifier service."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from typing import Any
7
+
8
+ import requests # type: ignore[import-untyped]
9
+
10
+ from .config import settings
11
+
12
+
13
+ class VerifierError(RuntimeError):
14
+ """Raised when the verifier cannot be reached or returns an error."""
15
+
16
+ def __init__(self, message: str, status_code: int | None = None) -> None:
17
+ super().__init__(message)
18
+ self.status_code = status_code
19
+
20
+
21
+ def _build_url(path: str) -> str:
22
+ base = settings.verifier_url.rstrip("/")
23
+ return f"{base}{path}"
24
+
25
+
26
+ def _request(
27
+ method: str,
28
+ path: str,
29
+ *,
30
+ params: dict[str, Any] | None = None,
31
+ json_data: dict[str, Any] | None = None,
32
+ headers: dict[str, str] | None = None,
33
+ retry: bool = True,
34
+ ) -> dict[str, Any]:
35
+ """Make HTTP request with automatic retry logic.
36
+
37
+ Args:
38
+ method: HTTP method (GET, POST, etc.)
39
+ path: API path
40
+ params: Query parameters
41
+ json_data: JSON body for POST requests
42
+ headers: Additional headers
43
+ retry: Whether to retry on transient failures (default: True)
44
+
45
+ Returns:
46
+ Response JSON data as dict
47
+
48
+ Raises:
49
+ VerifierError: If request fails after retries
50
+ """
51
+ request_headers: dict[str, str] = {"Accept": "application/json"}
52
+ if headers:
53
+ request_headers.update(headers)
54
+ url = _build_url(path)
55
+
56
+ max_attempts = settings.retry_max_attempts if retry else 1
57
+ backoff_factor = settings.retry_backoff_factor
58
+ retry_statuses = settings.retry_on_status
59
+
60
+ last_exception: Exception | None = None
61
+ response: requests.Response | None = None
62
+
63
+ for attempt in range(max_attempts):
64
+ try:
65
+ # Use separate connection and read timeouts
66
+ # Connection timeout: 5s (fast fail if can't connect)
67
+ # Read timeout: 60s (allow time for response to arrive after connection established)
68
+ # This helps when verifier processes quickly but response transmission is slow
69
+ response = requests.request(
70
+ method,
71
+ url,
72
+ params=params,
73
+ json=json_data,
74
+ headers=request_headers,
75
+ timeout=(5, 60), # (connect_timeout, read_timeout) in seconds
76
+ )
77
+
78
+ # Check if we should retry based on status code
79
+ if retry and attempt < max_attempts - 1 and response.status_code in retry_statuses:
80
+ wait_time = backoff_factor ** attempt
81
+ time.sleep(wait_time)
82
+ continue # Retry the request
83
+
84
+ # If we get here, either success or non-retryable error - break and process
85
+ break
86
+
87
+ except requests.Timeout as exc:
88
+ last_exception = exc
89
+ # Retry timeouts if we have attempts left
90
+ if retry and attempt < max_attempts - 1:
91
+ wait_time = backoff_factor ** attempt
92
+ time.sleep(wait_time)
93
+ continue
94
+
95
+ # Explicitly handle timeout - request took longer than allowed
96
+ # This could be connection timeout (5s) or read timeout (60s)
97
+ error_msg = (
98
+ f"Request to verifier timed out after {max_attempts} attempt(s): {url}\n"
99
+ "This is a CLI-side timeout.\n"
100
+ "If verifier logs show request completed, this is likely slow network response transmission.\n"
101
+ "Possible causes: network latency, large response size, or slow connection.\n"
102
+ "Tip: Try again in a moment or check verifier logs to confirm request was processed."
103
+ )
104
+ raise VerifierError(error_msg) from exc
105
+ except requests.RequestException as exc: # pragma: no cover - network failure
106
+ last_exception = exc
107
+ # Retry network errors if we have attempts left
108
+ if retry and attempt < max_attempts - 1:
109
+ wait_time = backoff_factor ** attempt
110
+ time.sleep(wait_time)
111
+ continue
112
+
113
+ # Provide more context about the failed URL
114
+ error_msg = f"Failed to reach verifier at {url} after {max_attempts} attempt(s): {exc}"
115
+ raise VerifierError(error_msg) from exc
116
+
117
+ # If we exhausted retries without getting a response, raise the last exception
118
+ if response is None and last_exception:
119
+ raise VerifierError(f"Request failed after {max_attempts} attempts: {last_exception}") from last_exception
120
+
121
+ # Process the response (response should be set at this point)
122
+ assert response is not None # nosec - response should be set if we got here
123
+
124
+ try:
125
+ data = response.json()
126
+ except ValueError:
127
+ data = None
128
+
129
+ if response.status_code >= 400:
130
+ if isinstance(data, dict):
131
+ detail = data.get("detail") or data.get("error") or response.text
132
+ else:
133
+ detail = response.text or "Unknown verifier error"
134
+
135
+ # Handle FastAPI validation errors which return detail as a list
136
+ if isinstance(detail, list):
137
+ # Format list of validation errors into a readable string
138
+ formatted_errors = []
139
+ for item in detail:
140
+ if isinstance(item, dict):
141
+ # Extract field location and message
142
+ loc = item.get("loc", [])
143
+ msg = item.get("msg", "Validation error")
144
+ field = " -> ".join(str(x) for x in loc) if loc else "unknown"
145
+ formatted_errors.append(f"{field}: {msg}")
146
+ else:
147
+ formatted_errors.append(str(item))
148
+ detail = "; ".join(formatted_errors)
149
+ elif isinstance(detail, str):
150
+ detail = detail.strip()
151
+ else:
152
+ detail = str(detail)
153
+
154
+ # Log error details for debugging (only in debug mode or for 500 errors)
155
+ if response.status_code >= 500:
156
+ import logging
157
+
158
+ logger = logging.getLogger(__name__)
159
+ logger.debug(f"Verifier error - URL: {url}")
160
+ logger.debug(f"Verifier error - Status: {response.status_code}")
161
+ logger.debug(f"Verifier error - Response: {response.text[:500]}")
162
+
163
+ raise VerifierError(detail, status_code=response.status_code)
164
+
165
+ if not isinstance(data, dict):
166
+ raise VerifierError("Unexpected verifier response payload.")
167
+ return data
168
+
169
+
170
+ def fetch_miner_status(
171
+ *,
172
+ hotkey: str,
173
+ slot: str,
174
+ ) -> dict[str, Any]:
175
+ """Return miner status without authentication (public endpoint)."""
176
+ return _request(
177
+ "GET",
178
+ "/v1/miner/status",
179
+ params={"hotkey": hotkey, "slot": slot},
180
+ )
181
+
182
+
183
+ def fetch_pair_status(
184
+ *,
185
+ hotkey: str,
186
+ slot: str,
187
+ network: str,
188
+ netuid: int,
189
+ message: str,
190
+ signature: str,
191
+ ) -> dict[str, Any]:
192
+ """Return the status for a (hotkey, slotUID) pair after verifying ownership."""
193
+ payload = {
194
+ "hotkey": hotkey,
195
+ "slot": slot,
196
+ "network": network,
197
+ "netuid": netuid,
198
+ "message": message,
199
+ "signature": signature,
200
+ }
201
+ return _request(
202
+ "POST",
203
+ "/v1/pair/status",
204
+ json_data=payload,
205
+ )
206
+
207
+
208
+ # REMOVED: fetch_pair_password and register_pair_password
209
+ # These functions are no longer needed - the new lock flow uses session tokens instead of passwords.
210
+ # The verifier endpoints /v1/pair/password/* have been removed.
211
+
212
+
213
+ def check_registration(
214
+ *,
215
+ hotkey: str,
216
+ miner_slot: str | None = None,
217
+ uid: str | None = None,
218
+ ) -> dict[str, Any]:
219
+ """Check if a hotkey is registered on subnet 35.
220
+
221
+ Returns: {registered: bool, uid: int | None}
222
+ """
223
+ params: dict[str, Any] = {"hotkey": hotkey}
224
+ if miner_slot is not None:
225
+ params["minerSlot"] = miner_slot
226
+ if uid is not None:
227
+ params["uid"] = uid
228
+ return _request(
229
+ "GET",
230
+ "/subnet/check-registration",
231
+ params=params,
232
+ )
233
+
234
+
235
+ def verify_hotkey(
236
+ *,
237
+ hotkey: str,
238
+ signature: str,
239
+ message: str,
240
+ ) -> dict[str, Any]:
241
+ """Verify Bittensor hotkey signature and get session token.
242
+
243
+ Returns: {verified: bool, session_token: str, expires_at: int}
244
+ """
245
+ payload = {
246
+ "hotkey": hotkey,
247
+ "signature": signature,
248
+ "message": message,
249
+ }
250
+ return _request(
251
+ "POST",
252
+ "/auth/verify-hotkey",
253
+ json_data=payload,
254
+ )
255
+
256
+
257
+ def request_lock_signature(
258
+ *,
259
+ session_token: str,
260
+ pool_id: str,
261
+ amount: int,
262
+ lock_days: int,
263
+ hotkey: str,
264
+ miner_slot: str | None,
265
+ uid: str | None,
266
+ owner: str,
267
+ chain_id: int,
268
+ vault_address: str,
269
+ ) -> dict[str, Any]:
270
+ """Request EIP-712 LockRequest signature from verifier.
271
+
272
+ Returns: {signature, timestamp, nonce, expiresAt, approveTx, lockTx}
273
+ """
274
+ payload = {
275
+ "poolId": pool_id,
276
+ "amount": amount,
277
+ "lockDays": lock_days,
278
+ "hotkey": hotkey,
279
+ "owner": owner,
280
+ "chainId": chain_id,
281
+ "vaultAddress": vault_address,
282
+ }
283
+ if miner_slot is not None:
284
+ payload["minerSlot"] = miner_slot
285
+ if uid is not None:
286
+ payload["uid"] = uid
287
+
288
+ headers = {"Authorization": f"Bearer {session_token}"}
289
+ return _request(
290
+ "POST",
291
+ "/lock/request-signature",
292
+ json_data=payload,
293
+ headers=headers,
294
+ )
295
+
296
+
297
+ def get_lock_status(
298
+ *,
299
+ tx_hash: str,
300
+ ) -> dict[str, Any]:
301
+ """Check status of a lock transaction.
302
+
303
+ Returns: {verified: bool, lockId: str | None, addedToEpoch: str | None, message: str | None}
304
+ """
305
+ return _request(
306
+ "GET",
307
+ "/lock/status",
308
+ params={"tx_hash": tx_hash}, # Match endpoint parameter name (snake_case)
309
+ )
310
+
311
+
312
+ def process_lock_transaction(
313
+ *,
314
+ tx_hash: str,
315
+ ) -> dict[str, Any]:
316
+ """Trigger immediate processing of a lock transaction.
317
+
318
+ Returns: {success: bool, action: str, hotkey: str, slot: str, ...}
319
+ """
320
+ return _request(
321
+ "POST",
322
+ "/lock/process",
323
+ json_data={"tx_hash": tx_hash},
324
+ )
325
+
326
+
327
+ # REMOVED: Old endpoints - replaced by new lock flow
328
+ # fetch_pair_password, register_pair_password, submit_lock_proof removed
329
+
330
+
331
+ __all__ = [
332
+ "VerifierError",
333
+ "_build_url",
334
+ "_request",
335
+ "fetch_pair_status",
336
+ "fetch_miner_status",
337
+ "check_registration",
338
+ "verify_hotkey",
339
+ "request_lock_signature",
340
+ "get_lock_status",
341
+ "process_lock_transaction",
342
+ ]
cartha_cli/wallet.py ADDED
@@ -0,0 +1,59 @@
1
+ """Wallet handling utilities for the Cartha CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import bittensor as bt
6
+ import typer
7
+ from rich.console import Console
8
+
9
+ from .bt import get_wallet
10
+ from .utils import format_timestamp
11
+
12
+ console = Console()
13
+
14
+ CHALLENGE_PREFIX = "cartha-pair-auth"
15
+ CHALLENGE_TTL_SECONDS = 120
16
+
17
+
18
+ def load_wallet(
19
+ wallet_name: str, wallet_hotkey: str, expected_hotkey: str | None = None
20
+ ) -> bt.wallet:
21
+ """Load a Bittensor wallet.
22
+
23
+ Args:
24
+ wallet_name: Coldkey wallet name
25
+ wallet_hotkey: Hotkey name
26
+ expected_hotkey: Optional expected hotkey SS58 address to validate
27
+
28
+ Returns:
29
+ Loaded wallet object
30
+
31
+ Raises:
32
+ typer.Exit: If wallet cannot be loaded or hotkey mismatch
33
+ """
34
+ try:
35
+ wallet = get_wallet(wallet_name, wallet_hotkey)
36
+ except bt.KeyFileError as exc:
37
+ detail = str(exc).strip()
38
+ name = wallet_name or "<unknown>"
39
+ hotkey = wallet_hotkey or "<unknown>"
40
+ message = (
41
+ f"Unable to open coldkey '{name}' hotkey '{hotkey}'. "
42
+ "Ensure the wallet exists, hotkey files are present, and the key is unlocked."
43
+ )
44
+ if detail:
45
+ message += f" ({detail})"
46
+ console.print(f"[bold red]{message}[/]")
47
+ raise typer.Exit(code=1) from exc
48
+ except Exception as exc: # pragma: no cover - defensive
49
+ console.print(f"[bold red]Failed to load wallet '{wallet_name}/{wallet_hotkey}': {exc}[/]")
50
+ raise typer.Exit(code=1) from exc
51
+
52
+ if expected_hotkey and wallet.hotkey.ss58_address != expected_hotkey:
53
+ console.print(
54
+ "[bold red]Hotkey mismatch: loaded wallet hotkey does not match the supplied address.[/]"
55
+ )
56
+ raise typer.Exit(code=1)
57
+
58
+ return wallet
59
+
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: cartha-cli
3
+ Version: 1.0.0
4
+ Summary: CLI utilities for Cartha subnet miners.
5
+ Project-URL: Homepage, https://cartha.finance
6
+ Project-URL: Repository, https://github.com/General-Tao-Ventures/cartha-cli
7
+ Project-URL: Documentation, https://github.com/General-Tao-Ventures/cartha-cli#readme
8
+ Author: Cartha Contributors
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: 0xmarkets,bittensor,cartha,cli,mining,subnet
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: <3.12,>=3.11
21
+ Requires-Dist: bittensor<10,>=9.12.2
22
+ Requires-Dist: eth-account<0.11,>=0.10
23
+ Requires-Dist: pydantic-settings<3,>=2.6
24
+ Requires-Dist: pydantic<3,>=2.6
25
+ Requires-Dist: python-dotenv>=1.0
26
+ Requires-Dist: requests>=2.32
27
+ Requires-Dist: torch<3,>=2.2
28
+ Requires-Dist: typer[all]>=0.12
29
+ Requires-Dist: web3<7,>=6.11
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=8.2; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Cartha CLI
35
+
36
+ **The official command-line tool for Cartha subnet miners.** Cartha is the Liquidity Provider for 0xMarkets DEX. A simple, powerful way to manage your mining operations—from registration to tracking your locked funds.
37
+
38
+ ## Why Cartha CLI?
39
+
40
+ Cartha CLI makes mining on the Cartha subnet effortless. As the Liquidity Provider for 0xMarkets DEX, Cartha enables miners to provide liquidity and earn rewards:
41
+
42
+ - **🔐 One-Click Registration** - Get started mining in minutes
43
+ - **📊 Instant Status Updates** - See all your pools, balances, and expiration dates at a glance
44
+ - **⏰ Smart Expiration Warnings** - Never miss a renewal with color-coded countdowns
45
+ - **💼 Multi-Pool Management** - Track multiple trading pairs in one place
46
+ - **🔑 Secure by Default** - Your password stays hidden until you actually need it
47
+
48
+ ## Quick Start
49
+
50
+ ```bash
51
+ # Install dependencies
52
+ uv sync
53
+
54
+ # Show available commands
55
+ uv run cartha
56
+
57
+ # Get started with registration
58
+ uv run cartha miner register --help
59
+
60
+ # Check your miner status (no authentication needed)
61
+ uv run cartha miner status --help
62
+
63
+ # Check CLI health and connectivity
64
+ uv run cartha health
65
+
66
+ # Or use short aliases
67
+ uv run cartha m status
68
+ uv run cartha v lock
69
+ ```
70
+
71
+ ## Requirements
72
+
73
+ - Python 3.11
74
+ - Bittensor wallet
75
+ - [`uv`](https://github.com/astral-sh/uv) package manager (or pip)
76
+
77
+ ## What You Can Do
78
+
79
+ ### Get Started
80
+
81
+ **Register your miner:**
82
+ ```bash
83
+ cartha miner register --wallet-name your-wallet --wallet-hotkey your-hotkey
84
+ ```
85
+
86
+ **Check your status anytime:**
87
+ ```bash
88
+ cartha miner status --wallet-name your-wallet --wallet-hotkey your-hotkey
89
+ # Or use the short alias: cartha m status
90
+ ```
91
+
92
+ ### Track Your Pools
93
+
94
+ See all your active trading pairs, balances, and when they expire—all in one command. The CLI shows you:
95
+ - Which pools are active and earning rewards
96
+ - How much you have locked in each pool
97
+ - Days remaining before expiration (with helpful warnings)
98
+ - Which pools are included in the next reward epoch
99
+
100
+ ### View Available Pools
101
+
102
+ See all available pools with their pool IDs and vault addresses:
103
+
104
+ ```bash
105
+ cartha vault pools
106
+ # Or use: cartha v pools
107
+ ```
108
+
109
+ This shows you which pools are available, their full pool IDs, vault contract addresses, and chain IDs.
110
+
111
+ ### Lock Your Funds
112
+
113
+ Create a new lock position with the streamlined lock flow:
114
+ ```bash
115
+ cartha vault lock \
116
+ --coldkey your-wallet \
117
+ --hotkey your-hotkey \
118
+ --pool-id "BTCUSD" \
119
+ --amount 1000.0 \
120
+ --lock-days 30 \
121
+ --owner-evm 0xYourEVMAddress \
122
+ --chain 8453 \
123
+ --vault-address 0xVaultAddress
124
+ # Or use: cartha v lock
125
+ ```
126
+
127
+ **Parameter Notes:**
128
+ - `--owner` and `--owner-evm` are interchangeable (EVM address that will own the lock)
129
+ - `--vault` and `--vault-address` are interchangeable (vault contract address)
130
+ - `--network` accepts `test` (netuid 78) or `finney` (netuid 35, default)
131
+ - `--chain` or `--chain-id` are interchangeable (EVM chain ID: 84532 for Base Sepolia testnet)
132
+
133
+ The CLI will:
134
+ 1. Check your registration on the specified network (subnet 35 for finney, subnet 78 for test)
135
+ 2. Authenticate with your Bittensor hotkey
136
+ 3. Request a signed LockRequest from the verifier
137
+ 4. Automatically open the Cartha Lock UI in your browser with all parameters pre-filled
138
+ 5. Guide you through Phase 1 (Approve USDC) and Phase 2 (Lock Position) via the web interface
139
+ 6. Automatically detect when approval completes and proceed to Phase 2
140
+ 7. The verifier automatically detects your lock and adds you to the upcoming epoch
141
+
142
+ **Managing Positions**: Visit https://cartha.finance/manage to view all your positions, extend locks, or top up existing positions.
143
+
144
+ ### View Your Password
145
+
146
+ When you need your password (like for signing transactions):
147
+ ```bash
148
+ cartha miner password --wallet-name your-wallet --wallet-hotkey your-hotkey
149
+ ```
150
+
151
+ **Tip:** Use `miner status` for daily checks—it's faster and doesn't require signing. Only use `miner password` when you actually need it.
152
+
153
+ ### Check Your Setup
154
+
155
+ Verify your CLI is configured correctly and can reach all services:
156
+
157
+ ```bash
158
+ cartha health
159
+ ```
160
+
161
+ This checks:
162
+ - Verifier connectivity and latency
163
+ - Bittensor network connectivity
164
+ - Configuration validation
165
+
166
+ Use `cartha health --verbose` for detailed troubleshooting information.
167
+
168
+ ## Need Help?
169
+
170
+ - **[Full Command Reference](docs/COMMANDS.md)** - Complete guide to all commands
171
+ - **[Testnet Guide](testnet/README.md)** - Getting started on testnet
172
+ - **[Feedback & Support](docs/FEEDBACK.md)** - Questions or suggestions?
173
+
174
+ ## Contributing
175
+
176
+ We welcome contributions! Please see our [Feedback & Support](docs/FEEDBACK.md) page for ways to get involved.
177
+
178
+ ---
179
+
180
+ **Made with ❤ by GTV**
@@ -0,0 +1,28 @@
1
+ cartha_cli/__init__.py,sha256=cSKsPAfHW8_hqJkGMXUh4mFNz9HXsq-UcgRl8G_2KhM,720
2
+ cartha_cli/bt.py,sha256=GGW8QvOgFPFNrIghu7JNygRmTgBeMt2vhEh26ZiPe4Q,6933
3
+ cartha_cli/config.py,sha256=MSHNiRow1tzHUJeMOvQRQsTslntV6DecLTQ-Y_CmB4A,2294
4
+ cartha_cli/display.py,sha256=Krim69DLgiCHSYDuWNjnOep25IAHUebRAceePHfsiRA,2040
5
+ cartha_cli/eth712.py,sha256=5NU0MnvOk89mxWnkDHzoOaSHN8TJGRAHVLGXmCq8jhM,241
6
+ cartha_cli/main.py,sha256=0-G2syhsj2okLblX5WYb5NGqWjOwF5eW2tDn9AF5vaw,6756
7
+ cartha_cli/pair.py,sha256=Y-TAjAK5FeWqKlUK52dHZLdBhDxx1CJbea8mlsQXkqc,6245
8
+ cartha_cli/utils.py,sha256=LWlJXzWNkIpInWclCJ2PObkG--JEN1mIcvVXsHmFllE,8429
9
+ cartha_cli/verifier.py,sha256=JFtDA9bhecsAUCQEp0SBrZ1Z-48EkDT1Q6y7X4eTK60,10548
10
+ cartha_cli/wallet.py,sha256=Jha1pONa4Hy2XsHMTrk60eBqdkwJyzsQYgPuI-cxxFo,1770
11
+ cartha_cli/commands/__init__.py,sha256=8CYMVEWJmg2qjLyE3ZeheQtS-E9doltDSfyyumOsET4,345
12
+ cartha_cli/commands/common.py,sha256=tpxKsdzjFtcb0ae7J-J-zxnh32qVXZd8jwjP-frFoio,2099
13
+ cartha_cli/commands/config.py,sha256=XT1LQo7b9vVm_mgwUTJRLCBCHJhtGclBzh_CTBAd280,10539
14
+ cartha_cli/commands/health.py,sha256=NRwmIultxUzAe7udOOBIdkcdGG5KsE_kuCs43LZObGk,20717
15
+ cartha_cli/commands/help.py,sha256=6ubfWtmjXfCtp6L_PYvn7rR7m5C_pp-iEjtRc6BS0GA,1721
16
+ cartha_cli/commands/miner_password.py,sha256=7cbcyrJ9KzCyJ68_174U_CXjBUt9BaYhgKAycRpv7AE,11078
17
+ cartha_cli/commands/miner_status.py,sha256=tWlCWcuwm9NEd4USuCLp_6qObikX2odc2e7m6Y_vNrU,21873
18
+ cartha_cli/commands/pair_status.py,sha256=Sk6-bIAcgAH3KxGg0Hw3PofLW_4eyonLUmJOkJ8gem0,19821
19
+ cartha_cli/commands/pools.py,sha256=LkFJlur1T5lxV2qz7DeYYj9GbyRiy0wjb1mUNsV1zQg,4104
20
+ cartha_cli/commands/prove_lock.py,sha256=rNxuKupTGyJ-SzKA5Yil6t6fPmLuzbX4jG4D4ZzVo4c,60562
21
+ cartha_cli/commands/register.py,sha256=m7BLTTmiEbeXRrStUSNQbwI79IN4KOBEjU-nN6cSU4k,10109
22
+ cartha_cli/commands/shared_options.py,sha256=itHzJSgxuKQxUVOh1_jVTcMQXjI3PPzexQyhqIbabxc,5874
23
+ cartha_cli/commands/version.py,sha256=u5oeccQzK0LLcCbgZm0U8-Vslk5vB_lVvW3xT5HPeTg,456
24
+ cartha_cli-1.0.0.dist-info/METADATA,sha256=W_n3aBURJG5xcgP-r9HF9grG5stONJLHYRsrUgaOZcg,5875
25
+ cartha_cli-1.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
26
+ cartha_cli-1.0.0.dist-info/entry_points.txt,sha256=sTYVMgb9l0fuJibUtWpGnIoDmgHinne97G4Y_cCwC-U,43
27
+ cartha_cli-1.0.0.dist-info/licenses/LICENSE,sha256=B4UCiDn13m4xYwIl4TMKfbuKw7kh9pg4c81rJecxHSo,1076
28
+ cartha_cli-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cartha = cartha_cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Cartha Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.