neuronum 1.5.1__py3-none-any.whl → 1.7.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.

Potentially problematic release.


This version of neuronum might be problematic. Click here for more details.

cli/__init__.py ADDED
File without changes
cli/main.py ADDED
@@ -0,0 +1,589 @@
1
+ import click
2
+ import questionary
3
+ from pathlib import Path
4
+ import requests
5
+ import subprocess
6
+ import os
7
+ import neuronum
8
+ import json
9
+
10
+ @click.group()
11
+ def cli():
12
+ """Neuronum CLI Tool"""
13
+
14
+
15
+ @click.command()
16
+ def create_cell():
17
+ cell_type = questionary.select(
18
+ "Choose Cell type:",
19
+ choices=["business", "community"]
20
+ ).ask()
21
+
22
+ network = questionary.select(
23
+ "Choose Network:",
24
+ choices=["neuronum.net"]
25
+ ).ask()
26
+
27
+ if cell_type == "business":
28
+ click.echo("Visit https://neuronum.net/createcell to create your Neuronum Business Cell")
29
+
30
+ if cell_type == "community":
31
+
32
+ email = click.prompt("Enter email")
33
+ password = click.prompt("Enter password", hide_input=True)
34
+ repeat_password = click.prompt("Repeat password", hide_input=True)
35
+
36
+ if password != repeat_password:
37
+ click.echo("Passwords do not match!")
38
+ return
39
+
40
+ url = f"https://{network}/api/create_cell/{cell_type}"
41
+
42
+ create_cell = {"email": email, "password": password}
43
+
44
+ try:
45
+ response = requests.post(url, json=create_cell)
46
+ response.raise_for_status()
47
+ status = response.json()["status"]
48
+
49
+ except requests.exceptions.RequestException as e:
50
+ click.echo(f"Error sending request: {e}")
51
+ return
52
+
53
+ if status == True:
54
+ host = response.json()["host"]
55
+ cellkey = click.prompt(f"Please verify your email address with the Cell Key send to {email}")
56
+
57
+ url = f"https://{network}/api/verify_email"
58
+
59
+ verify_email = {"host": host, "email": email, "cellkey": cellkey}
60
+
61
+ try:
62
+ response = requests.post(url, json=verify_email)
63
+ response.raise_for_status()
64
+ status = response.json()["status"]
65
+
66
+ except requests.exceptions.RequestException as e:
67
+ click.echo(f"Error sending request: {e}")
68
+ return
69
+
70
+ if status == True:
71
+ synapse = response.json()["synapse"]
72
+ credentials_folder_path = Path.home() / ".neuronum"
73
+ credentials_folder_path.mkdir(parents=True, exist_ok=True)
74
+
75
+ env_path = credentials_folder_path / ".env"
76
+ env_path.write_text(f"HOST={host}\nPASSWORD={password}\nNETWORK={network}\nSYNAPSE={synapse}\n")
77
+
78
+ click.echo(f"Welcome to Neuronum! Community Cell '{host}' created and connected!")
79
+
80
+ if status == False:
81
+ click.echo(f"Error:'{email}' already assigned!")
82
+
83
+
84
+
85
+ @click.command()
86
+ def connect_cell():
87
+ email = click.prompt("Enter your Email")
88
+ password = click.prompt("Enter password", hide_input=True)
89
+
90
+ network = questionary.select(
91
+ "Choose Network:",
92
+ choices=["neuronum.net"]
93
+ ).ask()
94
+
95
+ url = f"https://{network}/api/connect_cell"
96
+ payload = {"email": email, "password": password}
97
+
98
+ try:
99
+ response = requests.post(url, json=payload)
100
+ response.raise_for_status()
101
+ status = response.json()["status"]
102
+ host = response.json()["host"]
103
+ except requests.exceptions.RequestException as e:
104
+ click.echo(f"Error connecting: {e}")
105
+ return
106
+
107
+ if status == True:
108
+ cellkey = click.prompt(f"Please verify your email address with the Cell Key send to {email}")
109
+ url = f"https://{network}/api/verify_email"
110
+ verify_email = {"host": host, "email": email, "cellkey": cellkey}
111
+
112
+ try:
113
+ response = requests.post(url, json=verify_email)
114
+ response.raise_for_status()
115
+ status = response.json()["status"]
116
+ synapse = response.json()["synapse"]
117
+
118
+ except requests.exceptions.RequestException as e:
119
+ click.echo(f"Error sending request: {e}")
120
+ return
121
+
122
+ if status == True:
123
+ credentials_folder_path = Path.home() / ".neuronum"
124
+ credentials_folder_path.mkdir(parents=True, exist_ok=True)
125
+
126
+ env_path = credentials_folder_path / f".env"
127
+ env_path.write_text(f"HOST={host}\nPASSWORD={password}\nNETWORK={network}\nSYNAPSE={synapse}\n")
128
+
129
+ click.echo(f"Cell '{host}' connected!")
130
+ else:
131
+ click.echo(f"Connection failed!")
132
+
133
+
134
+ @click.command()
135
+ def view_cell():
136
+ credentials_folder_path = Path.home() / ".neuronum"
137
+ env_path = credentials_folder_path / ".env"
138
+
139
+ env_data = {}
140
+
141
+ try:
142
+ with open(env_path, "r") as f:
143
+ for line in f:
144
+ key, value = line.strip().split("=")
145
+ env_data[key] = value
146
+
147
+ host = env_data.get("HOST", "")
148
+
149
+ except FileNotFoundError:
150
+ click.echo("Error: No credentials found. Please connect to a cell first.")
151
+ return
152
+ except Exception as e:
153
+ click.echo(f"Error reading .env file: {e}")
154
+ return
155
+
156
+ if host:
157
+ click.echo(f"Connected Cell: '{host}'")
158
+ else:
159
+ click.echo("No active cell connection found.")
160
+
161
+
162
+ @click.command()
163
+ def disconnect_cell():
164
+ credentials_folder_path = Path.home() / ".neuronum"
165
+ env_path = credentials_folder_path / ".env"
166
+
167
+ env_data = {}
168
+
169
+ try:
170
+ with open(env_path, "r") as f:
171
+ for line in f:
172
+ key, value = line.strip().split("=")
173
+ env_data[key] = value
174
+
175
+ host = env_data.get("HOST", "")
176
+
177
+ except FileNotFoundError:
178
+ click.echo("Error: .env with credentials not found")
179
+ return
180
+ except Exception as e:
181
+ click.echo(f"Error reading .env file: {e}")
182
+ return
183
+
184
+ if env_path.exists():
185
+ if click.confirm(f"Are you sure you want to disconnect Cell '{host}'?", default=True):
186
+ os.remove(env_path)
187
+ click.echo(f"'{host}' disconnected!")
188
+ else:
189
+ click.echo("Disconnect canceled.")
190
+ else:
191
+ click.echo(f"No Neuronum Cell connected!")
192
+
193
+
194
+ @click.command()
195
+ def delete_cell():
196
+ credentials_folder_path = Path.home() / ".neuronum"
197
+ env_path = credentials_folder_path / ".env"
198
+
199
+ env_data = {}
200
+
201
+ try:
202
+ with open(env_path, "r") as f:
203
+ for line in f:
204
+ key, value = line.strip().split("=")
205
+ env_data[key] = value
206
+
207
+ host = env_data.get("HOST", "")
208
+ password = env_data.get("PASSWORD", "")
209
+ network = env_data.get("NETWORK", "")
210
+ synapse = env_data.get("SYNAPSE", "")
211
+
212
+ except FileNotFoundError:
213
+ click.echo("Error: No cell connected. Connect Cell first to delete")
214
+ return
215
+ except Exception as e:
216
+ click.echo(f"Error reading .env file: {e}")
217
+ return
218
+
219
+ confirm = click.confirm(f" Are you sure you want to delete '{host}'?", default=True)
220
+ if not confirm:
221
+ click.echo("Deletion canceled.")
222
+ return
223
+
224
+ url = f"https://{network}/api/delete_cell"
225
+ payload = {"host": host, "password": password, "synapse": synapse}
226
+
227
+ try:
228
+ response = requests.delete(url, json=payload)
229
+ response.raise_for_status()
230
+ status = response.json()["status"]
231
+ except requests.exceptions.RequestException as e:
232
+ click.echo(f"Error deleting cell: {e}")
233
+ return
234
+
235
+ if status == True:
236
+ env_path = credentials_folder_path / f"{host}.env"
237
+ if env_path.exists():
238
+ os.remove(env_path)
239
+ click.echo("Credentials deleted successfully!")
240
+ click.echo(f"Neuronum Cell '{host}' has been deleted!")
241
+ else:
242
+ click.echo(f"Neuronum Cell '{host}' deletion failed!")
243
+
244
+
245
+ @click.command()
246
+ @click.option('--sync', default=None, help="Optional stream ID to generate the sync template.")
247
+ def init_node(sync):
248
+
249
+ node_type = questionary.select(
250
+ "Choose Node type:",
251
+ choices=["public", "private"]
252
+ ).ask()
253
+
254
+ descr = click.prompt("Node description (max. 25 characters)")
255
+
256
+ stream = sync if sync else "n9gW3LxQcecI::stx"
257
+
258
+ credentials_folder_path = Path.home() / ".neuronum"
259
+ env_path = credentials_folder_path / ".env"
260
+
261
+ env_data = {}
262
+
263
+ try:
264
+ with open(env_path, "r") as f:
265
+ for line in f:
266
+ key, value = line.strip().split("=")
267
+ env_data[key] = value
268
+
269
+ host = env_data.get("HOST", "")
270
+ password = env_data.get("PASSWORD", "")
271
+ network = env_data.get("NETWORK", "")
272
+ synapse = env_data.get("SYNAPSE", "")
273
+
274
+ except FileNotFoundError:
275
+ click.echo("No cell connected. Connect your cell with command neuronum connect-cell")
276
+ return
277
+ except Exception as e:
278
+ click.echo(f"Error reading .env file: {e}")
279
+ return
280
+
281
+ cell = neuronum.Cell(
282
+ host=host,
283
+ password=password,
284
+ network=network,
285
+ synapse=synapse
286
+ )
287
+
288
+ tx = cell.list_tx()
289
+ ctx = cell.list_ctx()
290
+ stx = cell.list_stx()
291
+ contracts = cell.list_contracts()
292
+
293
+ url = f"https://{network}/api/init_node/{node_type}"
294
+
295
+ node = {"descr": descr, "host": host, "password": password, "synapse": synapse}
296
+
297
+ try:
298
+ response = requests.post(url, json=node)
299
+ response.raise_for_status()
300
+ nodeID = response.json()["nodeID"]
301
+ except requests.exceptions.RequestException as e:
302
+ click.echo(f"Error sending request: {e}")
303
+ return
304
+
305
+ node_filename = "node_" + nodeID.replace("::node", "")
306
+ project_path = Path(node_filename)
307
+ project_path.mkdir(exist_ok=True)
308
+
309
+ env_path = project_path / ".env"
310
+ env_path.write_text(f"NODE={nodeID}\nHOST={host}\nPASSWORD={password}\nNETWORK={network}\nSYNAPSE={synapse}\n")
311
+
312
+ gitignore_path = project_path / ".gitignore"
313
+ gitignore_path.write_text(f".env\n")
314
+
315
+ tx_path = project_path / "transmitters.json"
316
+ tx_path.write_text(json.dumps(tx, indent=4))
317
+
318
+ ctx_path = project_path / "circuits.json"
319
+ ctx_path.write_text(json.dumps(ctx, indent=4))
320
+
321
+ stx_path = project_path / "streams.json"
322
+ stx_path.write_text(json.dumps(stx, indent=4))
323
+
324
+ contracts_path = project_path / "contracts.json"
325
+ contracts_path.write_text(json.dumps(contracts, indent=4))
326
+
327
+ nodemd_path = project_path / "NODE.md"
328
+ nodemd_path.write_text("""\
329
+ ## Use this NODE.md file to add instructions on how to interact with your node
330
+ """)
331
+
332
+ main_path = project_path / "main.py"
333
+ main_path.write_text(f"""\
334
+ import neuronum
335
+ import os
336
+ from dotenv import load_dotenv
337
+
338
+ load_dotenv()
339
+ host = os.getenv("HOST")
340
+ password = os.getenv("PASSWORD")
341
+ network = os.getenv("NETWORK")
342
+ synapse = os.getenv("SYNAPSE")
343
+
344
+ cell = neuronum.Cell(
345
+ host=host,
346
+ password=password,
347
+ network=network,
348
+ synapse=synapse
349
+ )
350
+
351
+ STX = "{stream}"
352
+ stream = cell.sync(STX)
353
+ for operation in stream:
354
+ label = operation.get("label")
355
+ value = operation.get("data").get("message")
356
+ ts = operation.get("time")
357
+ stxID = operation.get("stxID")
358
+ operator = operation.get("operator")
359
+ print(label, value, ts, stxID, operator)
360
+ """)
361
+
362
+ click.echo(f"Neuronum Node '{nodeID}' initialized!")
363
+
364
+
365
+ @click.command()
366
+ def start_node():
367
+ click.echo("Starting Node...")
368
+
369
+ project_path = Path.cwd()
370
+ main_file = project_path / "main.py"
371
+
372
+ if not main_file.exists():
373
+ click.echo("Error: main.py not found. Make sure the node is set up.")
374
+ return
375
+
376
+ process = subprocess.Popen(["python", str(main_file)], start_new_session=True)
377
+
378
+ with open("node_pid.txt", "w") as f:
379
+ f.write(str(process.pid))
380
+
381
+ click.echo("Node started successfully!")
382
+
383
+
384
+ @click.command()
385
+ def stop_node():
386
+ click.echo("Stopping Node...")
387
+
388
+ try:
389
+ with open("node_pid.txt", "r") as f:
390
+ pid = int(f.read().strip())
391
+ os.kill(pid, 9)
392
+ os.remove("node_pid.txt")
393
+ click.echo("Node stopped successfully!")
394
+ except FileNotFoundError:
395
+ click.echo("Error: No active node process found.")
396
+ except Exception as e:
397
+ click.echo(f"Error stopping node: {e}")
398
+
399
+
400
+ @click.command()
401
+ def register_node():
402
+ env_data = {}
403
+ try:
404
+ with open(".env", "r") as f:
405
+ for line in f:
406
+ key, value = line.strip().split("=")
407
+ env_data[key] = value
408
+
409
+ nodeID = env_data.get("NODE", "")
410
+ host = env_data.get("HOST", "")
411
+ password = env_data.get("PASSWORD", "")
412
+ network = env_data.get("NETWORK", "")
413
+ synapse = env_data.get("SYNAPSE", "")
414
+
415
+ except FileNotFoundError:
416
+ print("Error: .env with credentials not found")
417
+ return
418
+ except Exception as e:
419
+ print(f"Error reading .env file: {e}")
420
+ return
421
+
422
+ try:
423
+ with open("NODE.md", "r") as f:
424
+ nodemd_file = f.read()
425
+
426
+ except FileNotFoundError:
427
+ print("Error: NODE.md file not found")
428
+ return
429
+ except Exception as e:
430
+ print(f"Error reading NODE.md file: {e}")
431
+ return
432
+
433
+ url = f"https://{network}/api/register_node"
434
+
435
+ node = {
436
+ "nodeID": nodeID,
437
+ "host": host,
438
+ "password": password,
439
+ "synapse": synapse,
440
+ "nodemd_file": nodemd_file
441
+ }
442
+
443
+ try:
444
+ response = requests.post(url, json=node)
445
+ response.raise_for_status()
446
+ nodeID = response.json()["nodeID"]
447
+ node_url = response.json()["node_url"]
448
+ except requests.exceptions.RequestException as e:
449
+ click.echo(f"Error sending request: {e}")
450
+ return
451
+
452
+ click.echo(f"Neuronum Node '{nodeID}' registered! Visit: {node_url}")
453
+
454
+
455
+ @click.command()
456
+ def update_node():
457
+ env_data = {}
458
+ try:
459
+ with open(".env", "r") as f:
460
+ for line in f:
461
+ key, value = line.strip().split("=")
462
+ env_data[key] = value
463
+
464
+ nodeID = env_data.get("NODE", "")
465
+ host = env_data.get("HOST", "")
466
+ password = env_data.get("PASSWORD", "")
467
+ network = env_data.get("NETWORK", "")
468
+ synapse = env_data.get("SYNAPSE", "")
469
+
470
+ except FileNotFoundError:
471
+ print("Error: .env with credentials not found")
472
+ return
473
+ except Exception as e:
474
+ print(f"Error reading .env file: {e}")
475
+ return
476
+
477
+ try:
478
+ with open("NODE.md", "r") as f:
479
+ nodemd_file = f.read()
480
+
481
+ except FileNotFoundError:
482
+ print("Error: NODE.md file not found")
483
+ return
484
+ except Exception as e:
485
+ print(f"Error reading NODE.md file: {e}")
486
+ return
487
+
488
+ url = f"https://{network}/api/update_node"
489
+
490
+ node = {
491
+ "nodeID": nodeID,
492
+ "host": host,
493
+ "password": password,
494
+ "synapse": synapse,
495
+ "nodemd_file": nodemd_file
496
+ }
497
+
498
+ try:
499
+ response = requests.post(url, json=node)
500
+ response.raise_for_status()
501
+ nodeID = response.json()["nodeID"]
502
+ node_url = response.json()["node_url"]
503
+ except requests.exceptions.RequestException as e:
504
+ click.echo(f"Error sending request: {e}")
505
+ return
506
+
507
+
508
+ cell = neuronum.Cell(
509
+ host=host,
510
+ password=password,
511
+ network=network,
512
+ synapse=synapse
513
+ )
514
+
515
+ tx = cell.list_tx()
516
+ ctx = cell.list_ctx()
517
+ stx = cell.list_stx()
518
+ contracts = cell.list_contracts()
519
+
520
+ tx_path = Path("transmitters.json")
521
+ ctx_path = Path("circuits.json")
522
+ stx_path = Path("streams.json")
523
+ contracts_path = Path("contracts.json")
524
+
525
+ tx_path.write_text(json.dumps(tx, indent=4))
526
+ ctx_path.write_text(json.dumps(ctx, indent=4))
527
+ stx_path.write_text(json.dumps(stx, indent=4))
528
+ contracts_path.write_text(json.dumps(contracts, indent=4))
529
+
530
+ click.echo(f"Neuronum Node '{nodeID}' updated! Visit: {node_url}")
531
+
532
+
533
+ @click.command()
534
+ def delete_node():
535
+ env_data = {}
536
+ try:
537
+ with open(".env", "r") as f:
538
+ for line in f:
539
+ key, value = line.strip().split("=")
540
+ env_data[key] = value
541
+
542
+ nodeID = env_data.get("NODE", "")
543
+ host = env_data.get("HOST", "")
544
+ password = env_data.get("PASSWORD", "")
545
+ network = env_data.get("NETWORK", "")
546
+ synapse = env_data.get("SYNAPSE", "")
547
+
548
+ except FileNotFoundError:
549
+ print("Error: .env with credentials not found")
550
+ return
551
+ except Exception as e:
552
+ print(f"Error reading .env file: {e}")
553
+ return
554
+
555
+ url = f"https://{network}/api/delete_node"
556
+
557
+ node = {
558
+ "nodeID": nodeID,
559
+ "host": host,
560
+ "password": password,
561
+ "synapse": synapse
562
+ }
563
+
564
+ try:
565
+ response = requests.post(url, json=node)
566
+ response.raise_for_status()
567
+ nodeID = response.json()["nodeID"]
568
+ except requests.exceptions.RequestException as e:
569
+ click.echo(f"Error sending request: {e}")
570
+ return
571
+
572
+ click.echo(f"Neuronum Node '{nodeID}' deleted!")
573
+
574
+
575
+ cli.add_command(create_cell)
576
+ cli.add_command(connect_cell)
577
+ cli.add_command(view_cell)
578
+ cli.add_command(disconnect_cell)
579
+ cli.add_command(delete_cell)
580
+ cli.add_command(init_node)
581
+ cli.add_command(start_node)
582
+ cli.add_command(stop_node)
583
+ cli.add_command(register_node)
584
+ cli.add_command(update_node)
585
+ cli.add_command(delete_node)
586
+
587
+
588
+ if __name__ == "__main__":
589
+ cli()
neuronum/neuronum.py CHANGED
@@ -206,130 +206,56 @@ class Cell:
206
206
  print(f"Unexpected error: {e}")
207
207
 
208
208
 
209
- def register_node(self, descr: str, mode: str, stx: str):
210
- if mode == "public":
211
- url = f"https://{self.network}/api/register_node/public"
212
- elif mode == "private":
213
- url = f"https://{self.network}/api/register_node/private"
214
- else:
215
- return {"error": "Invalid mode", "message": "Mode has to be 'public' or 'private'"}
216
-
217
- node_data = {
218
- "descr": descr,
219
- "mode": mode,
220
- "stream": stx,
221
- "cell": self.to_dict()
222
- }
223
-
224
- try:
225
- response = requests.post(
226
- url,
227
- json=node_data,
228
- )
229
-
230
- response.raise_for_status()
231
-
232
- return response.json()["nodeID"]
233
-
234
- except requests.exceptions.RequestException as e:
235
- print(f"Error sending request: {e}")
236
- except Exception as e:
237
- print(f"Unexpected error: {e}")
238
-
239
-
240
- def delete_node(self, nodeID: str):
241
- url = f"https://{self.network}/api/delete_node"
242
-
243
- delete_node = {
244
- "nodeID": nodeID,
245
- "cell": self.to_dict()
246
- }
247
-
248
- try:
249
- response = requests.post(
250
- url,
251
- json=delete_node,
252
- )
253
-
254
- response.raise_for_status()
255
- print(response.json())
256
-
257
- except requests.exceptions.RequestException as e:
258
- print(f"Error sending request: {e}")
259
- except Exception as e:
260
- print(f"Unexpected error: {e}")
261
-
262
-
263
- def list_tx(self, cellID: str):
209
+ def list_tx(self):
264
210
  full_url = f"https://{self.network}/api/list_tx"
265
211
 
266
212
  list_tx = {
267
- "cell": self.to_dict(),
268
- "cellID": cellID
213
+ "cell": self.to_dict()
269
214
  }
270
215
 
271
216
  try:
272
217
  response = requests.post(full_url, json=list_tx)
273
218
  response.raise_for_status()
274
- return response.json()
219
+ return response.json()["Transmitters"]
275
220
  except requests.exceptions.RequestException as e:
276
221
  print(f"Error sending request: {e}")
277
222
  except Exception as e:
278
223
  print(f"Unexpected error: {e}")
279
224
 
280
225
 
281
- def list_ctx(self, cellID: str):
226
+ def list_ctx(self):
282
227
  full_url = f"https://{self.network}/api/list_ctx"
283
228
 
284
229
  list_ctx = {
285
- "cell": self.to_dict(),
286
- "cellID": cellID
230
+ "cell": self.to_dict()
287
231
  }
288
232
 
289
233
  try:
290
234
  response = requests.post(full_url, json=list_ctx)
291
235
  response.raise_for_status()
292
- return response.json()
236
+ return response.json()["Circuits"]
293
237
  except requests.exceptions.RequestException as e:
294
238
  print(f"Error sending request: {e}")
295
239
  except Exception as e:
296
240
  print(f"Unexpected error: {e}")
297
241
 
298
242
 
299
- def list_stx(self, cellID: str):
243
+ def list_stx(self):
300
244
  full_url = f"https://{self.network}/api/list_stx"
301
245
 
302
246
  list_stx = {
303
- "cell": self.to_dict(),
304
- "cellID": cellID
247
+ "cell": self.to_dict()
305
248
  }
306
249
 
307
250
  try:
308
251
  response = requests.post(full_url, json=list_stx)
309
252
  response.raise_for_status()
310
- return response.json()
253
+ return response.json()["Streams"]
311
254
  except requests.exceptions.RequestException as e:
312
255
  print(f"Error sending request: {e}")
313
256
  except Exception as e:
314
257
  print(f"Unexpected error: {e}")
315
258
 
316
-
317
- def connect(self):
318
- url = f"https://{self.network}/api/connect"
319
-
320
- test = {
321
- "cell": self.to_dict()
322
- }
323
-
324
- try:
325
- response = requests.post(url, json=test)
326
- response.raise_for_status()
327
- print(response.json()["connection"])
328
- except requests.exceptions.RequestException as e:
329
- print(f"Error sending request: {e}")
330
- except Exception as e:
331
- print(f"Unexpected error: {e}")
332
-
333
259
 
334
260
  def store(self, label: str, data: dict, ctx: Optional[str] = None):
335
261
  if ctx:
@@ -459,14 +385,13 @@ class Cell:
459
385
  try:
460
386
  ws = create_connection(f"wss://{self.network}/sync/{stx}")
461
387
  ws.send(json.dumps(auth))
462
- print("Stream connection set...")
388
+ print("Listening to Stream...")
463
389
 
464
390
  try:
465
391
  while True:
466
392
  try:
467
393
  raw_operation = ws.recv()
468
394
  operation = json.loads(raw_operation)
469
- print("Listening to Stream...")
470
395
  yield operation
471
396
 
472
397
  except socket.timeout:
@@ -474,17 +399,147 @@ class Cell:
474
399
  continue
475
400
 
476
401
  except KeyboardInterrupt:
477
- print("Stream-Synchronization ended!")
478
- except Exception as e:
479
- print(f"Error: {e}")
480
- finally:
481
402
  ws.close()
482
403
  print("Connection closed.")
483
-
404
+ except Exception as e:
405
+ print(f"Error: {e}")
406
+
407
+
484
408
  except KeyboardInterrupt:
485
409
  print("Stream-Synchronization ended!")
486
410
  ws.close()
487
411
  print("Connection closed. Goodbye!")
488
412
 
489
413
 
414
+ def sign_contract(self, contractID: str):
415
+ full_url = f"https://{self.network}/api/sign_contract"
416
+
417
+ sign_contract = {
418
+ "contractID": contractID,
419
+ "cell": self.to_dict()
420
+ }
421
+
422
+ try:
423
+ response = requests.post(full_url, json=sign_contract)
424
+ response.raise_for_status()
425
+ return response.json()["token"]
426
+ except requests.exceptions.RequestException as e:
427
+ print(f"Error sending request: {e}")
428
+ except Exception as e:
429
+ print(f"Unexpected error: {e}")
430
+
431
+
432
+ def validate_token(self, token: str, cp: str, contractID: str):
433
+ full_url = f"https://{self.network}/api/validate_token"
434
+
435
+ validate = {
436
+ "token": token,
437
+ "cp": cp,
438
+ "contractID": contractID,
439
+ "cell": self.to_dict()
440
+ }
441
+
442
+ try:
443
+ response = requests.post(full_url, json=validate)
444
+ response.raise_for_status()
445
+ return response.json()["validity"]
446
+ except requests.exceptions.RequestException as e:
447
+ print(f"Error sending request: {e}")
448
+ except Exception as e:
449
+ print(f"Unexpected error: {e}")
450
+
451
+
452
+ def request_token(self, cp: str, contractID: str):
453
+ full_url = f"https://{self.network}/api/request_token"
454
+
455
+ request_token = {
456
+ "cp": cp,
457
+ "contractID": contractID,
458
+ "cell": self.to_dict()
459
+ }
460
+
461
+ try:
462
+ response = requests.post(full_url, json=request_token)
463
+ response.raise_for_status()
464
+ print(response.json())
465
+ except requests.exceptions.RequestException as e:
466
+ print(f"Error sending request: {e}")
467
+ except Exception as e:
468
+ print(f"Unexpected error: {e}")
469
+
470
+
471
+ def present_token(self, token: str, cp: str, contractID: str):
472
+ full_url = f"https://{self.network}/api/present_token"
473
+
474
+ present_token = {
475
+ "token": token,
476
+ "cp": cp,
477
+ "contractID": contractID,
478
+ "cell": self.to_dict()
479
+ }
480
+
481
+ try:
482
+ response = requests.post(full_url, json=present_token)
483
+ response.raise_for_status()
484
+ print(response.json())
485
+ except requests.exceptions.RequestException as e:
486
+ print(f"Error sending request: {e}")
487
+ except Exception as e:
488
+ print(f"Unexpected error: {e}")
489
+
490
+
491
+ def create_contract(self, descr: str, details: dict, partners: list):
492
+ full_url = f"https://{self.network}/api/create_contract"
493
+
494
+ create_contract = {
495
+ "cell": self.to_dict(),
496
+ "descr": descr,
497
+ "details": details,
498
+ "partners": partners
499
+ }
500
+ try:
501
+ response = requests.post(full_url, json=create_contract)
502
+ response.raise_for_status()
503
+ return response.json()["contractID"]
504
+ except requests.exceptions.RequestException as e:
505
+ print(f"Error sending request: {e}")
506
+ except Exception as e:
507
+ print(f"Unexpected error: {e}")
508
+
509
+
510
+ def delete_contract(self, contractID: str):
511
+ full_url = f"https://{self.network}/api/delete_contract"
512
+
513
+ request = {
514
+ "cell": self.to_dict(),
515
+ "contractID": contractID
516
+ }
517
+
518
+ try:
519
+ response = requests.post(full_url, json=request)
520
+ response.raise_for_status()
521
+ print(f"Response from Neuronum: {response.json()}")
522
+ except requests.exceptions.RequestException as e:
523
+ print(f"Error sending request: {e}")
524
+ except Exception as e:
525
+ print(f"Unexpected error: {e}")
526
+
527
+
528
+ def list_contracts(self):
529
+ full_url = f"https://{self.network}/api/list_contracts"
530
+
531
+ list_contracts = {
532
+ "cell": self.to_dict()
533
+ }
534
+
535
+ try:
536
+ response = requests.post(full_url, json=list_contracts)
537
+ response.raise_for_status()
538
+ return response.json()["Contracts"]
539
+ except requests.exceptions.RequestException as e:
540
+ print(f"Error sending request: {e}")
541
+ except Exception as e:
542
+ print(f"Unexpected error: {e}")
543
+
544
+
490
545
  __all__ = ['Cell']
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: neuronum
3
- Version: 1.5.1
4
- Summary: Interact with the Neuronum Network to build & automate interconnected networks of soft- and hardware components
3
+ Version: 1.7.0
4
+ Summary: Official client library to interact with the Neuronum Network
5
5
  Home-page: https://neuronum.net
6
6
  Author: Neuronum Cybernetics
7
7
  Author-email: welcome@neuronum.net
@@ -9,52 +9,98 @@ Project-URL: GitHub, https://github.com/neuronumcybernetics/neuronum
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Operating System :: OS Independent
12
- Requires-Python: >=3.6
12
+ Requires-Python: >=3.8
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
  Requires-Dist: requests
16
16
  Requires-Dist: websocket-client
17
+ Requires-Dist: click
18
+ Requires-Dist: questionary
19
+ Requires-Dist: python-dotenv
17
20
 
18
21
  ![Neuronum Logo](https://neuronum.net/static/logo_pip.png "Neuronum")
19
22
 
20
- [![Website](https://img.shields.io/badge/Website-Neuronum-blue)](https://neuronum.net)
21
- [![Documentation](https://img.shields.io/badge/Docs-Read%20now-green)](https://github.com/neuronumcybernetics/neuronum)
22
- [![Tutorials](https://img.shields.io/badge/Tutorials-Watch%20now-red)](https://www.youtube.com/@neuronumnet)
23
+ [![Website](https://img.shields.io/badge/Website-Neuronum-blue)](https://neuronum.net) [![Documentation](https://img.shields.io/badge/Docs-Read%20now-green)](https://github.com/neuronumcybernetics/neuronum)
23
24
 
24
- `Neuronum` is a cybernetic framework enabling businesses to build & automate interconnected networks of soft- and hardware components
25
+ Build, deploy and automate IoT connectivity with `Neuronum`
25
26
 
26
27
  ## Features
27
- - **Cell**: Identity to connect and interact with the Neuronum Network
28
+ - **Cells/Cell-CLI**: Create and manage Neuronum Cells from the command line
29
+ - **Nodes/Node-CLI**: Setup and manage Neuronum Nodes from the command line
28
30
  - **Transmitters (TX)**: Automate economic data transfer
29
31
  - **Circuits (CTX)**: Store data in Key-Value-Label databases
30
32
  - **Streams (STX)**: Stream, synchronize and control data in real time
31
- - **Nodes**: Soft-/Hardware components participating in the Network ecosystem
33
+ - **Contracts/Tokens**: Automate services exchange and authorization between Cells and Nodes
32
34
 
33
- To interact with the Network you will need to create a Neuronum Cell.
34
- Create your Cell: [Create Cell](https://neuronum.net/createcell)
35
+ ### Installation
36
+ Install the Neuronum library using pip:
37
+ ```sh
38
+ $ pip install neuronum
39
+ ```
35
40
 
41
+ ### Cells/Cell-CLI
42
+ To interact with the Neuronum Network, you must first create a Neuronum Cell
36
43
 
37
- ### Installation & Connection
38
- Start with installing the Neuronum library using pip:
39
- ```python
40
- pip install neuronum
44
+ Create Cell:
45
+ ```sh
46
+ $ neuronum create-cell
41
47
  ```
42
48
 
43
- Configure and test Cell connection:
44
- ```python
45
- import neuronum
49
+ Connect Cell:
50
+ ```sh
51
+ $ neuronum connect-cell
52
+ ```
53
+
54
+ View connected Cell:
55
+ ```sh
56
+ $ neuronum view-cell
57
+ ```
58
+
59
+ Disconnect Cell:
60
+ ```sh
61
+ $ neuronum disconnect-cell
62
+ ```
63
+
64
+ Delete Cell:
65
+ ```sh
66
+ $ neuronum delete-cell
67
+ ```
68
+
69
+ ### Nodes/Node-CLI
70
+ Neuronum Nodes are soft- and hardware components that power the Neuronum Network, enabling seamless communication between Nodes and Cells
71
+
72
+ Initialize a Node:
73
+ ```sh
74
+ $ neuronum init-node # neuronum init-node --sync id::stx (optional)
75
+ ```
76
+
77
+ Start a Node:
78
+ ```sh
79
+ $ neuronum start-node
80
+ ```
81
+
82
+ Stop a Node:
83
+ ```sh
84
+ $ neuronum stop-node
85
+ ```
46
86
 
47
- cell = neuronum.Cell(
48
- host="host::cell", # cell host
49
- password="your_password", # cell password
50
- network="neuronum.net", # cell network
51
- synapse="your_synapse" # cell synapse
52
- )
53
- cell.connect() # connect to network
87
+ Register a Node on the Neuronum Network:
88
+ ```sh
89
+ $ neuronum register-node
90
+ ```
91
+
92
+ Update a Node:
93
+ ```sh
94
+ $ neuronum update-node
95
+ ```
96
+
97
+ Delete a Node:
98
+ ```sh
99
+ $ neuronum delete-node
54
100
  ```
55
101
 
56
102
  ### Transmitters (TX)
57
- Transmitters (TX) are used to create predefined templates to receive and send data in a standardized format.
103
+ Transmitters (TX) are used to create predefined templates to receive and send data in a standardized format
58
104
 
59
105
  Create Transmitter (TX):
60
106
  ```python
@@ -87,10 +133,9 @@ TX = "id::tx" # select Trans
87
133
  cell.delete_tx(TX) # delete TX
88
134
  ```
89
135
 
90
- List Transmitter (TX) from Cell:
91
- ```python
92
- cellID = "id::cell" # select Cell
93
- txList = cell.list_tx(cellID) # list Transmitters (TX)
136
+ List Transmitter (TX) your Cell can activate:
137
+ ```python
138
+ txList = cell.list_tx() # list Transmitters (TX)
94
139
  ```
95
140
 
96
141
  ### Circuits (CTX)
@@ -177,10 +222,9 @@ CTX = "id::ctx" # select Circu
177
222
  cell.delete_ctx(CTX) # delete CTX
178
223
  ```
179
224
 
180
- List Circuits (CTX) from Cell:
181
- ```python
182
- cellID = "id::cell" # select Cell
183
- ctxList = cell.list_ctx(cellID) # list Circuits (CTX)
225
+ List Circuits (CTX) your Cell can interact with:
226
+ ```python
227
+ ctxList = cell.list_ctx() # list Circuits (CTX)
184
228
  ```
185
229
 
186
230
  ### Streams (STX)
@@ -243,25 +287,56 @@ for operation in stream: # load stream
243
287
  operator = operation.get("operator")
244
288
  ```
245
289
 
246
- List Streams (STX) from Cell:
247
- ```python
248
- cellID = "id::cell" # select Cell
249
- stxList = cell.list_stx(cellID) # list Streams (STX)
290
+ List Streams (STX) your Cell can interact with:
291
+ ```python
292
+ stxList = cell.list_stx() # list Streams (STX)
250
293
  ```
251
294
 
252
- ### Nodes
253
- Neuronum Nodes are computing hardware running the Neuronum Client Library, enabling seamless data transmission, synchronization, and facilitating public Stream (STX) access
295
+ ### Contracts/Tokens
296
+ Contracts are predefined token-based rules to automate service exchange and authorization between Cells and Nodes
254
297
 
255
- Register a Node with its associated Stream (STX):
298
+ Create a Contract:
256
299
  ```python
257
- descr = "node_name" # description (max 25 characters)
258
- mode = "public" # "public" or "private" Node
259
- STX = "id::stx" # select Stream (STX)
260
- nodeID = cell.register_node(descr, mode, STX) # register Node
300
+ descr = "Test Contract" # short description (max 25 characters)
301
+ details = { # define token details
302
+ "price_in_eur": False, # token price in EUR (int, float or False)
303
+ "max_usage": False, # max number of uses (int or False)
304
+ "validity_in_min": False # token expiration time in min (int, float or False)
305
+ }
306
+ partners = ["id::cell", "id::cell"]
307
+ contractID = cell.create_contract(descr, details, partners)
261
308
  ```
262
309
 
263
- Delete Node:
264
- ```python
265
- nodeID = "id::node" # select Node
266
- cell.delete_node(nodeID) # delete Node
310
+ Sign a Contract:
311
+ ```python
312
+ contractID = "id::contract" # select contract
313
+ token = cell.sign_contract(contractID)
314
+ ```
315
+
316
+ Request a Token from another Cell to authorize a service:
317
+ ```python
318
+ cp = "id::cell" # select counterparty cell
319
+ contractID = "id::contract" # select contract
320
+ cell.request_token(cp, contractID)
321
+ ```
322
+
323
+ Present a Token to another Cell to authorize a service:
324
+ ```python
325
+ token = "token" # select token
326
+ cp = "id::cell" # select counterparty cell
327
+ contractID = "id::contract" # select the contract
328
+ cell.present_token(token, cp, contractID)
329
+ ```
330
+
331
+ Validate a Token to authorize a service:
332
+ ```python
333
+ token = "token" # select token
334
+ cp = "id::cell" # select counterparty cell
335
+ contractID = "id::contract" # select contract
336
+ cell.validate_token(token, cp, contractID)
337
+ ```
338
+
339
+ List Contracts your Cell can interact with:
340
+ ```python
341
+ contractList = cell.list_contracts()
267
342
  ```
@@ -0,0 +1,10 @@
1
+ cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ cli/main.py,sha256=w4pNMA7ySCKTk4Z-Mb3W7cIw5ZUpLrP92L2HfrcFxSM,17637
3
+ neuronum/__init__.py,sha256=Drsm263_w3_VWgl1YsKLUr8WwVodqV3TSjqpxLjyq_M,46
4
+ neuronum/neuronum.py,sha256=4j3fV7VHPb1KcQucOEDBQ-v2SoG9c2WfGPqMfI3OF88,16498
5
+ neuronum-1.7.0.dist-info/LICENSE,sha256=UiZjNHiCyRP6WoZfbYQh9cv4JW96wIofKXmzBJrYSUk,1125
6
+ neuronum-1.7.0.dist-info/METADATA,sha256=GqgpO0lCzLKJwOfNRH8GzXPbRgsvRvZm3YP5FPVMbNs,12806
7
+ neuronum-1.7.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
8
+ neuronum-1.7.0.dist-info/entry_points.txt,sha256=XKYBcRNxGeJpZZkDPsa8HA_RaJ7Km_R_JaUq5T9Nk2U,42
9
+ neuronum-1.7.0.dist-info/top_level.txt,sha256=ru8Fr84cHm6oHr_DcJ8-uaq3RTiuCRFIr6AC8V0zPu4,13
10
+ neuronum-1.7.0.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ neuronum = cli.main:cli
@@ -1,7 +0,0 @@
1
- neuronum/__init__.py,sha256=Drsm263_w3_VWgl1YsKLUr8WwVodqV3TSjqpxLjyq_M,46
2
- neuronum/neuronum.py,sha256=AL0zXiULx-R-Co4XyYtL0lF-z7GGqXGXDxQEYgithPY,14363
3
- neuronum-1.5.1.dist-info/LICENSE,sha256=UiZjNHiCyRP6WoZfbYQh9cv4JW96wIofKXmzBJrYSUk,1125
4
- neuronum-1.5.1.dist-info/METADATA,sha256=H3UZ6u7BHgUcngCTf_0xDwcHjIR1EBlazKe38rXkqnc,11212
5
- neuronum-1.5.1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
6
- neuronum-1.5.1.dist-info/top_level.txt,sha256=73zXVVO9UTTiwEcSaXytsJ8n0q47OCwAqPlIh-hzWJU,9
7
- neuronum-1.5.1.dist-info/RECORD,,