meshcore-cli 1.2.1__py3-none-any.whl → 1.2.3__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.
- meshcore_cli/meshcore_cli.py +148 -20
- {meshcore_cli-1.2.1.dist-info → meshcore_cli-1.2.3.dist-info}/METADATA +2 -1
- meshcore_cli-1.2.3.dist-info/RECORD +8 -0
- meshcore_cli-1.2.1.dist-info/RECORD +0 -8
- {meshcore_cli-1.2.1.dist-info → meshcore_cli-1.2.3.dist-info}/WHEEL +0 -0
- {meshcore_cli-1.2.1.dist-info → meshcore_cli-1.2.3.dist-info}/entry_points.txt +0 -0
- {meshcore_cli-1.2.1.dist-info → meshcore_cli-1.2.3.dist-info}/licenses/LICENSE +0 -0
meshcore_cli/meshcore_cli.py
CHANGED
|
@@ -24,13 +24,16 @@ from prompt_toolkit.key_binding import KeyBindings
|
|
|
24
24
|
from prompt_toolkit.shortcuts import radiolist_dialog
|
|
25
25
|
from prompt_toolkit.completion.word_completer import WordCompleter
|
|
26
26
|
from prompt_toolkit.document import Document
|
|
27
|
+
from hashlib import sha256
|
|
28
|
+
from Crypto.Cipher import AES
|
|
29
|
+
from Crypto.Hash import HMAC, SHA256
|
|
27
30
|
|
|
28
31
|
import re
|
|
29
32
|
|
|
30
33
|
from meshcore import MeshCore, EventType, logger
|
|
31
34
|
|
|
32
35
|
# Version
|
|
33
|
-
VERSION = "v1.2.
|
|
36
|
+
VERSION = "v1.2.3"
|
|
34
37
|
|
|
35
38
|
# default ble address is stored in a config file
|
|
36
39
|
MCCLI_CONFIG_DIR = str(Path.home()) + "/.config/meshcore/"
|
|
@@ -52,6 +55,7 @@ ANSI_INVERT = "\033[7m"
|
|
|
52
55
|
ANSI_NORMAL = "\033[27m"
|
|
53
56
|
ANSI_GREEN = "\033[0;32m"
|
|
54
57
|
ANSI_BGREEN = "\033[1;32m"
|
|
58
|
+
ANSI_DGREEN="\033[0;38;5;22m"
|
|
55
59
|
ANSI_BLUE = "\033[0;34m"
|
|
56
60
|
ANSI_BBLUE = "\033[1;34m"
|
|
57
61
|
ANSI_RED = "\033[0;31m"
|
|
@@ -193,6 +197,57 @@ process_event_message.print_snr=False
|
|
|
193
197
|
process_event_message.color=True
|
|
194
198
|
process_event_message.last_node=None
|
|
195
199
|
|
|
200
|
+
async def handle_log_rx(event):
|
|
201
|
+
mc = handle_log_rx.mc
|
|
202
|
+
if handle_log_rx.json_log_rx: # json mode ... raw dump
|
|
203
|
+
msg = json.dumps(event.payload)
|
|
204
|
+
if handle_message.above:
|
|
205
|
+
print_above(msg)
|
|
206
|
+
else :
|
|
207
|
+
print(msg)
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
pkt = bytes().fromhex(event.payload["payload"])
|
|
211
|
+
|
|
212
|
+
if handle_log_rx.channel_echoes:
|
|
213
|
+
if pkt[0] == 0x15:
|
|
214
|
+
path_len = pkt[1]
|
|
215
|
+
path = pkt[2:path_len+2].hex()
|
|
216
|
+
chan_hash = pkt[path_len+2:path_len+3].hex()
|
|
217
|
+
cipher_mac = pkt[path_len+3:path_len+5]
|
|
218
|
+
msg = pkt[path_len+5:]
|
|
219
|
+
channel = None
|
|
220
|
+
for c in await get_channels(mc):
|
|
221
|
+
if c["channel_hash"] == chan_hash : # validate against MAC
|
|
222
|
+
h = HMAC.new(bytes.fromhex(c["channel_secret"]), digestmod=SHA256)
|
|
223
|
+
h.update(msg)
|
|
224
|
+
if h.digest()[0:2] == cipher_mac:
|
|
225
|
+
channel = c
|
|
226
|
+
break
|
|
227
|
+
|
|
228
|
+
if channel is None :
|
|
229
|
+
chan_name = chan_hash
|
|
230
|
+
message = msg.hex()
|
|
231
|
+
else:
|
|
232
|
+
chan_name = channel["channel_name"]
|
|
233
|
+
aes_key = bytes.fromhex(channel["channel_secret"])
|
|
234
|
+
cipher = AES.new(aes_key, AES.MODE_ECB)
|
|
235
|
+
message = cipher.decrypt(msg)[5:].decode("utf-8").strip("\x00")
|
|
236
|
+
|
|
237
|
+
width = os.get_terminal_size().columns
|
|
238
|
+
cars = width - 13 - 2 * path_len - len(chan_name) - 1
|
|
239
|
+
dispmsg = message[0:cars]
|
|
240
|
+
txt = f"{ANSI_LIGHT_GRAY}{chan_name} {ANSI_DGREEN}{dispmsg+(cars-len(dispmsg))*" "} {ANSI_YELLOW}[{path}]{ANSI_LIGHT_GRAY}{event.payload['snr']:6,.2f}{event.payload['rssi']:4}{ANSI_END}"
|
|
241
|
+
if handle_message.above:
|
|
242
|
+
print_above(txt)
|
|
243
|
+
else:
|
|
244
|
+
print(txt)
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
handle_log_rx.json_log_rx = False
|
|
248
|
+
handle_log_rx.channel_echoes = False
|
|
249
|
+
handle_log_rx.mc = None
|
|
250
|
+
|
|
196
251
|
async def handle_advert(event):
|
|
197
252
|
if not handle_advert.print_adverts:
|
|
198
253
|
return
|
|
@@ -320,7 +375,7 @@ class MyNestedCompleter(NestedCompleter):
|
|
|
320
375
|
opts = self.options.keys()
|
|
321
376
|
completer = WordCompleter(
|
|
322
377
|
opts, ignore_case=self.ignore_case,
|
|
323
|
-
pattern=re.compile(r"([a-zA-Z0-9_
|
|
378
|
+
pattern=re.compile(r"([a-zA-Z0-9_\\/\#]+|[^a-zA-Z0-9_\s\#]+)"))
|
|
324
379
|
yield from completer.get_completions(document, complete_event)
|
|
325
380
|
else: # normal behavior for remainder
|
|
326
381
|
yield from super().get_completions(document, complete_event)
|
|
@@ -417,6 +472,8 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
417
472
|
"color" : {"on":None, "off":None},
|
|
418
473
|
"print_name" : {"on":None, "off":None},
|
|
419
474
|
"print_adverts" : {"on":None, "off":None},
|
|
475
|
+
"json_log_rx" : {"on":None, "off":None},
|
|
476
|
+
"channel_echoes" : {"on":None, "off":None},
|
|
420
477
|
"print_new_contacts" : {"on": None, "off":None},
|
|
421
478
|
"print_path_updates" : {"on":None,"off":None},
|
|
422
479
|
"classic_prompt" : {"on" : None, "off":None},
|
|
@@ -444,6 +501,8 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
444
501
|
"color":None,
|
|
445
502
|
"print_name":None,
|
|
446
503
|
"print_adverts":None,
|
|
504
|
+
"json_log_rx":None,
|
|
505
|
+
"channel_echoes":None,
|
|
447
506
|
"print_path_updates":None,
|
|
448
507
|
"print_new_contacts":None,
|
|
449
508
|
"classic_prompt":None,
|
|
@@ -604,6 +663,14 @@ def make_completion_dict(contacts, pending={}, to=None, channels=None):
|
|
|
604
663
|
|
|
605
664
|
completion_list.update(slash_contacts_completion_list)
|
|
606
665
|
|
|
666
|
+
slash_chan_completion_list = {}
|
|
667
|
+
if not channels is None:
|
|
668
|
+
for c in channels :
|
|
669
|
+
if c["channel_name"] != "":
|
|
670
|
+
slash_chan_completion_list["/" + c["channel_name"]] = None
|
|
671
|
+
|
|
672
|
+
completion_list.update(slash_chan_completion_list)
|
|
673
|
+
|
|
607
674
|
completion_list.update({
|
|
608
675
|
"script" : None,
|
|
609
676
|
"quit" : None
|
|
@@ -675,7 +742,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
675
742
|
if classic :
|
|
676
743
|
prompt = prompt + " > "
|
|
677
744
|
else :
|
|
678
|
-
prompt = prompt + "
|
|
745
|
+
prompt = prompt + f"{ANSI_NORMAL}🭬{ANSI_INVERT}"
|
|
679
746
|
|
|
680
747
|
if not contact is None :
|
|
681
748
|
if not last_ack:
|
|
@@ -696,7 +763,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
696
763
|
prompt = prompt + f"{ANSI_INVERT}"
|
|
697
764
|
|
|
698
765
|
if print_name and not classic :
|
|
699
|
-
prompt = prompt + "
|
|
766
|
+
prompt = prompt + f"{ANSI_NORMAL}🭨{ANSI_INVERT}"
|
|
700
767
|
|
|
701
768
|
prompt = prompt + f"{contact['adv_name']}"
|
|
702
769
|
if classic :
|
|
@@ -728,14 +795,31 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
728
795
|
|
|
729
796
|
# raw meshcli command as on command line
|
|
730
797
|
elif line.startswith("$") :
|
|
731
|
-
|
|
732
|
-
|
|
798
|
+
try :
|
|
799
|
+
args = shlex.split(line[1:])
|
|
800
|
+
await process_cmds(mc, args)
|
|
801
|
+
except ValueError:
|
|
802
|
+
logger.error("Error parsing line {line[1:]}")
|
|
733
803
|
|
|
734
804
|
elif line.startswith("/") :
|
|
735
805
|
path = line.split(" ", 1)[0]
|
|
736
806
|
if path.count("/") == 1:
|
|
737
|
-
args =
|
|
738
|
-
|
|
807
|
+
args = line[1:].split(" ")
|
|
808
|
+
tct = mc.get_contact_by_name(args[0])
|
|
809
|
+
if len(args)>1 and not tct is None: # a contact, send a message
|
|
810
|
+
if tct["type"] == 1 or tct["type"] == 3: # client or room
|
|
811
|
+
last_ack = await msg_ack(mc, tct, line.split(" ", 1)[1])
|
|
812
|
+
else:
|
|
813
|
+
print("Can only send msg to chan, client or room")
|
|
814
|
+
else :
|
|
815
|
+
ch = await get_channel_by_name(mc, args[0])
|
|
816
|
+
if len(args)>1 and not ch is None: # a channel, send message
|
|
817
|
+
await send_chan_msg(mc, ch["channel_idx"], line.split(" ", 1)[1])
|
|
818
|
+
else :
|
|
819
|
+
try :
|
|
820
|
+
await process_cmds(mc, shlex.split(line[1:]))
|
|
821
|
+
except ValueError:
|
|
822
|
+
logger.error(f"Error processing line{line[1:]}")
|
|
739
823
|
else:
|
|
740
824
|
cmdline = line[1:].split("/",1)[1]
|
|
741
825
|
contact_name = path[1:].split("/",1)[0]
|
|
@@ -744,10 +828,11 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
744
828
|
print(f"{contact_name} is not a contact")
|
|
745
829
|
else:
|
|
746
830
|
if not await process_contact_chat_line(mc, tct, cmdline):
|
|
747
|
-
if
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
831
|
+
if cmdline != "":
|
|
832
|
+
if tct["type"] == 1:
|
|
833
|
+
last_ack = await msg_ack(mc, tct, cmdline)
|
|
834
|
+
else :
|
|
835
|
+
await process_cmds(mc, ["cmd", tct["adv_name"], cmdline])
|
|
751
836
|
|
|
752
837
|
elif line.startswith("to ") : # dest
|
|
753
838
|
dest = line[3:]
|
|
@@ -805,7 +890,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
805
890
|
if ln is None :
|
|
806
891
|
print("No received msg yet !")
|
|
807
892
|
elif ln["type"] == 0 :
|
|
808
|
-
await
|
|
893
|
+
await send_chan_msg(mc, ln["chan_nb"], line[1:])
|
|
809
894
|
else :
|
|
810
895
|
last_ack = await msg_ack(mc, ln, line[1:])
|
|
811
896
|
if last_ack == False :
|
|
@@ -813,8 +898,11 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
813
898
|
|
|
814
899
|
# commands are passed through if at root
|
|
815
900
|
elif contact is None or line.startswith(".") :
|
|
816
|
-
|
|
817
|
-
|
|
901
|
+
try:
|
|
902
|
+
args = shlex.split(line)
|
|
903
|
+
await process_cmds(mc, args)
|
|
904
|
+
except ValueError:
|
|
905
|
+
logger.error(f"Error processing {line}")
|
|
818
906
|
|
|
819
907
|
elif await process_contact_chat_line(mc, contact, line):
|
|
820
908
|
pass
|
|
@@ -837,7 +925,7 @@ Line starting with \"$\" or \".\" will issue a meshcli command.
|
|
|
837
925
|
last_ack = await msg_ack(mc, contact, line)
|
|
838
926
|
|
|
839
927
|
elif contact["type"] == 0 : # channel, send msg to channel
|
|
840
|
-
await
|
|
928
|
+
await send_chan_msg(mc, contact["chan_nb"], line)
|
|
841
929
|
|
|
842
930
|
elif contact["type"] == 1 : # chat, send to recipient and wait ack
|
|
843
931
|
last_ack = await msg_ack(mc, contact, line)
|
|
@@ -970,7 +1058,18 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
970
1058
|
password_file = ""
|
|
971
1059
|
password = ""
|
|
972
1060
|
if os.path.isdir(MCCLI_CONFIG_DIR) :
|
|
1061
|
+
# if a password file exists with node name open it and destroy it
|
|
973
1062
|
password_file = MCCLI_CONFIG_DIR + contact['adv_name'] + ".pass"
|
|
1063
|
+
if os.path.exists(password_file) :
|
|
1064
|
+
with open(password_file, "r", encoding="utf-8") as f :
|
|
1065
|
+
password=f.readline().strip()
|
|
1066
|
+
os.remove(password_file)
|
|
1067
|
+
password_file = MCCLI_CONFIG_DIR + contact["public_key"] + ".pass"
|
|
1068
|
+
with open(password_file, "w", encoding="utf-8") as f :
|
|
1069
|
+
f.write(password)
|
|
1070
|
+
|
|
1071
|
+
# this is the new correct password file, using pubkey
|
|
1072
|
+
password_file = MCCLI_CONFIG_DIR + contact["public_key"] + ".pass"
|
|
974
1073
|
if os.path.exists(password_file) :
|
|
975
1074
|
with open(password_file, "r", encoding="utf-8") as f :
|
|
976
1075
|
password=f.readline().strip()
|
|
@@ -993,6 +1092,9 @@ async def process_contact_chat_line(mc, contact, line):
|
|
|
993
1092
|
|
|
994
1093
|
if line.startswith("forget_password") or line.startswith("fp"):
|
|
995
1094
|
password_file = MCCLI_CONFIG_DIR + contact['adv_name'] + ".pass"
|
|
1095
|
+
if os.path.exists(password_file):
|
|
1096
|
+
os.remove(password_file)
|
|
1097
|
+
password_file = MCCLI_CONFIG_DIR + contact['public_key'] + ".pass"
|
|
996
1098
|
if os.path.exists(password_file):
|
|
997
1099
|
os.remove(password_file)
|
|
998
1100
|
try:
|
|
@@ -1112,6 +1214,7 @@ async def set_channel (mc, chan, name, key=None):
|
|
|
1112
1214
|
return None
|
|
1113
1215
|
|
|
1114
1216
|
info = res.payload
|
|
1217
|
+
info["channel_hash"] = sha256(info["channel_secret"]).digest()[0:1].hex()
|
|
1115
1218
|
info["channel_secret"] = info["channel_secret"].hex()
|
|
1116
1219
|
|
|
1117
1220
|
if hasattr(mc,'channels') :
|
|
@@ -1200,12 +1303,14 @@ async def get_channels (mc, anim=False) :
|
|
|
1200
1303
|
if res.type == EventType.ERROR:
|
|
1201
1304
|
break
|
|
1202
1305
|
info = res.payload
|
|
1306
|
+
info["channel_hash"] = sha256(info["channel_secret"]).digest()[0:1].hex()
|
|
1203
1307
|
info["channel_secret"] = info["channel_secret"].hex()
|
|
1204
1308
|
mc.channels.append(info)
|
|
1205
1309
|
ch = ch + 1
|
|
1206
1310
|
if anim:
|
|
1207
1311
|
print(".", end="", flush=True)
|
|
1208
|
-
|
|
1312
|
+
if anim:
|
|
1313
|
+
print (" Done")
|
|
1209
1314
|
return mc.channels
|
|
1210
1315
|
|
|
1211
1316
|
async def print_trace_to (mc, contact):
|
|
@@ -1411,6 +1516,14 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1411
1516
|
process_event_message.print_snr = (cmds[2] == "on")
|
|
1412
1517
|
if json_output :
|
|
1413
1518
|
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1519
|
+
case "json_log_rx" :
|
|
1520
|
+
handle_log_rx.json_log_rx = (cmds[2] == "on")
|
|
1521
|
+
if json_output :
|
|
1522
|
+
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1523
|
+
case "channel_echoes" :
|
|
1524
|
+
handle_log_rx.channel_echoes = (cmds[2] == "on")
|
|
1525
|
+
if json_output :
|
|
1526
|
+
print(json.dumps({"cmd" : cmds[1], "param" : cmds[2]}))
|
|
1414
1527
|
case "print_adverts" :
|
|
1415
1528
|
handle_advert.print_adverts = (cmds[2] == "on")
|
|
1416
1529
|
if json_output :
|
|
@@ -1641,6 +1754,16 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
1641
1754
|
print(json.dumps({"color" : process_event_message.color}))
|
|
1642
1755
|
else:
|
|
1643
1756
|
print(f"{'on' if process_event_message.color else 'off'}")
|
|
1757
|
+
case "json_log_rx":
|
|
1758
|
+
if json_output :
|
|
1759
|
+
print(json.dumps({"json_log_rx" : handle_log_rx.json_log_rx}))
|
|
1760
|
+
else:
|
|
1761
|
+
print(f"{'on' if handle_log_rx.json_log_rx else 'off'}")
|
|
1762
|
+
case "channel_echoes":
|
|
1763
|
+
if json_output :
|
|
1764
|
+
print(json.dumps({"channel_echoes" : handle_log_rx.channel_echoes}))
|
|
1765
|
+
else:
|
|
1766
|
+
print(f"{'on' if handle_log_rx.channel_echoes else 'off'}")
|
|
1644
1767
|
case "print_adverts":
|
|
1645
1768
|
if json_output :
|
|
1646
1769
|
print(json.dumps({"print_adverts" : handle_advert.print_adverts}))
|
|
@@ -2511,7 +2634,7 @@ async def next_cmd(mc, cmds, json_output=False):
|
|
|
2511
2634
|
if json_output:
|
|
2512
2635
|
await ps.prompt_async()
|
|
2513
2636
|
else:
|
|
2514
|
-
await ps.prompt_async("Press Enter to continue
|
|
2637
|
+
await ps.prompt_async("Press Enter to continue ...\n")
|
|
2515
2638
|
except (EOFError, KeyboardInterrupt, asyncio.CancelledError):
|
|
2516
2639
|
pass
|
|
2517
2640
|
|
|
@@ -2604,8 +2727,11 @@ async def process_script(mc, file, json_output=False):
|
|
|
2604
2727
|
line = line.strip()
|
|
2605
2728
|
if not (line == "" or line[0] == "#"):
|
|
2606
2729
|
logger.debug(f"processing {line}")
|
|
2607
|
-
|
|
2608
|
-
|
|
2730
|
+
try :
|
|
2731
|
+
cmds = shlex.split(line)
|
|
2732
|
+
await process_cmds(mc, cmds, json_output)
|
|
2733
|
+
except ValueError:
|
|
2734
|
+
logger.error(f"Error processing {line}")
|
|
2609
2735
|
|
|
2610
2736
|
def version():
|
|
2611
2737
|
print (f"meshcore-cli: command line interface to MeshCore companion radios {VERSION}")
|
|
@@ -2881,10 +3007,12 @@ async def main(argv):
|
|
|
2881
3007
|
handle_message.mc = mc # connect meshcore to handle_message
|
|
2882
3008
|
handle_advert.mc = mc
|
|
2883
3009
|
handle_path_update.mc = mc
|
|
3010
|
+
handle_log_rx.mc = mc
|
|
2884
3011
|
|
|
2885
3012
|
mc.subscribe(EventType.ADVERTISEMENT, handle_advert)
|
|
2886
3013
|
mc.subscribe(EventType.PATH_UPDATE, handle_path_update)
|
|
2887
3014
|
mc.subscribe(EventType.NEW_CONTACT, handle_new_contact)
|
|
3015
|
+
mc.subscribe(EventType.RX_LOG_DATA, handle_log_rx)
|
|
2888
3016
|
|
|
2889
3017
|
mc.auto_update_contacts = True
|
|
2890
3018
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcore-cli
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: Command line interface to meshcore companion radios
|
|
5
5
|
Project-URL: Homepage, https://github.com/fdlamotte/meshcore-cli
|
|
6
6
|
Project-URL: Issues, https://github.com/fdlamotte/meshcore-cli/issues
|
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
13
|
Requires-Dist: meshcore>=2.1.19
|
|
14
14
|
Requires-Dist: prompt-toolkit>=3.0.50
|
|
15
|
+
Requires-Dist: pycryptodome
|
|
15
16
|
Requires-Dist: requests>=2.28.0
|
|
16
17
|
Description-Content-Type: text/markdown
|
|
17
18
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
meshcore_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
meshcore_cli/__main__.py,sha256=PfYgibmu2LEtC-OV7L1UgmvV3swJ5rQ4bbXHlwUFlgE,83
|
|
3
|
+
meshcore_cli/meshcore_cli.py,sha256=4vJusi30D_UBxw9lq4mTO5NqLp8y3zpTb09rCcF7064,126909
|
|
4
|
+
meshcore_cli-1.2.3.dist-info/METADATA,sha256=Z727GJ6CD7OzRfEUGose_dAfACX6Oo2IbM3kzBWihtc,11657
|
|
5
|
+
meshcore_cli-1.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
meshcore_cli-1.2.3.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
|
|
7
|
+
meshcore_cli-1.2.3.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
|
|
8
|
+
meshcore_cli-1.2.3.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
meshcore_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
meshcore_cli/__main__.py,sha256=PfYgibmu2LEtC-OV7L1UgmvV3swJ5rQ4bbXHlwUFlgE,83
|
|
3
|
-
meshcore_cli/meshcore_cli.py,sha256=6bMf4qSfISXSragUj9Av6i9xB-hE2ZfGmOs0-8D6D58,120885
|
|
4
|
-
meshcore_cli-1.2.1.dist-info/METADATA,sha256=Xh4Vcnhnevq2ZdPInOP0wSp2V3eRVj41THOnXPj8Gzw,11629
|
|
5
|
-
meshcore_cli-1.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
meshcore_cli-1.2.1.dist-info/entry_points.txt,sha256=77V29Pyth11GteDk7tneBN3MMk8JI7bTlS-BGSmxCmI,103
|
|
7
|
-
meshcore_cli-1.2.1.dist-info/licenses/LICENSE,sha256=F9s987VtS0AKxW7LdB2EkLMkrdeERI7ICdLJR60A9M4,1066
|
|
8
|
-
meshcore_cli-1.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|