hippius 0.1.13__py3-none-any.whl → 0.2.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.
- {hippius-0.1.13.dist-info → hippius-0.2.0.dist-info}/METADATA +2 -2
- hippius-0.2.0.dist-info/RECORD +12 -0
- hippius_sdk/__init__.py +5 -1
- hippius_sdk/cli.py +399 -49
- hippius_sdk/client.py +20 -22
- hippius_sdk/config.py +19 -26
- hippius_sdk/ipfs.py +62 -408
- hippius_sdk/ipfs_core.py +216 -0
- hippius_sdk/substrate.py +235 -60
- hippius_sdk/utils.py +152 -0
- hippius-0.1.13.dist-info/RECORD +0 -10
- {hippius-0.1.13.dist-info → hippius-0.2.0.dist-info}/WHEEL +0 -0
- {hippius-0.1.13.dist-info → hippius-0.2.0.dist-info}/entry_points.txt +0 -0
hippius_sdk/cli.py
CHANGED
@@ -7,9 +7,11 @@ utilities for encryption key generation, file operations, and marketplace intera
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
import argparse
|
10
|
+
import asyncio
|
10
11
|
import base64
|
11
12
|
import concurrent.futures
|
12
13
|
import getpass
|
14
|
+
import inspect
|
13
15
|
import json
|
14
16
|
import os
|
15
17
|
import random
|
@@ -181,12 +183,12 @@ def create_client(args):
|
|
181
183
|
return client
|
182
184
|
|
183
185
|
|
184
|
-
def handle_download(client, cid, output_path, decrypt=None):
|
186
|
+
async def handle_download(client, cid, output_path, decrypt=None):
|
185
187
|
"""Handle the download command"""
|
186
188
|
print(f"Downloading {cid} to {output_path}...")
|
187
189
|
|
188
190
|
# Use the enhanced download method which returns formatted information
|
189
|
-
result = client.download_file(cid, output_path, decrypt=decrypt)
|
191
|
+
result = await client.download_file(cid, output_path, decrypt=decrypt)
|
190
192
|
|
191
193
|
print(f"Download successful in {result['elapsed_seconds']} seconds!")
|
192
194
|
print(f"Saved to: {result['output_path']}")
|
@@ -198,10 +200,10 @@ def handle_download(client, cid, output_path, decrypt=None):
|
|
198
200
|
return 0
|
199
201
|
|
200
202
|
|
201
|
-
def handle_exists(client, cid):
|
203
|
+
async def handle_exists(client, cid):
|
202
204
|
"""Handle the exists command"""
|
203
205
|
print(f"Checking if CID {cid} exists on IPFS...")
|
204
|
-
result = client.exists(cid)
|
206
|
+
result = await client.exists(cid)
|
205
207
|
|
206
208
|
# Use the formatted CID from the result
|
207
209
|
formatted_cid = result["formatted_cid"]
|
@@ -217,12 +219,12 @@ def handle_exists(client, cid):
|
|
217
219
|
return 0
|
218
220
|
|
219
221
|
|
220
|
-
def handle_cat(client, cid, max_size, decrypt=None):
|
222
|
+
async def handle_cat(client, cid, max_size, decrypt=None):
|
221
223
|
"""Handle the cat command"""
|
222
224
|
print(f"Retrieving content of CID {cid}...")
|
223
225
|
try:
|
224
226
|
# Use the enhanced cat method with formatting
|
225
|
-
result = client.cat(cid, max_display_bytes=max_size, decrypt=decrypt)
|
227
|
+
result = await client.cat(cid, max_display_bytes=max_size, decrypt=decrypt)
|
226
228
|
|
227
229
|
# Display file information
|
228
230
|
print(
|
@@ -255,7 +257,7 @@ def handle_cat(client, cid, max_size, decrypt=None):
|
|
255
257
|
return 0
|
256
258
|
|
257
259
|
|
258
|
-
def handle_store(client, file_path, miner_ids, encrypt=None):
|
260
|
+
async def handle_store(client, file_path, miner_ids, encrypt=None):
|
259
261
|
"""Handle the store command"""
|
260
262
|
if not os.path.exists(file_path):
|
261
263
|
print(f"Error: File {file_path} not found")
|
@@ -265,7 +267,7 @@ def handle_store(client, file_path, miner_ids, encrypt=None):
|
|
265
267
|
start_time = time.time()
|
266
268
|
|
267
269
|
# Use the enhanced upload_file method that returns formatted information
|
268
|
-
result = client.upload_file(file_path, encrypt=encrypt)
|
270
|
+
result = await client.upload_file(file_path, encrypt=encrypt)
|
269
271
|
|
270
272
|
ipfs_elapsed_time = time.time() - start_time
|
271
273
|
|
@@ -282,11 +284,23 @@ def handle_store(client, file_path, miner_ids, encrypt=None):
|
|
282
284
|
start_time = time.time()
|
283
285
|
|
284
286
|
try:
|
287
|
+
# Check if we have credits
|
288
|
+
try:
|
289
|
+
if hasattr(client.substrate_client, "get_free_credits"):
|
290
|
+
credits = client.substrate_client.get_free_credits()
|
291
|
+
print(f"Account credits: {credits}")
|
292
|
+
if credits <= 0:
|
293
|
+
print(
|
294
|
+
f"Warning: Account has no free credits (current: {credits}). Transaction may fail."
|
295
|
+
)
|
296
|
+
except Exception as e:
|
297
|
+
print(f"Warning: Could not check free credits: {e}")
|
298
|
+
|
285
299
|
# Create a file input object for the marketplace
|
286
300
|
file_input = {"fileHash": result["cid"], "fileName": result["filename"]}
|
287
301
|
|
288
|
-
# Store on Substrate
|
289
|
-
client.substrate_client.storage_request([file_input], miner_ids)
|
302
|
+
# Store on Substrate - now it's an async call
|
303
|
+
tx_hash = await client.substrate_client.storage_request([file_input], miner_ids)
|
290
304
|
|
291
305
|
substrate_elapsed_time = time.time() - start_time
|
292
306
|
print(
|
@@ -307,7 +321,7 @@ def handle_store(client, file_path, miner_ids, encrypt=None):
|
|
307
321
|
return 0
|
308
322
|
|
309
323
|
|
310
|
-
def handle_store_dir(client, dir_path, miner_ids, encrypt=None):
|
324
|
+
async def handle_store_dir(client, dir_path, miner_ids, encrypt=None):
|
311
325
|
"""Handle the store-dir command"""
|
312
326
|
if not os.path.isdir(dir_path):
|
313
327
|
print(f"Error: Directory {dir_path} not found")
|
@@ -331,7 +345,7 @@ def handle_store_dir(client, dir_path, miner_ids, encrypt=None):
|
|
331
345
|
for file_path, rel_path in all_files:
|
332
346
|
try:
|
333
347
|
print(f" Uploading: {rel_path}")
|
334
|
-
file_result = client.upload_file(file_path, encrypt=encrypt)
|
348
|
+
file_result = await client.upload_file(file_path, encrypt=encrypt)
|
335
349
|
individual_cids.append(
|
336
350
|
{
|
337
351
|
"path": rel_path,
|
@@ -351,7 +365,7 @@ def handle_store_dir(client, dir_path, miner_ids, encrypt=None):
|
|
351
365
|
print(f" Error uploading {rel_path}: {e}")
|
352
366
|
|
353
367
|
# Now upload the entire directory
|
354
|
-
result = client.upload_directory(dir_path, encrypt=encrypt)
|
368
|
+
result = await client.upload_directory(dir_path, encrypt=encrypt)
|
355
369
|
|
356
370
|
ipfs_elapsed_time = time.time() - start_time
|
357
371
|
|
@@ -384,7 +398,7 @@ def handle_store_dir(client, dir_path, miner_ids, encrypt=None):
|
|
384
398
|
file_inputs.append({"fileHash": item["cid"], "fileName": item["filename"]})
|
385
399
|
|
386
400
|
# Store all files in a single batch request
|
387
|
-
client.substrate_client.storage_request(file_inputs, miner_ids)
|
401
|
+
tx_hash = await client.substrate_client.storage_request(file_inputs, miner_ids)
|
388
402
|
|
389
403
|
substrate_elapsed_time = time.time() - start_time
|
390
404
|
print(
|
@@ -450,7 +464,7 @@ def handle_credits(client, account_address):
|
|
450
464
|
return 0
|
451
465
|
|
452
466
|
|
453
|
-
def handle_files(client, account_address, show_all_miners=False):
|
467
|
+
async def handle_files(client, account_address, show_all_miners=False):
|
454
468
|
"""
|
455
469
|
Display files stored by a user in a nice format.
|
456
470
|
|
@@ -490,7 +504,9 @@ def handle_files(client, account_address, show_all_miners=False):
|
|
490
504
|
|
491
505
|
# Get files for the account using the new profile-based method
|
492
506
|
print(f"Retrieving files for account: {account_address}")
|
493
|
-
files = client.substrate_client.get_user_files_from_profile(
|
507
|
+
files = await client.substrate_client.get_user_files_from_profile(
|
508
|
+
account_address
|
509
|
+
)
|
494
510
|
|
495
511
|
# Check if any files were found
|
496
512
|
if not files:
|
@@ -522,10 +538,10 @@ def handle_files(client, account_address, show_all_miners=False):
|
|
522
538
|
if "size_formatted" in file and file["size_formatted"] is not None:
|
523
539
|
size_formatted = file["size_formatted"]
|
524
540
|
file_size = file.get("file_size", 0)
|
525
|
-
if file_size is not None:
|
541
|
+
if file_size is not None and file_size > 0:
|
526
542
|
print(f" File size: {file_size:,} bytes ({size_formatted})")
|
527
543
|
else:
|
528
|
-
print(f" File size:
|
544
|
+
print(f" File size: {size_formatted}")
|
529
545
|
else:
|
530
546
|
print(f" File size: Unknown")
|
531
547
|
|
@@ -568,7 +584,9 @@ def handle_files(client, account_address, show_all_miners=False):
|
|
568
584
|
return 0
|
569
585
|
|
570
586
|
|
571
|
-
def handle_ec_files(
|
587
|
+
async def handle_ec_files(
|
588
|
+
client, account_address, show_all_miners=False, show_chunks=False
|
589
|
+
):
|
572
590
|
"""Handle the ec-files command to show only erasure-coded files"""
|
573
591
|
print("Looking for erasure-coded files...")
|
574
592
|
try:
|
@@ -604,7 +622,9 @@ def handle_ec_files(client, account_address, show_all_miners=False, show_chunks=
|
|
604
622
|
return 1
|
605
623
|
|
606
624
|
# First, get all user files using the profile method
|
607
|
-
files = client.substrate_client.get_user_files_from_profile(
|
625
|
+
files = await client.substrate_client.get_user_files_from_profile(
|
626
|
+
account_address
|
627
|
+
)
|
608
628
|
|
609
629
|
# Filter for metadata files (ending with .ec_metadata)
|
610
630
|
ec_metadata_files = []
|
@@ -637,7 +657,7 @@ def handle_ec_files(client, account_address, show_all_miners=False, show_chunks=
|
|
637
657
|
# Fetch and parse the metadata to get original file info
|
638
658
|
try:
|
639
659
|
# Use the formatted CID, not the raw hex-encoded version
|
640
|
-
metadata = client.ipfs_client.cat(formatted_cid)
|
660
|
+
metadata = await client.ipfs_client.cat(formatted_cid)
|
641
661
|
|
642
662
|
# Check if we have text content
|
643
663
|
if metadata.get("is_text", False):
|
@@ -772,7 +792,7 @@ def handle_ec_files(client, account_address, show_all_miners=False, show_chunks=
|
|
772
792
|
return 0
|
773
793
|
|
774
794
|
|
775
|
-
def handle_erasure_code(
|
795
|
+
async def handle_erasure_code(
|
776
796
|
client, file_path, k, m, chunk_size, miner_ids, encrypt=None, verbose=True
|
777
797
|
):
|
778
798
|
"""Handle the erasure-code command"""
|
@@ -814,7 +834,7 @@ def handle_erasure_code(
|
|
814
834
|
choice = input("> ").strip().lower()
|
815
835
|
|
816
836
|
if choice in ("y", "yes"):
|
817
|
-
return handle_erasure_code_directory(
|
837
|
+
return await handle_erasure_code_directory(
|
818
838
|
client, file_path, k, m, chunk_size, miner_ids, encrypt, verbose
|
819
839
|
)
|
820
840
|
else:
|
@@ -879,7 +899,7 @@ def handle_erasure_code(
|
|
879
899
|
|
880
900
|
try:
|
881
901
|
# Use the store_erasure_coded_file method directly from HippiusClient
|
882
|
-
result = client.store_erasure_coded_file(
|
902
|
+
result = await client.store_erasure_coded_file(
|
883
903
|
file_path=file_path,
|
884
904
|
k=k,
|
885
905
|
m=m,
|
@@ -945,7 +965,7 @@ def handle_erasure_code(
|
|
945
965
|
return 1
|
946
966
|
|
947
967
|
|
948
|
-
def handle_erasure_code_directory(
|
968
|
+
async def handle_erasure_code_directory(
|
949
969
|
client, dir_path, k, m, chunk_size, miner_ids, encrypt=None, verbose=True
|
950
970
|
):
|
951
971
|
"""Apply erasure coding to each file in a directory individually"""
|
@@ -1025,7 +1045,7 @@ def handle_erasure_code_directory(
|
|
1025
1045
|
|
1026
1046
|
try:
|
1027
1047
|
# Use the store_erasure_coded_file method directly from HippiusClient
|
1028
|
-
result = client.store_erasure_coded_file(
|
1048
|
+
result = await client.store_erasure_coded_file(
|
1029
1049
|
file_path=file_path,
|
1030
1050
|
k=k,
|
1031
1051
|
m=m,
|
@@ -1093,7 +1113,7 @@ def handle_erasure_code_directory(
|
|
1093
1113
|
return 0 if failed == 0 else 1
|
1094
1114
|
|
1095
1115
|
|
1096
|
-
def handle_reconstruct(client, metadata_cid, output_file, verbose=True):
|
1116
|
+
async def handle_reconstruct(client, metadata_cid, output_file, verbose=True):
|
1097
1117
|
"""Handle the reconstruct command for erasure-coded files"""
|
1098
1118
|
# Check if zfec is installed
|
1099
1119
|
try:
|
@@ -1112,7 +1132,7 @@ def handle_reconstruct(client, metadata_cid, output_file, verbose=True):
|
|
1112
1132
|
|
1113
1133
|
try:
|
1114
1134
|
# Use the reconstruct_from_erasure_code method
|
1115
|
-
result = client.reconstruct_from_erasure_code(
|
1135
|
+
result = await client.reconstruct_from_erasure_code(
|
1116
1136
|
metadata_cid=metadata_cid, output_file=output_file, verbose=verbose
|
1117
1137
|
)
|
1118
1138
|
|
@@ -1500,6 +1520,261 @@ def handle_default_address_clear():
|
|
1500
1520
|
return 0
|
1501
1521
|
|
1502
1522
|
|
1523
|
+
async def handle_pinning_status(
|
1524
|
+
client, account_address, verbose=False, show_contents=True
|
1525
|
+
):
|
1526
|
+
"""Handle the pinning-status command"""
|
1527
|
+
print("Checking file pinning status...")
|
1528
|
+
try:
|
1529
|
+
# Get the account address we're querying
|
1530
|
+
if account_address is None:
|
1531
|
+
# If no address provided, first try to get from keypair (if available)
|
1532
|
+
if (
|
1533
|
+
hasattr(client.substrate_client, "_keypair")
|
1534
|
+
and client.substrate_client._keypair is not None
|
1535
|
+
):
|
1536
|
+
account_address = client.substrate_client._keypair.ss58_address
|
1537
|
+
else:
|
1538
|
+
# Try to get the default address
|
1539
|
+
default_address = get_default_address()
|
1540
|
+
if default_address:
|
1541
|
+
account_address = default_address
|
1542
|
+
else:
|
1543
|
+
has_default = get_default_address() is not None
|
1544
|
+
print(
|
1545
|
+
"Error: No account address provided, and client has no keypair."
|
1546
|
+
)
|
1547
|
+
if has_default:
|
1548
|
+
print(
|
1549
|
+
"Please provide an account address with '--account_address' or the default address may be invalid."
|
1550
|
+
)
|
1551
|
+
else:
|
1552
|
+
print(
|
1553
|
+
"Please provide an account address with '--account_address' or set a default with:"
|
1554
|
+
)
|
1555
|
+
print(" hippius address set-default <your_account_address>")
|
1556
|
+
return 1
|
1557
|
+
|
1558
|
+
storage_requests = client.substrate_client.get_pinning_status(account_address)
|
1559
|
+
|
1560
|
+
# Check if any storage requests were found
|
1561
|
+
if not storage_requests:
|
1562
|
+
print(f"No pinning requests found for account: {account_address}")
|
1563
|
+
return 0
|
1564
|
+
|
1565
|
+
print(
|
1566
|
+
f"\nFound {len(storage_requests)} pinning requests for account: {account_address}"
|
1567
|
+
)
|
1568
|
+
print("-" * 80)
|
1569
|
+
|
1570
|
+
# Format and display each storage request
|
1571
|
+
for i, request in enumerate(storage_requests, 1):
|
1572
|
+
try:
|
1573
|
+
print(f"Request {i}:")
|
1574
|
+
|
1575
|
+
# Display CID if available
|
1576
|
+
cid = None
|
1577
|
+
if "cid" in request:
|
1578
|
+
cid = request.get("cid", "Unknown")
|
1579
|
+
print(f" CID: {cid}")
|
1580
|
+
|
1581
|
+
# Display file name if available
|
1582
|
+
if "file_name" in request:
|
1583
|
+
file_name = request.get("file_name", "Unknown")
|
1584
|
+
print(f" File name: {file_name}")
|
1585
|
+
elif "raw_value" in request and "file_name" in request["raw_value"]:
|
1586
|
+
# Try to extract from raw value if it's available
|
1587
|
+
try:
|
1588
|
+
raw_value = request["raw_value"]
|
1589
|
+
if isinstance(raw_value, str) and "{" in raw_value:
|
1590
|
+
# It's a string representation of a dict, try to extract the file_name
|
1591
|
+
if "'file_name': " in raw_value:
|
1592
|
+
start_idx = raw_value.find("'file_name': '") + len(
|
1593
|
+
"'file_name': '"
|
1594
|
+
)
|
1595
|
+
end_idx = raw_value.find("'", start_idx)
|
1596
|
+
if start_idx > 0 and end_idx > start_idx:
|
1597
|
+
file_name = raw_value[start_idx:end_idx]
|
1598
|
+
print(f" File name: {file_name}")
|
1599
|
+
except Exception:
|
1600
|
+
pass
|
1601
|
+
|
1602
|
+
# Display total replicas if available
|
1603
|
+
if "total_replicas" in request:
|
1604
|
+
total_replicas = request.get("total_replicas", 0)
|
1605
|
+
print(f" Total replicas: {total_replicas}")
|
1606
|
+
|
1607
|
+
# Display owner if available
|
1608
|
+
if "owner" in request:
|
1609
|
+
owner = request.get("owner", "Unknown")
|
1610
|
+
print(f" Owner: {owner}")
|
1611
|
+
|
1612
|
+
# Display timestamps if available
|
1613
|
+
if "created_at" in request:
|
1614
|
+
created_at = request.get("created_at", 0)
|
1615
|
+
if created_at > 0:
|
1616
|
+
print(f" Created at block: {created_at}")
|
1617
|
+
|
1618
|
+
if "last_charged_at" in request:
|
1619
|
+
last_charged_at = request.get("last_charged_at", 0)
|
1620
|
+
if last_charged_at > 0:
|
1621
|
+
print(f" Last charged at block: {last_charged_at}")
|
1622
|
+
|
1623
|
+
# Display assignment status and progress info
|
1624
|
+
status_text = "Awaiting validator"
|
1625
|
+
if "is_assigned" in request:
|
1626
|
+
is_assigned = request.get("is_assigned", False)
|
1627
|
+
if is_assigned:
|
1628
|
+
status_text = "Assigned to miners"
|
1629
|
+
|
1630
|
+
# Enhanced status info
|
1631
|
+
if "miner_ids" in request and "total_replicas" in request:
|
1632
|
+
miner_ids = request.get("miner_ids", [])
|
1633
|
+
total_replicas = request.get("total_replicas", 0)
|
1634
|
+
|
1635
|
+
if len(miner_ids) > 0:
|
1636
|
+
if len(miner_ids) == total_replicas:
|
1637
|
+
status_text = "Fully pinned"
|
1638
|
+
else:
|
1639
|
+
status_text = "Partially pinned"
|
1640
|
+
|
1641
|
+
print(f" Status: {status_text}")
|
1642
|
+
|
1643
|
+
# Display validator if available
|
1644
|
+
if "selected_validator" in request:
|
1645
|
+
validator = request.get("selected_validator", "")
|
1646
|
+
if validator:
|
1647
|
+
print(f" Selected validator: {validator}")
|
1648
|
+
|
1649
|
+
# Display miners if available
|
1650
|
+
if "miner_ids" in request:
|
1651
|
+
miner_ids = request.get("miner_ids", [])
|
1652
|
+
if miner_ids:
|
1653
|
+
print(f" Assigned miners: {len(miner_ids)}")
|
1654
|
+
for miner in miner_ids[:3]: # Show first 3 miners
|
1655
|
+
print(f" - {miner}")
|
1656
|
+
if len(miner_ids) > 3:
|
1657
|
+
print(f" ... and {len(miner_ids) - 3} more")
|
1658
|
+
else:
|
1659
|
+
print(f" Assigned miners: None")
|
1660
|
+
|
1661
|
+
# Calculate pinning percentage if we have total_replicas
|
1662
|
+
if "total_replicas" in request and request["total_replicas"] > 0:
|
1663
|
+
total_replicas = request["total_replicas"]
|
1664
|
+
pinning_pct = (len(miner_ids) / total_replicas) * 100
|
1665
|
+
print(
|
1666
|
+
f" Pinning progress: {pinning_pct:.1f}% ({len(miner_ids)}/{total_replicas} miners)"
|
1667
|
+
)
|
1668
|
+
|
1669
|
+
# Display raw data for debugging
|
1670
|
+
if verbose:
|
1671
|
+
print(" Raw data:")
|
1672
|
+
if "raw_key" in request:
|
1673
|
+
print(f" Key: {request['raw_key']}")
|
1674
|
+
if "raw_value" in request:
|
1675
|
+
print(f" Value: {request['raw_value']}")
|
1676
|
+
|
1677
|
+
# Try to fetch the content and determine if it's a file list by inspecting its contents
|
1678
|
+
if show_contents and cid:
|
1679
|
+
try:
|
1680
|
+
print("\n Fetching contents from IPFS...")
|
1681
|
+
# Fetch the contents from IPFS
|
1682
|
+
file_data = await client.ipfs_client.cat(cid)
|
1683
|
+
|
1684
|
+
if file_data and file_data.get("is_text", False):
|
1685
|
+
try:
|
1686
|
+
# Try to parse as JSON
|
1687
|
+
content_json = json.loads(
|
1688
|
+
file_data.get("content", "{}")
|
1689
|
+
)
|
1690
|
+
|
1691
|
+
# Detect if this is a file list by checking if it's a list of file objects
|
1692
|
+
is_file_list = False
|
1693
|
+
if (
|
1694
|
+
isinstance(content_json, list)
|
1695
|
+
and len(content_json) > 0
|
1696
|
+
):
|
1697
|
+
# Check if it looks like a file list
|
1698
|
+
sample_item = content_json[0]
|
1699
|
+
if isinstance(sample_item, dict) and (
|
1700
|
+
"cid" in sample_item
|
1701
|
+
or "fileHash" in sample_item
|
1702
|
+
or "filename" in sample_item
|
1703
|
+
or "fileName" in sample_item
|
1704
|
+
):
|
1705
|
+
is_file_list = True
|
1706
|
+
|
1707
|
+
if is_file_list:
|
1708
|
+
# It's a file list - display the files
|
1709
|
+
print(
|
1710
|
+
f" Content is a file list with {len(content_json)} files:"
|
1711
|
+
)
|
1712
|
+
print(" " + "-" * 40)
|
1713
|
+
for j, file_info in enumerate(content_json, 1):
|
1714
|
+
filename = file_info.get(
|
1715
|
+
"filename"
|
1716
|
+
) or file_info.get("fileName", "Unknown")
|
1717
|
+
file_cid = file_info.get(
|
1718
|
+
"cid"
|
1719
|
+
) or file_info.get("fileHash", "Unknown")
|
1720
|
+
print(f" File {j}: {filename}")
|
1721
|
+
print(f" CID: {file_cid}")
|
1722
|
+
|
1723
|
+
# Show size if available
|
1724
|
+
if "size" in file_info:
|
1725
|
+
size = file_info["size"]
|
1726
|
+
size_formatted = (
|
1727
|
+
client.format_size(size)
|
1728
|
+
if hasattr(client, "format_size")
|
1729
|
+
else f"{size} bytes"
|
1730
|
+
)
|
1731
|
+
print(f" Size: {size_formatted}")
|
1732
|
+
|
1733
|
+
print(" " + "-" * 40)
|
1734
|
+
else:
|
1735
|
+
# Not a file list, show a compact summary
|
1736
|
+
content_type = type(content_json).__name__
|
1737
|
+
preview = str(content_json)
|
1738
|
+
if len(preview) > 100:
|
1739
|
+
preview = preview[:100] + "..."
|
1740
|
+
print(f" Content type: JSON {content_type}")
|
1741
|
+
print(f" Content preview: {preview}")
|
1742
|
+
except json.JSONDecodeError:
|
1743
|
+
# Not JSON, just show text preview
|
1744
|
+
content = file_data.get("content", "")
|
1745
|
+
preview = (
|
1746
|
+
content[:100] + "..."
|
1747
|
+
if len(content) > 100
|
1748
|
+
else content
|
1749
|
+
)
|
1750
|
+
print(f" Content type: Text")
|
1751
|
+
print(f" Content preview: {preview}")
|
1752
|
+
else:
|
1753
|
+
# Binary data
|
1754
|
+
content_size = len(file_data.get("content", b""))
|
1755
|
+
size_formatted = (
|
1756
|
+
client.format_size(content_size)
|
1757
|
+
if hasattr(client, "format_size")
|
1758
|
+
else f"{content_size} bytes"
|
1759
|
+
)
|
1760
|
+
print(f" Content type: Binary data")
|
1761
|
+
print(f" Content size: {size_formatted}")
|
1762
|
+
except Exception as e:
|
1763
|
+
print(f" Error fetching file list contents: {e}")
|
1764
|
+
|
1765
|
+
print("-" * 80)
|
1766
|
+
except Exception as e:
|
1767
|
+
print(f" Error displaying request {i}: {e}")
|
1768
|
+
print("-" * 80)
|
1769
|
+
continue
|
1770
|
+
|
1771
|
+
except Exception as e:
|
1772
|
+
print(f"Error retrieving pinning status: {e}")
|
1773
|
+
return 1
|
1774
|
+
|
1775
|
+
return 0
|
1776
|
+
|
1777
|
+
|
1503
1778
|
def main():
|
1504
1779
|
"""Main CLI entry point for hippius command."""
|
1505
1780
|
# Set up the argument parser
|
@@ -1532,6 +1807,9 @@ examples:
|
|
1532
1807
|
# View all miners for stored files
|
1533
1808
|
hippius files --all-miners
|
1534
1809
|
|
1810
|
+
# Check file pinning status
|
1811
|
+
hippius pinning-status
|
1812
|
+
|
1535
1813
|
# Erasure code a file (Reed-Solomon)
|
1536
1814
|
hippius erasure-code large_file.mp4 --k 3 --m 5
|
1537
1815
|
|
@@ -1678,6 +1956,32 @@ examples:
|
|
1678
1956
|
)
|
1679
1957
|
)
|
1680
1958
|
|
1959
|
+
# Pinning status command
|
1960
|
+
pinning_status_parser = subparsers.add_parser(
|
1961
|
+
"pinning-status", help="Check the status of file pinning requests"
|
1962
|
+
)
|
1963
|
+
pinning_status_parser.add_argument(
|
1964
|
+
"--account_address",
|
1965
|
+
help="Substrate account to check pinning status for (defaults to your keyfile account)",
|
1966
|
+
)
|
1967
|
+
pinning_status_parser.add_argument(
|
1968
|
+
"--verbose",
|
1969
|
+
"-v",
|
1970
|
+
action="store_true",
|
1971
|
+
help="Show detailed debug information",
|
1972
|
+
)
|
1973
|
+
pinning_status_parser.add_argument(
|
1974
|
+
"--show-contents",
|
1975
|
+
action="store_true",
|
1976
|
+
default=True,
|
1977
|
+
help="Show the contents of file lists (defaults to true)",
|
1978
|
+
)
|
1979
|
+
pinning_status_parser.add_argument(
|
1980
|
+
"--no-contents",
|
1981
|
+
action="store_true",
|
1982
|
+
help="Don't show the contents of file lists",
|
1983
|
+
)
|
1984
|
+
|
1681
1985
|
# Erasure Coded Files command
|
1682
1986
|
ec_files_parser = subparsers.add_parser(
|
1683
1987
|
"ec-files", help="View erasure-coded files stored by you or another account"
|
@@ -1712,6 +2016,9 @@ examples:
|
|
1712
2016
|
keygen_parser.add_argument(
|
1713
2017
|
"--copy", action="store_true", help="Copy the generated key to the clipboard"
|
1714
2018
|
)
|
2019
|
+
keygen_parser.add_argument(
|
2020
|
+
"--save", action="store_true", help="Save the key to the Hippius configuration"
|
2021
|
+
)
|
1715
2022
|
|
1716
2023
|
# Erasure code command
|
1717
2024
|
erasure_code_parser = subparsers.add_parser(
|
@@ -1897,16 +2204,6 @@ examples:
|
|
1897
2204
|
parser.print_help()
|
1898
2205
|
return 1
|
1899
2206
|
|
1900
|
-
# Special case for keygen which doesn't need client initialization
|
1901
|
-
if args.command == "keygen":
|
1902
|
-
# Handle key generation separately
|
1903
|
-
if args.copy:
|
1904
|
-
return key_generation_cli()
|
1905
|
-
else:
|
1906
|
-
# Create a new argparse namespace with just the copy flag for compatibility
|
1907
|
-
keygen_args = argparse.Namespace(copy=False)
|
1908
|
-
return key_generation_cli()
|
1909
|
-
|
1910
2207
|
try:
|
1911
2208
|
# Parse miner IDs if provided
|
1912
2209
|
miner_ids = None
|
@@ -1957,27 +2254,47 @@ examples:
|
|
1957
2254
|
encryption_key=encryption_key,
|
1958
2255
|
)
|
1959
2256
|
|
1960
|
-
# Handle commands
|
2257
|
+
# Handle commands - separate async and sync handlers
|
2258
|
+
# Create a helper function to handle async handlers
|
2259
|
+
def run_async_handler(handler_func, *args, **kwargs):
|
2260
|
+
# Check if the handler is async
|
2261
|
+
if inspect.iscoroutinefunction(handler_func):
|
2262
|
+
# Run the async handler in the event loop
|
2263
|
+
return asyncio.run(handler_func(*args, **kwargs))
|
2264
|
+
else:
|
2265
|
+
# Run the handler directly
|
2266
|
+
return handler_func(*args, **kwargs)
|
2267
|
+
|
2268
|
+
# Handle commands with the helper function
|
1961
2269
|
if args.command == "download":
|
1962
|
-
return
|
2270
|
+
return run_async_handler(
|
2271
|
+
handle_download, client, args.cid, args.output_path, decrypt=decrypt
|
2272
|
+
)
|
1963
2273
|
|
1964
2274
|
elif args.command == "exists":
|
1965
|
-
return handle_exists
|
2275
|
+
return run_async_handler(handle_exists, client, args.cid)
|
1966
2276
|
|
1967
2277
|
elif args.command == "cat":
|
1968
|
-
return
|
2278
|
+
return run_async_handler(
|
2279
|
+
handle_cat, client, args.cid, args.max_size, decrypt=decrypt
|
2280
|
+
)
|
1969
2281
|
|
1970
2282
|
elif args.command == "store":
|
1971
|
-
return
|
2283
|
+
return run_async_handler(
|
2284
|
+
handle_store, client, args.file_path, miner_ids, encrypt=encrypt
|
2285
|
+
)
|
1972
2286
|
|
1973
2287
|
elif args.command == "store-dir":
|
1974
|
-
return
|
2288
|
+
return run_async_handler(
|
2289
|
+
handle_store_dir, client, args.dir_path, miner_ids, encrypt=encrypt
|
2290
|
+
)
|
1975
2291
|
|
1976
2292
|
elif args.command == "credits":
|
1977
2293
|
return handle_credits(client, args.account_address)
|
1978
2294
|
|
1979
2295
|
elif args.command == "files":
|
1980
|
-
return
|
2296
|
+
return run_async_handler(
|
2297
|
+
handle_files,
|
1981
2298
|
client,
|
1982
2299
|
args.account_address,
|
1983
2300
|
show_all_miners=(
|
@@ -1985,8 +2302,21 @@ examples:
|
|
1985
2302
|
),
|
1986
2303
|
)
|
1987
2304
|
|
2305
|
+
elif args.command == "pinning-status":
|
2306
|
+
show_contents = (
|
2307
|
+
not args.no_contents if hasattr(args, "no_contents") else True
|
2308
|
+
)
|
2309
|
+
return run_async_handler(
|
2310
|
+
handle_pinning_status,
|
2311
|
+
client,
|
2312
|
+
args.account_address,
|
2313
|
+
verbose=args.verbose,
|
2314
|
+
show_contents=show_contents,
|
2315
|
+
)
|
2316
|
+
|
1988
2317
|
elif args.command == "ec-files":
|
1989
|
-
return
|
2318
|
+
return run_async_handler(
|
2319
|
+
handle_ec_files,
|
1990
2320
|
client,
|
1991
2321
|
args.account_address,
|
1992
2322
|
show_all_miners=(
|
@@ -1996,7 +2326,8 @@ examples:
|
|
1996
2326
|
)
|
1997
2327
|
|
1998
2328
|
elif args.command == "erasure-code":
|
1999
|
-
return
|
2329
|
+
return run_async_handler(
|
2330
|
+
handle_erasure_code,
|
2000
2331
|
client,
|
2001
2332
|
args.file_path,
|
2002
2333
|
args.k,
|
@@ -2008,10 +2339,29 @@ examples:
|
|
2008
2339
|
)
|
2009
2340
|
|
2010
2341
|
elif args.command == "reconstruct":
|
2011
|
-
return
|
2012
|
-
|
2342
|
+
return run_async_handler(
|
2343
|
+
handle_reconstruct,
|
2344
|
+
client,
|
2345
|
+
args.metadata_cid,
|
2346
|
+
args.output_file,
|
2347
|
+
verbose=args.verbose,
|
2013
2348
|
)
|
2014
2349
|
|
2350
|
+
elif args.command == "keygen":
|
2351
|
+
# Generate and save an encryption key
|
2352
|
+
client = HippiusClient()
|
2353
|
+
encryption_key = client.generate_encryption_key()
|
2354
|
+
print(f"Generated encryption key: {encryption_key}")
|
2355
|
+
|
2356
|
+
# Save to config if requested
|
2357
|
+
if hasattr(args, "save") and args.save:
|
2358
|
+
print("Saving encryption key to configuration...")
|
2359
|
+
handle_config_set("encryption", "encryption_key", encryption_key)
|
2360
|
+
print(
|
2361
|
+
"Encryption key saved. Files will not be automatically encrypted unless you set encryption.encrypt_by_default to true"
|
2362
|
+
)
|
2363
|
+
return 0
|
2364
|
+
|
2015
2365
|
elif args.command == "config":
|
2016
2366
|
if args.config_action == "get":
|
2017
2367
|
return handle_config_get(args.section, args.key)
|