neuronum 5.7.0__tar.gz → 5.8.1__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.
Potentially problematic release.
This version of neuronum might be problematic. Click here for more details.
- {neuronum-5.7.0/neuronum.egg-info → neuronum-5.8.1}/PKG-INFO +12 -7
- {neuronum-5.7.0 → neuronum-5.8.1}/README.md +10 -6
- {neuronum-5.7.0 → neuronum-5.8.1}/cli/main.py +234 -141
- {neuronum-5.7.0 → neuronum-5.8.1/neuronum.egg-info}/PKG-INFO +12 -7
- {neuronum-5.7.0 → neuronum-5.8.1}/neuronum.egg-info/requires.txt +1 -0
- {neuronum-5.7.0 → neuronum-5.8.1}/setup.py +2 -1
- {neuronum-5.7.0 → neuronum-5.8.1}/LICENSE.md +0 -0
- {neuronum-5.7.0 → neuronum-5.8.1}/cli/__init__.py +0 -0
- {neuronum-5.7.0 → neuronum-5.8.1}/neuronum/__init__.py +0 -0
- {neuronum-5.7.0 → neuronum-5.8.1}/neuronum/neuronum.py +0 -0
- {neuronum-5.7.0 → neuronum-5.8.1}/neuronum.egg-info/SOURCES.txt +0 -0
- {neuronum-5.7.0 → neuronum-5.8.1}/neuronum.egg-info/dependency_links.txt +0 -0
- {neuronum-5.7.0 → neuronum-5.8.1}/neuronum.egg-info/entry_points.txt +0 -0
- {neuronum-5.7.0 → neuronum-5.8.1}/neuronum.egg-info/top_level.txt +0 -0
- {neuronum-5.7.0 → neuronum-5.8.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: neuronum
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.8.1
|
|
4
4
|
Summary: Official client library to interact with the Neuronum Network
|
|
5
5
|
Home-page: https://neuronum.net
|
|
6
6
|
Author: Neuronum Cybernetics
|
|
@@ -18,6 +18,7 @@ Requires-Dist: click
|
|
|
18
18
|
Requires-Dist: questionary
|
|
19
19
|
Requires-Dist: python-dotenv
|
|
20
20
|
Requires-Dist: requests
|
|
21
|
+
Requires-Dist: psutil
|
|
21
22
|
Dynamic: author
|
|
22
23
|
Dynamic: author-email
|
|
23
24
|
Dynamic: classifier
|
|
@@ -54,10 +55,11 @@ Dynamic: summary
|
|
|
54
55
|
------------------
|
|
55
56
|
|
|
56
57
|
### **A Getting Started into the Neuronum Network**
|
|
57
|
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
58
|
+
In this brief getting started guide, you will:
|
|
59
|
+
- [Learn about Neuronum](#about-neuronum)
|
|
60
|
+
- [Connect to the Network](#connect-to-neuronum)
|
|
61
|
+
- [Build a Neuronum Node](#build-on-neuronum)
|
|
62
|
+
- [Interact with your Node](#interact-with-neuronum)
|
|
61
63
|
|
|
62
64
|
------------------
|
|
63
65
|
|
|
@@ -101,10 +103,13 @@ neuronum connect-cell # connect Cell
|
|
|
101
103
|
------------------
|
|
102
104
|
|
|
103
105
|
|
|
104
|
-
### **Build On Neuronum**
|
|
106
|
+
### **Build On Neuronum**
|
|
107
|
+
[View Node Examples](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes)
|
|
108
|
+
|
|
109
|
+
|
|
105
110
|
Initialize a Node (app template):
|
|
106
111
|
```sh
|
|
107
|
-
neuronum init-node --app # initialize a Node with app template
|
|
112
|
+
neuronum init-node --app # initialize a Node with app template
|
|
108
113
|
```
|
|
109
114
|
|
|
110
115
|
Change into Node folder
|
|
@@ -22,10 +22,11 @@
|
|
|
22
22
|
------------------
|
|
23
23
|
|
|
24
24
|
### **A Getting Started into the Neuronum Network**
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
25
|
+
In this brief getting started guide, you will:
|
|
26
|
+
- [Learn about Neuronum](#about-neuronum)
|
|
27
|
+
- [Connect to the Network](#connect-to-neuronum)
|
|
28
|
+
- [Build a Neuronum Node](#build-on-neuronum)
|
|
29
|
+
- [Interact with your Node](#interact-with-neuronum)
|
|
29
30
|
|
|
30
31
|
------------------
|
|
31
32
|
|
|
@@ -69,10 +70,13 @@ neuronum connect-cell # connect Cell
|
|
|
69
70
|
------------------
|
|
70
71
|
|
|
71
72
|
|
|
72
|
-
### **Build On Neuronum**
|
|
73
|
+
### **Build On Neuronum**
|
|
74
|
+
[View Node Examples](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes)
|
|
75
|
+
|
|
76
|
+
|
|
73
77
|
Initialize a Node (app template):
|
|
74
78
|
```sh
|
|
75
|
-
neuronum init-node --app # initialize a Node with app template
|
|
79
|
+
neuronum init-node --app # initialize a Node with app template
|
|
76
80
|
```
|
|
77
81
|
|
|
78
82
|
Change into Node folder
|
|
@@ -10,6 +10,8 @@ import click
|
|
|
10
10
|
import questionary
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
import requests
|
|
13
|
+
import psutil
|
|
14
|
+
from datetime import datetime
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
@click.group()
|
|
@@ -251,9 +253,13 @@ def delete_cell():
|
|
|
251
253
|
@click.option('--stream', multiple=True, default=None, help="Optional stream ID for stream.")
|
|
252
254
|
@click.option('--app', is_flag=True, help="Generate a Node with app template")
|
|
253
255
|
def init_node(sync, stream, app):
|
|
254
|
-
|
|
256
|
+
descr = click.prompt("Node description: Type up to 25 characters").strip()
|
|
257
|
+
if descr and len(descr) > 25:
|
|
258
|
+
click.echo("Description too long. Max 25 characters allowed.")
|
|
259
|
+
return
|
|
260
|
+
asyncio.run(async_init_node(sync, stream, app, descr))
|
|
255
261
|
|
|
256
|
-
async def async_init_node(sync, stream, app):
|
|
262
|
+
async def async_init_node(sync, stream, app, descr):
|
|
257
263
|
credentials_folder_path = Path.home() / ".neuronum"
|
|
258
264
|
env_path = credentials_folder_path / ".env"
|
|
259
265
|
|
|
@@ -278,11 +284,16 @@ async def async_init_node(sync, stream, app):
|
|
|
278
284
|
return
|
|
279
285
|
|
|
280
286
|
url = f"https://{network}/api/init_node"
|
|
281
|
-
|
|
287
|
+
node = {
|
|
288
|
+
"host": host,
|
|
289
|
+
"password": password,
|
|
290
|
+
"synapse": synapse,
|
|
291
|
+
"descr": descr,
|
|
292
|
+
}
|
|
282
293
|
|
|
283
294
|
async with aiohttp.ClientSession() as session:
|
|
284
295
|
try:
|
|
285
|
-
async with session.post(url, json=
|
|
296
|
+
async with session.post(url, json=node) as response:
|
|
286
297
|
response.raise_for_status()
|
|
287
298
|
data = await response.json()
|
|
288
299
|
nodeID = data["nodeID"]
|
|
@@ -316,16 +327,6 @@ async def async_init_node(sync, stream, app):
|
|
|
316
327
|
env_path = project_path / ".env"
|
|
317
328
|
await asyncio.to_thread(env_path.write_text, f"NODE={nodeID}\nHOST={host}\nPASSWORD={password}\nNETWORK={network}\nSYNAPSE={synapse}\n")
|
|
318
329
|
|
|
319
|
-
gitignore_path = project_path / ".gitignore"
|
|
320
|
-
await asyncio.to_thread(gitignore_path.write_text, ".env\n")
|
|
321
|
-
|
|
322
|
-
requirements_path = project_path / "requirements.txt"
|
|
323
|
-
requirements_content = """\
|
|
324
|
-
# Please add additional packages below if your Node uses more
|
|
325
|
-
neuronum
|
|
326
|
-
"""
|
|
327
|
-
await asyncio.to_thread(requirements_path.write_text, requirements_content)
|
|
328
|
-
|
|
329
330
|
nodemd_path = project_path / "NODE.md"
|
|
330
331
|
await asyncio.to_thread(nodemd_path.write_text, """### NODE.md: How to interact with this Node
|
|
331
332
|
|
|
@@ -565,13 +566,38 @@ asyncio.run(main())
|
|
|
565
566
|
@click.command()
|
|
566
567
|
@click.option('--d', is_flag=True, help="Start node in detached mode")
|
|
567
568
|
def start_node(d):
|
|
569
|
+
pid_file = Path.cwd() / "status.txt"
|
|
570
|
+
system_name = platform.system()
|
|
571
|
+
active_pids = []
|
|
572
|
+
|
|
573
|
+
start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
574
|
+
|
|
575
|
+
if pid_file.exists():
|
|
576
|
+
try:
|
|
577
|
+
with open(pid_file, "r") as f:
|
|
578
|
+
pids = [int(line.strip()) for line in f if line.strip().isdigit()]
|
|
579
|
+
for pid in pids:
|
|
580
|
+
if system_name == "Windows":
|
|
581
|
+
if psutil.pid_exists(pid):
|
|
582
|
+
active_pids.append(pid)
|
|
583
|
+
else:
|
|
584
|
+
try:
|
|
585
|
+
os.kill(pid, 0)
|
|
586
|
+
active_pids.append(pid)
|
|
587
|
+
except OSError:
|
|
588
|
+
continue
|
|
589
|
+
except Exception as e:
|
|
590
|
+
click.echo(f"Failed to read PID file: {e}")
|
|
591
|
+
|
|
592
|
+
if active_pids:
|
|
593
|
+
click.echo(f"Node is already running. Active PIDs: {', '.join(map(str, active_pids))}")
|
|
594
|
+
return
|
|
595
|
+
|
|
568
596
|
click.echo("Starting Node...")
|
|
569
597
|
|
|
570
598
|
project_path = Path.cwd()
|
|
571
599
|
script_files = glob.glob("sync_*.py") + glob.glob("stream_*.py") + glob.glob("app.py")
|
|
572
|
-
|
|
573
600
|
processes = []
|
|
574
|
-
system_name = platform.system()
|
|
575
601
|
|
|
576
602
|
for script in script_files:
|
|
577
603
|
script_path = project_path / script
|
|
@@ -587,7 +613,10 @@ def start_node(d):
|
|
|
587
613
|
start_new_session=True
|
|
588
614
|
)
|
|
589
615
|
else:
|
|
590
|
-
process = subprocess.Popen(
|
|
616
|
+
process = subprocess.Popen(
|
|
617
|
+
[python_cmd, str(script_path)],
|
|
618
|
+
start_new_session=True
|
|
619
|
+
)
|
|
591
620
|
|
|
592
621
|
processes.append(process.pid)
|
|
593
622
|
|
|
@@ -595,55 +624,172 @@ def start_node(d):
|
|
|
595
624
|
click.echo("Error: No valid node script found. Ensure the node is set up correctly.")
|
|
596
625
|
return
|
|
597
626
|
|
|
598
|
-
with open(
|
|
627
|
+
with open(pid_file, "w") as f:
|
|
628
|
+
f.write(f"Started at: {start_time}\n")
|
|
599
629
|
f.write("\n".join(map(str, processes)))
|
|
600
630
|
|
|
601
|
-
click.echo("Node started successfully
|
|
631
|
+
click.echo(f"Node started successfully with PIDs: {', '.join(map(str, processes))}")
|
|
602
632
|
|
|
603
633
|
|
|
604
634
|
@click.command()
|
|
605
|
-
def
|
|
606
|
-
|
|
635
|
+
def check_node():
|
|
636
|
+
click.echo("Checking Node status...")
|
|
607
637
|
|
|
608
|
-
|
|
609
|
-
|
|
638
|
+
env_data = {}
|
|
639
|
+
try:
|
|
640
|
+
with open(".env", "r") as f:
|
|
641
|
+
for line in f:
|
|
642
|
+
if "=" in line:
|
|
643
|
+
key, value = line.strip().split("=", 1)
|
|
644
|
+
env_data[key] = value
|
|
645
|
+
nodeID = env_data.get("NODE", "")
|
|
646
|
+
except FileNotFoundError:
|
|
647
|
+
click.echo("Error: .env with credentials not found")
|
|
648
|
+
return
|
|
649
|
+
except Exception as e:
|
|
650
|
+
click.echo(f"Error reading .env file: {e}")
|
|
651
|
+
return
|
|
652
|
+
|
|
653
|
+
pid_file = Path.cwd() / "status.txt"
|
|
610
654
|
|
|
611
|
-
|
|
655
|
+
if not pid_file.exists():
|
|
656
|
+
click.echo(f"Node {nodeID} is not running. Status file missing.")
|
|
657
|
+
return
|
|
612
658
|
|
|
613
659
|
try:
|
|
614
|
-
with open(
|
|
615
|
-
|
|
660
|
+
with open(pid_file, "r") as f:
|
|
661
|
+
lines = f.readlines()
|
|
662
|
+
timestamp_line = next((line for line in lines if line.startswith("Started at:")), None)
|
|
663
|
+
pids = [int(line.strip()) for line in lines if line.strip().isdigit()]
|
|
664
|
+
|
|
665
|
+
if timestamp_line:
|
|
666
|
+
click.echo(timestamp_line.strip())
|
|
667
|
+
start_time = datetime.strptime(timestamp_line.split(":", 1)[1].strip(), "%Y-%m-%d %H:%M:%S")
|
|
668
|
+
now = datetime.now()
|
|
669
|
+
uptime = now - start_time
|
|
670
|
+
click.echo(f"Uptime: {str(uptime).split('.')[0]}")
|
|
671
|
+
except Exception as e:
|
|
672
|
+
click.echo(f"Failed to read PID file: {e}")
|
|
673
|
+
return
|
|
616
674
|
|
|
617
|
-
|
|
675
|
+
system_name = platform.system()
|
|
676
|
+
running_pids = []
|
|
618
677
|
|
|
619
|
-
|
|
678
|
+
for pid in pids:
|
|
679
|
+
if system_name == "Windows":
|
|
680
|
+
if psutil.pid_exists(pid):
|
|
681
|
+
running_pids.append(pid)
|
|
682
|
+
else:
|
|
620
683
|
try:
|
|
684
|
+
os.kill(pid, 0)
|
|
685
|
+
running_pids.append(pid)
|
|
686
|
+
except OSError:
|
|
687
|
+
continue
|
|
688
|
+
|
|
689
|
+
if running_pids:
|
|
690
|
+
for pid in running_pids:
|
|
691
|
+
proc = psutil.Process(pid)
|
|
692
|
+
mem = proc.memory_info().rss / (1024 * 1024) # in MB
|
|
693
|
+
cpu = proc.cpu_percent(interval=0.1)
|
|
694
|
+
click.echo(f"PID {pid} → Memory: {mem:.2f} MB | CPU: {cpu:.1f}%")
|
|
695
|
+
click.echo(f"Node {nodeID} is running. Active PIDs: {', '.join(map(str, running_pids))}")
|
|
696
|
+
else:
|
|
697
|
+
click.echo(f"Node {nodeID} is not running.")
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
@click.command()
|
|
701
|
+
@click.option('--d', is_flag=True, help="Restart node in detached mode")
|
|
702
|
+
def restart_node(d):
|
|
703
|
+
pid_file = Path.cwd() / "status.txt"
|
|
704
|
+
system_name = platform.system()
|
|
705
|
+
|
|
706
|
+
start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
707
|
+
|
|
708
|
+
env_data = {}
|
|
709
|
+
try:
|
|
710
|
+
with open(".env", "r") as f:
|
|
711
|
+
for line in f:
|
|
712
|
+
key, value = line.strip().split("=")
|
|
713
|
+
env_data[key] = value
|
|
714
|
+
|
|
715
|
+
nodeID = env_data.get("NODE", "")
|
|
716
|
+
|
|
717
|
+
except FileNotFoundError:
|
|
718
|
+
print("Error: .env with credentials not found")
|
|
719
|
+
return
|
|
720
|
+
except Exception as e:
|
|
721
|
+
print(f"Error reading .env file: {e}")
|
|
722
|
+
return
|
|
723
|
+
|
|
724
|
+
if pid_file.exists():
|
|
725
|
+
try:
|
|
726
|
+
with open(pid_file, "r") as f:
|
|
727
|
+
pids = [int(line.strip()) for line in f if line.strip().isdigit()]
|
|
728
|
+
|
|
729
|
+
for pid in pids:
|
|
621
730
|
if system_name == "Windows":
|
|
622
|
-
|
|
731
|
+
if psutil.pid_exists(pid):
|
|
732
|
+
proc = psutil.Process(pid)
|
|
733
|
+
proc.terminate()
|
|
623
734
|
else:
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
735
|
+
try:
|
|
736
|
+
os.kill(pid, 15)
|
|
737
|
+
except OSError:
|
|
738
|
+
continue
|
|
627
739
|
|
|
628
|
-
|
|
629
|
-
click.echo("Node stopped successfully!")
|
|
740
|
+
pid_file.unlink()
|
|
630
741
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
742
|
+
click.echo(f"Terminated existing {nodeID} processes: {', '.join(map(str, pids))}")
|
|
743
|
+
|
|
744
|
+
except Exception as e:
|
|
745
|
+
click.echo(f"Failed to terminate processes: {e}")
|
|
746
|
+
return
|
|
747
|
+
else:
|
|
748
|
+
click.echo(f"Node {nodeID} is not running")
|
|
749
|
+
|
|
750
|
+
click.echo(f"Starting Node {nodeID}...")
|
|
751
|
+
project_path = Path.cwd()
|
|
752
|
+
script_files = glob.glob("sync_*.py") + glob.glob("stream_*.py") + glob.glob("app.py")
|
|
753
|
+
processes = []
|
|
754
|
+
|
|
755
|
+
python_cmd = "pythonw" if system_name == "Windows" else "python"
|
|
756
|
+
|
|
757
|
+
for script in script_files:
|
|
758
|
+
script_path = project_path / script
|
|
759
|
+
if script_path.exists():
|
|
760
|
+
if d:
|
|
761
|
+
process = subprocess.Popen(
|
|
762
|
+
["nohup", python_cmd, str(script_path), "&"] if system_name != "Windows"
|
|
763
|
+
else [python_cmd, str(script_path)],
|
|
764
|
+
stdout=subprocess.DEVNULL,
|
|
765
|
+
stderr=subprocess.DEVNULL,
|
|
766
|
+
start_new_session=True
|
|
767
|
+
)
|
|
768
|
+
else:
|
|
769
|
+
process = subprocess.Popen([python_cmd, str(script_path)], start_new_session=True)
|
|
770
|
+
|
|
771
|
+
processes.append(process.pid)
|
|
772
|
+
|
|
773
|
+
if not processes:
|
|
774
|
+
click.echo("Error: No valid node script found.")
|
|
775
|
+
return
|
|
776
|
+
|
|
777
|
+
with open(pid_file, "w") as f:
|
|
778
|
+
f.write(f"Started at: {start_time}\n")
|
|
779
|
+
f.write("\n".join(map(str, processes)))
|
|
780
|
+
|
|
781
|
+
click.echo(f"Node {nodeID} started with new PIDs: {', '.join(map(str, processes))}")
|
|
635
782
|
|
|
636
783
|
|
|
637
784
|
@click.command()
|
|
638
|
-
def
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
785
|
+
def stop_node():
|
|
786
|
+
asyncio.run(async_stop_node())
|
|
787
|
+
|
|
788
|
+
async def async_stop_node():
|
|
789
|
+
click.echo("Stopping Node...")
|
|
790
|
+
|
|
791
|
+
node_pid_path = Path("status.txt")
|
|
645
792
|
|
|
646
|
-
async def async_connect_node(descr, node_type):
|
|
647
793
|
env_data = {}
|
|
648
794
|
try:
|
|
649
795
|
with open(".env", "r") as f:
|
|
@@ -652,10 +798,6 @@ async def async_connect_node(descr, node_type):
|
|
|
652
798
|
env_data[key] = value
|
|
653
799
|
|
|
654
800
|
nodeID = env_data.get("NODE", "")
|
|
655
|
-
host = env_data.get("HOST", "")
|
|
656
|
-
password = env_data.get("PASSWORD", "")
|
|
657
|
-
network = env_data.get("NETWORK", "")
|
|
658
|
-
synapse = env_data.get("SYNAPSE", "")
|
|
659
801
|
|
|
660
802
|
except FileNotFoundError:
|
|
661
803
|
print("Error: .env with credentials not found")
|
|
@@ -665,52 +807,46 @@ async def async_connect_node(descr, node_type):
|
|
|
665
807
|
return
|
|
666
808
|
|
|
667
809
|
try:
|
|
668
|
-
with open("
|
|
669
|
-
|
|
810
|
+
with open("status.txt", "r") as f:
|
|
811
|
+
pids = [int(line.strip()) for line in f if line.strip().isdigit()]
|
|
670
812
|
|
|
671
|
-
|
|
672
|
-
print("Error: NODE.md file not found")
|
|
673
|
-
return
|
|
674
|
-
except Exception as e:
|
|
675
|
-
print(f"Error reading NODE.md file: {e}")
|
|
676
|
-
return
|
|
813
|
+
system_name = platform.system()
|
|
677
814
|
|
|
678
|
-
|
|
815
|
+
for pid in pids:
|
|
816
|
+
try:
|
|
817
|
+
if system_name == "Windows":
|
|
818
|
+
await asyncio.to_thread(subprocess.run, ["taskkill", "/F", "/PID", str(pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
819
|
+
else:
|
|
820
|
+
await asyncio.to_thread(os.kill, pid, 9)
|
|
821
|
+
except ProcessLookupError:
|
|
822
|
+
click.echo(f"Warning: Process {pid} already stopped or does not exist.")
|
|
679
823
|
|
|
680
|
-
|
|
681
|
-
"nodeID"
|
|
682
|
-
"descr": descr,
|
|
683
|
-
"host": host,
|
|
684
|
-
"password": password,
|
|
685
|
-
"synapse": synapse,
|
|
686
|
-
"nodemd_file": nodemd_file
|
|
687
|
-
}
|
|
824
|
+
await asyncio.to_thread(os.remove, node_pid_path)
|
|
825
|
+
click.echo(f"Node {nodeID} stopped successfully!")
|
|
688
826
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
data = await response.json()
|
|
694
|
-
nodeID = data["nodeID"]
|
|
695
|
-
node_url = data["node_url"]
|
|
696
|
-
except aiohttp.ClientError as e:
|
|
697
|
-
click.echo(f"Error sending request: {e}")
|
|
698
|
-
return
|
|
699
|
-
|
|
700
|
-
if nodeID == "Node does not exist":
|
|
701
|
-
click.echo(f"Neuronum Node not found! Make sure you initialized your Node correctly")
|
|
702
|
-
else:
|
|
703
|
-
if node_type == "public":
|
|
704
|
-
click.echo(f"Public Neuronum Node '{nodeID}' connected! Visit: {node_url}")
|
|
705
|
-
else:
|
|
706
|
-
click.echo(f"Private Neuronum Node '{nodeID}' connected!")
|
|
827
|
+
except FileNotFoundError:
|
|
828
|
+
click.echo("Error: No active node process found.")
|
|
829
|
+
except subprocess.CalledProcessError:
|
|
830
|
+
click.echo("Error: Unable to stop some node processes.")
|
|
707
831
|
|
|
708
832
|
|
|
709
833
|
@click.command()
|
|
710
834
|
def update_node():
|
|
711
|
-
|
|
835
|
+
node_type = questionary.select(
|
|
836
|
+
"Update Node type:",
|
|
837
|
+
choices=["public", "private"]
|
|
838
|
+
).ask()
|
|
839
|
+
descr = click.prompt(
|
|
840
|
+
"Update Node description: Type up to 25 characters, or press Enter to leave it unchanged",
|
|
841
|
+
default="None",
|
|
842
|
+
show_default=False
|
|
843
|
+
).strip()
|
|
844
|
+
if descr and len(descr) > 25:
|
|
845
|
+
click.echo("Description too long. Max 25 characters allowed.")
|
|
846
|
+
return
|
|
847
|
+
asyncio.run(async_update_node(node_type, descr))
|
|
712
848
|
|
|
713
|
-
async def async_update_node():
|
|
849
|
+
async def async_update_node(node_type: str, descr: str) -> None:
|
|
714
850
|
env_data = {}
|
|
715
851
|
|
|
716
852
|
try:
|
|
@@ -744,17 +880,19 @@ async def async_update_node():
|
|
|
744
880
|
return
|
|
745
881
|
|
|
746
882
|
url = f"https://{network}/api/update_node"
|
|
747
|
-
|
|
883
|
+
node = {
|
|
748
884
|
"nodeID": nodeID,
|
|
749
885
|
"host": host,
|
|
750
886
|
"password": password,
|
|
751
887
|
"synapse": synapse,
|
|
752
|
-
"
|
|
888
|
+
"node_type": node_type,
|
|
889
|
+
"nodemd_file": nodemd_file,
|
|
890
|
+
"descr": descr,
|
|
753
891
|
}
|
|
754
892
|
|
|
755
893
|
async with aiohttp.ClientSession() as session:
|
|
756
894
|
try:
|
|
757
|
-
async with session.post(url, json=
|
|
895
|
+
async with session.post(url, json=node) as response:
|
|
758
896
|
response.raise_for_status()
|
|
759
897
|
data = await response.json()
|
|
760
898
|
nodeID = data["nodeID"]
|
|
@@ -782,54 +920,10 @@ async def async_update_node():
|
|
|
782
920
|
await asyncio.to_thread(Path("streams.json").write_text, json.dumps(stx, indent=4))
|
|
783
921
|
await asyncio.to_thread(Path("nodes.json").write_text, json.dumps(nodes, indent=4))
|
|
784
922
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
def disconnect_node():
|
|
790
|
-
asyncio.run(async_disconnect_node())
|
|
791
|
-
|
|
792
|
-
async def async_disconnect_node():
|
|
793
|
-
env_data = {}
|
|
794
|
-
|
|
795
|
-
try:
|
|
796
|
-
with open(".env", "r") as f:
|
|
797
|
-
for line in f:
|
|
798
|
-
key, value = line.strip().split("=")
|
|
799
|
-
env_data[key] = value
|
|
800
|
-
|
|
801
|
-
nodeID = env_data.get("NODE", "")
|
|
802
|
-
host = env_data.get("HOST", "")
|
|
803
|
-
password = env_data.get("PASSWORD", "")
|
|
804
|
-
network = env_data.get("NETWORK", "")
|
|
805
|
-
synapse = env_data.get("SYNAPSE", "")
|
|
806
|
-
|
|
807
|
-
except FileNotFoundError:
|
|
808
|
-
click.echo("Error: .env with credentials not found")
|
|
809
|
-
return
|
|
810
|
-
except Exception as e:
|
|
811
|
-
click.echo(f"Error reading .env file: {e}")
|
|
812
|
-
return
|
|
813
|
-
|
|
814
|
-
url = f"https://{network}/api/disconnect_node"
|
|
815
|
-
node_payload = {
|
|
816
|
-
"nodeID": nodeID,
|
|
817
|
-
"host": host,
|
|
818
|
-
"password": password,
|
|
819
|
-
"synapse": synapse
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
async with aiohttp.ClientSession() as session:
|
|
823
|
-
try:
|
|
824
|
-
async with session.post(url, json=node_payload) as response:
|
|
825
|
-
response.raise_for_status()
|
|
826
|
-
data = await response.json()
|
|
827
|
-
nodeID = data["nodeID"]
|
|
828
|
-
except aiohttp.ClientError as e:
|
|
829
|
-
click.echo(f"Error sending request: {e}")
|
|
830
|
-
return
|
|
831
|
-
|
|
832
|
-
click.echo(f"Neuronum Node '{nodeID}' disconnected!")
|
|
923
|
+
if node_type == "public":
|
|
924
|
+
click.echo(f"Neuronum Node '{nodeID}' updated! Visit: {node_url}")
|
|
925
|
+
else:
|
|
926
|
+
click.echo(f"Neuronum Node '{nodeID}' updated!")
|
|
833
927
|
|
|
834
928
|
|
|
835
929
|
@click.command()
|
|
@@ -1004,16 +1098,15 @@ async def async_sync(stx):
|
|
|
1004
1098
|
|
|
1005
1099
|
|
|
1006
1100
|
cli.add_command(create_cell)
|
|
1007
|
-
cli.add_command(connect_cell)
|
|
1008
1101
|
cli.add_command(view_cell)
|
|
1009
1102
|
cli.add_command(disconnect_cell)
|
|
1010
1103
|
cli.add_command(delete_cell)
|
|
1011
1104
|
cli.add_command(init_node)
|
|
1012
1105
|
cli.add_command(start_node)
|
|
1106
|
+
cli.add_command(restart_node)
|
|
1013
1107
|
cli.add_command(stop_node)
|
|
1014
|
-
cli.add_command(
|
|
1108
|
+
cli.add_command(check_node)
|
|
1015
1109
|
cli.add_command(update_node)
|
|
1016
|
-
cli.add_command(disconnect_node)
|
|
1017
1110
|
cli.add_command(delete_node)
|
|
1018
1111
|
cli.add_command(activate)
|
|
1019
1112
|
cli.add_command(load)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: neuronum
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.8.1
|
|
4
4
|
Summary: Official client library to interact with the Neuronum Network
|
|
5
5
|
Home-page: https://neuronum.net
|
|
6
6
|
Author: Neuronum Cybernetics
|
|
@@ -18,6 +18,7 @@ Requires-Dist: click
|
|
|
18
18
|
Requires-Dist: questionary
|
|
19
19
|
Requires-Dist: python-dotenv
|
|
20
20
|
Requires-Dist: requests
|
|
21
|
+
Requires-Dist: psutil
|
|
21
22
|
Dynamic: author
|
|
22
23
|
Dynamic: author-email
|
|
23
24
|
Dynamic: classifier
|
|
@@ -54,10 +55,11 @@ Dynamic: summary
|
|
|
54
55
|
------------------
|
|
55
56
|
|
|
56
57
|
### **A Getting Started into the Neuronum Network**
|
|
57
|
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
58
|
+
In this brief getting started guide, you will:
|
|
59
|
+
- [Learn about Neuronum](#about-neuronum)
|
|
60
|
+
- [Connect to the Network](#connect-to-neuronum)
|
|
61
|
+
- [Build a Neuronum Node](#build-on-neuronum)
|
|
62
|
+
- [Interact with your Node](#interact-with-neuronum)
|
|
61
63
|
|
|
62
64
|
------------------
|
|
63
65
|
|
|
@@ -101,10 +103,13 @@ neuronum connect-cell # connect Cell
|
|
|
101
103
|
------------------
|
|
102
104
|
|
|
103
105
|
|
|
104
|
-
### **Build On Neuronum**
|
|
106
|
+
### **Build On Neuronum**
|
|
107
|
+
[View Node Examples](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes)
|
|
108
|
+
|
|
109
|
+
|
|
105
110
|
Initialize a Node (app template):
|
|
106
111
|
```sh
|
|
107
|
-
neuronum init-node --app # initialize a Node with app template
|
|
112
|
+
neuronum init-node --app # initialize a Node with app template
|
|
108
113
|
```
|
|
109
114
|
|
|
110
115
|
Change into Node folder
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name='neuronum',
|
|
5
|
-
version='5.
|
|
5
|
+
version='5.8.1',
|
|
6
6
|
author='Neuronum Cybernetics',
|
|
7
7
|
author_email='welcome@neuronum.net',
|
|
8
8
|
description='Official client library to interact with the Neuronum Network',
|
|
@@ -26,6 +26,7 @@ setup(
|
|
|
26
26
|
'questionary',
|
|
27
27
|
'python-dotenv',
|
|
28
28
|
'requests',
|
|
29
|
+
'psutil',
|
|
29
30
|
],
|
|
30
31
|
entry_points={
|
|
31
32
|
"console_scripts": [
|
|
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
|