neuronum 7.0.4__py3-none-any.whl → 8.1.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/main.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import subprocess
2
2
  import os
3
- import neuronum
4
3
  import platform
5
4
  import glob
6
5
  import asyncio
@@ -12,7 +11,9 @@ import requests
12
11
  import psutil
13
12
  from datetime import datetime
14
13
  import sys
15
-
14
+ import json
15
+ from cryptography.hazmat.primitives.asymmetric import ec
16
+ from cryptography.hazmat.primitives import serialization
16
17
 
17
18
  @click.group()
18
19
  def cli():
@@ -223,6 +224,7 @@ def delete_cell():
223
224
  return
224
225
 
225
226
  confirm = click.confirm(f" Are you sure you want to delete '{host}'?", default=True)
227
+ os.remove(env_path)
226
228
  if not confirm:
227
229
  click.echo("Deletion canceled.")
228
230
  return
@@ -249,15 +251,14 @@ def delete_cell():
249
251
 
250
252
 
251
253
  @click.command()
252
- @click.option('--blank', is_flag=True, help="Generate a Node Template without Stream and Transmitter")
253
- def init_node(blank):
254
+ def init_node():
254
255
  descr = click.prompt("Node description: Type up to 25 characters").strip()
255
256
  if descr and len(descr) > 25:
256
257
  click.echo("Description too long. Max 25 characters allowed.")
257
258
  return
258
- asyncio.run(async_init_node(blank, descr))
259
+ asyncio.run(async_init_node(descr))
259
260
 
260
- async def async_init_node(blank, descr):
261
+ async def async_init_node(descr):
261
262
  credentials_folder_path = Path.home() / ".neuronum"
262
263
  env_path = credentials_folder_path / ".env"
263
264
 
@@ -269,18 +270,12 @@ async def async_init_node(blank, descr):
269
270
  key, value = line.strip().split("=")
270
271
  env_data[key] = value
271
272
 
273
+
272
274
  host = env_data.get("HOST", "")
273
275
  password = env_data.get("PASSWORD", "")
274
276
  network = env_data.get("NETWORK", "")
275
277
  synapse = env_data.get("SYNAPSE", "")
276
278
 
277
- cell = neuronum.Cell(
278
- host=host,
279
- password=password,
280
- network=network,
281
- synapse=synapse
282
- )
283
-
284
279
  except FileNotFoundError:
285
280
  click.echo("No cell connected. Connect your cell with command neuronum connect-cell")
286
281
  return
@@ -301,431 +296,90 @@ async def async_init_node(blank, descr):
301
296
  async with session.post(url, json=node) as response:
302
297
  response.raise_for_status()
303
298
  data = await response.json()
304
- nodeID = data["nodeID"]
299
+ node_id = data["nodeID"]
305
300
  except aiohttp.ClientError as e:
306
301
  click.echo(f"Error sending request: {e}")
307
302
  return
308
303
 
309
- node_filename = descr + "_" + nodeID.replace("::node", "")
304
+ node_filename = descr + "_" + node_id.replace("::node", "")
310
305
  project_path = Path(node_filename)
311
306
  project_path.mkdir(exist_ok=True)
312
307
 
313
- env_path = project_path / ".env"
314
- await asyncio.to_thread(env_path.write_text, f"NODE={nodeID}\nHOST={host}\nPASSWORD={password}\nNETWORK={network}\nSYNAPSE={synapse}\n")
315
-
316
- if blank is False:
317
- stx_descr = f"{nodeID} App"
318
- partners = ["private"]
319
- stxID = await cell.create_stx(stx_descr, partners)
320
-
321
- tx_descr = f"Greet {nodeID}"
322
- key_values = {
323
- "ping": "pong",
324
- }
325
- STX = stxID
326
- label = "ping:pong"
327
- partners = ["private"]
328
- txID = await cell.create_tx(tx_descr, key_values, STX, label, partners)
329
-
330
- app_path = project_path / "app.py"
331
- app_path.write_text(f"""\
332
- import asyncio
333
- import neuronum
334
- import os
335
- import json
336
- from dotenv import load_dotenv
337
- from jinja2 import Environment, FileSystemLoader
338
-
339
- env = Environment(loader=FileSystemLoader('.'))
340
- template = env.get_template('ping.html')
341
-
342
- with open('config.json', 'r') as f:
343
- data = json.load(f)
344
- terms_url = data['legals']['terms']
345
- privacy_url = data['legals']['privacy_policy']
346
- last_update = data['legals']['last_update']
347
-
348
-
349
- load_dotenv()
350
- host = os.getenv("HOST")
351
- password = os.getenv("PASSWORD")
352
- network = os.getenv("NETWORK")
353
- synapse = os.getenv("SYNAPSE")
354
-
355
- cell = neuronum.Cell(
356
- host=host,
357
- password=password,
358
- network=network,
359
- synapse=synapse
360
- )
361
-
362
- async def main():
363
- STX = "{stxID}"
364
- async for operation in cell.sync(STX):
365
- txID = operation.get("txID")
366
- client = operation.get("operator")
367
- ts = operation.get("time")
368
- data = operation.get("data")
369
- operation_id = operation.get("operationID")
370
-
371
- if txID == "{txID}":
372
-
373
- def render_html_template(client, ts, data, operation_id, terms_url, privacy_url, last_update):
374
- return template.render(client=client, ts=ts, data=data, operation_id=operation_id, terms_url=terms_url, privacy_url=privacy_url, last_update=last_update)
375
-
376
- html_content = render_html_template(client, ts, data, operation_id, terms_url, privacy_url, last_update)
377
-
378
- data = {{
379
- "json": f"{{operation_id}} - Reply from {nodeID}: Pinged by {{client}} at {{ts}} with data: {{data}}",
380
- "html": html_content
381
- }}
382
-
383
- await cell.notify(f"{{client}}", "{nodeID} Ping","Pinged successfully")
384
-
385
- await cell.tx_response(txID, client, data)
386
-
387
- asyncio.run(main())
388
- """)
389
-
390
- html_path = project_path / "ping.html"
391
- html_content = f"""\
392
- <!DOCTYPE html>
393
- <html>
394
- <head>
395
- <style>
396
- body {{
397
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
398
- background-color: #121212;
399
- color: #e0e0e0;
400
- margin: 0;
401
- padding: 0;
402
- display: flex;
403
- justify-content: center;
404
- align-items: center;
405
- min-height: 100vh;
406
- }}
407
-
408
- .container {{
409
- background-color: #1e1e1e;
410
- border-radius: 12px;
411
- padding: 40px;
412
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
413
- width: 100%;
414
- max-width: 500px;
415
- text-align: center;
416
- box-sizing: border-box;
417
- }}
418
-
419
- .logo {{
420
- width: 80px;
421
- margin-bottom: 25px;
422
- filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.1));
423
- }}
424
-
425
- h1 {{
426
- font-size: 1.5em;
427
- font-weight: 600;
428
- margin-bottom: 5px;
429
- color: #f5f5f5;
430
- }}
431
-
432
- .subtitle {{
433
- font-size: 0.9em;
434
- color: #a0a0a0;
435
- margin-bottom: 30px;
436
- }}
437
-
438
- .data-row {{
439
- background-color: #2a2a2a;
440
- padding: 12px 15px;
441
- border-radius: 8px;
442
- margin-bottom: 10px;
443
- display: flex;
444
- justify-content: space-between;
445
- align-items: center;
446
- }}
447
-
448
- .data-label {{
449
- font-weight: 400;
450
- color: #a0a0a0;
451
- margin: 0;
452
- }}
453
-
454
- .data-value {{
455
- font-weight: 500;
456
- color: #e0e0e0;
457
- margin: 0;
458
- }}
459
-
460
- .data-value.truncated {{
461
- white-space: nowrap;
462
- overflow: hidden;
463
- text-overflow: ellipsis;
464
- max-width: 60%;
465
- }}
466
-
467
- .data-value.client {{
468
- color: #8cafff;
469
- }}
470
- .data-value.timestamp {{
471
- color: #a1e8a1;
472
- }}
473
- .data-value.operation-id {{
474
- color: #f7a2a2;
475
- }}
476
- .api-button {{
477
- background: #01c07d 100%;
478
- color: white;
479
- border: none;
480
- border-radius: 8px;
481
- padding: 12px 24px;
482
- font-size: 16px;
483
- font-weight: bold;
484
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
485
- cursor: pointer;
486
- margin-top: 10px;
487
- }}
488
- </style>
489
- </head>
490
- <body>
491
- <div class="container">
492
- <img class="logo" src="https://neuronum.net/static/logo.png" alt="Neuronum Logo">
493
-
494
- <h1>Reply from {nodeID}</h1>
495
- <p class="subtitle">Pinged successfully.</p>
496
-
497
- <div class="data-row">
498
- <p class="data-label">Client</p>
499
- <p class="data-value client">{{{{client}}}}</p>
500
- </div>
501
-
502
- <div class="data-row">
503
- <p class="data-label">Timestamp</p>
504
- <p class="data-value timestamp">{{{{ts}}}}</p>
505
- </div>
506
-
507
- <div class="data-row">
508
- <p class="data-label">Data</p>
509
- <p class="data-value">{{{{data}}}}</p>
510
- </div>
511
-
512
- <div class="data-row">
513
- <p class="data-label">Operation ID</p>
514
- <p class="data-value operation-id truncated">{{{{operation_id}}}}</p>
515
- </div>
516
-
517
- <button id="send-request-btn" class="api-button">Ping again</button>
518
- </div>
519
-
520
- <script>
521
- document.getElementById('send-request-btn').addEventListener('click', () => {{
522
- const apiEndpoint = 'https://neuronum.net/api/activate/{txID}';
523
-
524
- const dataToSend = {{
525
- "data": {{"ping": "node"}},
526
- "cell": {{
527
- "host": CLIENT_CELL,
528
- "session": CLIENT_SESSION,
529
- }}
530
- }};
531
-
532
- fetch(apiEndpoint, {{
533
- method: 'POST',
534
- headers: {{
535
- 'Content-Type': 'application/json',
536
- 'Accept': 'application/json'
537
- }},
538
- body: JSON.stringify(dataToSend)
539
- }})
540
- .then(response => {{
541
- if (!response.ok) {{
542
- throw new Error(`HTTP error! status: ${{response.status}}`);
543
- }}
544
- return response.json();
545
- }})
546
- .then(data => {{
547
- if (data.success && data.response && data.response.html) {{
548
- document.open();
549
- document.write(data.response.html);
550
- document.close();
551
- console.log('API Response: Page replaced with new HTML.');
552
- }} else {{
553
- console.error('API Response does not contain HTML to replace the page:', data);
554
- alert('API response error: Expected HTML content to replace the page.');
555
- }}
556
- }})
557
- .catch(error => {{
558
- console.error('API request failed:', error);
559
- alert('API request failed. See the console for details.');
560
- }});
561
- }});
562
- </script>
563
-
564
- <div id="legal-banner" style="border-radius: 10px; margin: 15px; position: fixed; bottom: 0; left: 0; right: 0; background-color: #2a2a2a; color: #e0e0e0; padding: 16px; text-align: center; font-size: 14px; z-index: 9999; box-shadow: 0 -2px 10px rgba(0,0,0,0.5);">
565
- By continuing, you agree to our
566
- Terms (<span style="color: #8cafff;">{{{{terms_url}}}}</span>) &
567
- Privacy Policy (<span style="color: #8cafff;">{{{{privacy_url}}}}</span>)
568
- <br>
569
- <button id="accept-legal" style="margin-top: 15px; margin-bottom: 15px; background: #01c07d; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer;">Accept</button>
570
- <br>
571
- Last Update: {{{{last_update}}}}
572
- </div>
573
-
574
- <script>
575
- const banner = document.getElementById('legal-banner');
576
- const acceptBtn = document.getElementById('accept-legal');
577
- acceptBtn.addEventListener('click', () => {{
578
- banner.remove();
579
- }});
580
- </script>
581
-
582
- </body>
583
- </html>
584
- """
585
- html_path.write_text(html_content)
586
-
587
- config_path = project_path / "config.json"
588
- await asyncio.to_thread(
589
- config_path.write_text,
590
- f"""{{
591
- "app_metadata": {{
592
- "name": "{descr}",
593
- "version": "1.0.0",
594
- "author": "{host}"
595
- }},
596
- "data_gateways": [
597
- {{
598
- "type": "transmitter",
599
- "id": "{txID}",
600
- "info": "Ping Your Node"
601
- }}
602
- ],
603
- "legals": {{
604
- "terms": "https://url_to_your/terms",
605
- "privacy_policy": "https://url_to_your/privacy_policy",
606
- "last_update" : "DD/MM/YYYY"
607
- }}
608
- }}"""
609
- )
610
-
611
- nodemd_path = project_path / "NODE.md"
612
- await asyncio.to_thread(nodemd_path.write_text, f"""### NODE.md of {nodeID}
613
-
614
- Welcome to your Node's documentation! This guide provides several ways for users to interact with your application.
615
-
616
- ***
617
-
618
- ### 💻 Using the CLI
619
-
620
- To ping this Node via the command-line interface, use the following command:
621
-
622
- `neuronum activate --tx {txID} 'ping:node'`
623
-
624
- ***
625
-
626
- ### 🐍 With Python
627
-
628
- For programmatic access, use the following Python code snippet. This script utilizes the `neuronum` library to activate the transaction and receive a response.
629
-
630
- ```python
631
- import asyncio
632
- import neuronum
633
-
634
- # Set up Cell connection parameters
635
- cell = neuronum.Cell(
636
- host="host", # Cell host
637
- password="password", # Cell password
638
- network="neuronum.net", # Cell network
639
- synapse="synapse" # Cell synapse
640
- )
308
+ try:
309
+ private_key = ec.generate_private_key(ec.SECP256R1())
310
+ public_key = private_key.public_key()
641
311
 
642
- async def main():
643
- # Define the transaction ID and data payload
644
- TX = "{txID}"
645
- data = {{"ping": "node"}}
646
-
647
- # Activate the transaction and get the response
648
- tx_response = await cell.activate_tx(TX, data)
649
-
650
- # Print the response from the Node
651
- print(tx_response)
652
-
653
- # Run the main asynchronous function
654
- if __name__ == "__main__":
655
- asyncio.run(main())
656
- ```
312
+ pem_private = private_key.private_bytes(
313
+ encoding=serialization.Encoding.PEM,
314
+ format=serialization.PrivateFormat.PKCS8,
315
+ encryption_algorithm=serialization.NoEncryption()
316
+ )
657
317
 
658
- 🤖 Via Cellai (Android App - Currently in Testing)
659
- Download the app from the Google Play Store.
660
- Send the command "Ping Node" to Cellai
661
- """)
662
-
663
- else:
664
- stxID = "id::stx"
665
- txID = "id::tx"
318
+ pem_public = public_key.public_bytes(
319
+ encoding=serialization.Encoding.PEM,
320
+ format=serialization.PublicFormat.SubjectPublicKeyInfo
321
+ )
666
322
 
667
- app_path = project_path / "app.py"
668
- app_path.write_text(f"""\
323
+ public_key_pem_file = project_path / "public_key.pem"
324
+ with open(public_key_pem_file, "wb") as key_file:
325
+ key_file.write(pem_public)
326
+
327
+ private_key_pem_file = project_path / "private_key.pem"
328
+ with open(private_key_pem_file, "wb") as key_file:
329
+ key_file.write(pem_private)
330
+
331
+ pem_public_str = pem_public.decode('utf-8')
332
+ pem_public_oneline = "".join(pem_public_str.split())
333
+
334
+ current_directory = os.getcwd()
335
+ private_key_file = os.path.join(current_directory / project_path, "private_key.pem")
336
+ public_key_file = os.path.join(current_directory / project_path, "public_key.pem")
337
+ except:
338
+ print("Error creating Private/Public Key Pair")
339
+
340
+ app_path = project_path / "app.py"
341
+ app_path.write_text(f"""\
669
342
  import asyncio
670
- import neuronum
671
- import os
672
- import json
673
- from dotenv import load_dotenv
674
- from jinja2 import Environment, FileSystemLoader
675
-
343
+ from neuronum import Node
344
+ from jinja2 import Environment, FileSystemLoader
345
+
676
346
  env = Environment(loader=FileSystemLoader('.'))
677
347
  template = env.get_template('ping.html')
678
-
679
- with open('config.json', 'r') as f:
680
- data = json.load(f)
681
- terms_url = data['legals']['terms']
682
- privacy_url = data['legals']['privacy_policy']
683
- last_update = data['legals']['last_update']
684
-
685
-
686
- load_dotenv()
687
- host = os.getenv("HOST")
688
- password = os.getenv("PASSWORD")
689
- network = os.getenv("NETWORK")
690
- synapse = os.getenv("SYNAPSE")
691
-
692
- cell = neuronum.Cell(
693
- host=host,
694
- password=password,
695
- network=network,
696
- synapse=synapse
348
+
349
+ node = Node(
350
+ id="{node_id}",
351
+ private_key="{private_key_file}",
352
+ public_key="{public_key_file}"
697
353
  )
698
-
699
- async def main():
700
- STX = "{stxID}"
701
- async for operation in cell.sync(STX):
702
- txID = operation.get("txID")
703
- client = operation.get("operator")
704
- ts = operation.get("time")
705
- data = operation.get("data")
706
- operation_id = operation.get("operationID")
707
-
708
- if txID == "{txID}":
354
+
355
+ async def main():
356
+
357
+ async for transmitter in node.sync():
358
+ ts = transmitter.get("time")
359
+ data = transmitter.get("data")
360
+ transmitter_id = transmitter.get("transmitter_id")
361
+ client = transmitter.get("operator")
362
+ client_public_key = data.get("publicKey")
363
+ action = data.get("action")
364
+
365
+ response_data = {{}}
366
+
367
+ if action == "ping_node":
709
368
 
710
- def render_html_template(client, ts, data, operation_id, terms_url, privacy_url, last_update):
711
- return template.render(client=client, ts=ts, data=data, operation_id=operation_id, terms_url=terms_url, privacy_url=privacy_url, last_update=last_update)
712
-
713
- html_content = render_html_template(client, ts, data, operation_id, terms_url, privacy_url, last_update)
369
+ html_content = template.render(client=client, ts=ts, data=action, transmitter_id=transmitter_id)
714
370
 
715
- data = {{
716
- "json": f"{{operation_id}} - Reply from {nodeID}: Pinged by {{client}} at {{ts}} with data: {{data}}",
371
+ response_data = {{
372
+ "json": f"{{transmitter_id}} - Reply from {node_id}: Pinged by {{client}} at {{ts}} with action: {{action}}",
717
373
  "html": html_content
718
374
  }}
719
-
720
- await cell.notify(f"{{client}}", "{nodeID} Ping","Pinged successfully")
721
-
722
- await cell.tx_response(txID, client, data)
375
+
376
+ await node.tx_response(transmitter_id, response_data, client_public_key)
723
377
 
724
378
  asyncio.run(main())
725
379
  """)
726
380
 
727
- html_path = project_path / "ping.html"
728
- html_content = f"""\
381
+ html_path = project_path / "ping.html"
382
+ html_content = f"""\
729
383
  <!DOCTYPE html>
730
384
  <html>
731
385
  <head>
@@ -807,7 +461,7 @@ asyncio.run(main())
807
461
  .data-value.timestamp {{
808
462
  color: #a1e8a1;
809
463
  }}
810
- .data-value.operation-id {{
464
+ .data-value.transmitter-id {{
811
465
  color: #f7a2a2;
812
466
  }}
813
467
  .api-button {{
@@ -828,7 +482,7 @@ asyncio.run(main())
828
482
  <div class="container">
829
483
  <img class="logo" src="https://neuronum.net/static/logo.png" alt="Neuronum Logo">
830
484
 
831
- <h1>Reply from {nodeID}</h1>
485
+ <h1>Reply from {node_id}</h1>
832
486
  <p class="subtitle">Pinged successfully.</p>
833
487
 
834
488
  <div class="data-row">
@@ -847,157 +501,61 @@ asyncio.run(main())
847
501
  </div>
848
502
 
849
503
  <div class="data-row">
850
- <p class="data-label">Operation ID</p>
851
- <p class="data-value operation-id truncated">{{{{operation_id}}}}</p>
504
+ <p class="data-label">Transmitter ID</p>
505
+ <p class="data-value transmitter-id truncated">{{{{transmitter_id}}}}</p>
852
506
  </div>
853
507
 
854
508
  <button id="send-request-btn" class="api-button">Ping again</button>
855
509
  </div>
856
510
 
857
511
  <script>
858
- document.getElementById('send-request-btn').addEventListener('click', () => {{
859
- const apiEndpoint = 'https://neuronum.net/api/activate/{txID}';
860
-
861
- const dataToSend = {{
862
- "data": {{"ping": "node"}},
863
- "cell": {{
864
- "host": CLIENT_CELL,
865
- "session": CLIENT_SESSION,
866
- }}
867
- }};
868
-
869
- fetch(apiEndpoint, {{
870
- method: 'POST',
871
- headers: {{
872
- 'Content-Type': 'application/json',
873
- 'Accept': 'application/json'
874
- }},
875
- body: JSON.stringify(dataToSend)
876
- }})
877
- .then(response => {{
878
- if (!response.ok) {{
879
- throw new Error(`HTTP error! status: ${{response.status}}`);
512
+ document.getElementById('send-request-btn').addEventListener('click', () => {{
513
+ const messagePayload = {{
514
+ type: 'iframe_request',
515
+ endpoint: 'https://neuronum.net/browser/api/activate_tx/{node_id}',
516
+ data: {{ "action": "ping_node" }},
517
+ nodePublicKey: '{pem_public_oneline}',
518
+ }};
519
+
520
+ if (window.parent) {{
521
+ window.parent.postMessage(messagePayload, '*');
880
522
  }}
881
- return response.json();
882
- }})
883
- .then(data => {{
884
- if (data.success && data.response && data.response.html) {{
885
- document.open();
886
- document.write(data.response.html);
887
- document.close();
888
- console.log('API Response: Page replaced with new HTML.');
889
- }} else {{
890
- console.error('API Response does not contain HTML to replace the page:', data);
891
- alert('API response error: Expected HTML content to replace the page.');
892
- }}
893
- }})
894
- .catch(error => {{
895
- console.error('API request failed:', error);
896
- alert('API request failed. See the console for details.');
897
523
  }});
898
- }});
899
- </script>
900
-
901
- <div id="legal-banner" style="border-radius: 10px; margin: 15px; position: fixed; bottom: 0; left: 0; right: 0; background-color: #2a2a2a; color: #e0e0e0; padding: 16px; text-align: center; font-size: 14px; z-index: 9999; box-shadow: 0 -2px 10px rgba(0,0,0,0.5);">
902
- By continuing, you agree to our
903
- Terms (<span style="color: #8cafff;">{{{{terms_url}}}}</span>) &
904
- Privacy Policy (<span style="color: #8cafff;">{{{{privacy_url}}}}</span>)
905
- <br>
906
- <button id="accept-legal" style="margin-top: 15px; margin-bottom: 15px; background: #01c07d; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer;">Accept</button>
907
- <br>
908
- Last Update: {{{{last_update}}}}
909
- </div>
910
-
911
- <script>
912
- const banner = document.getElementById('legal-banner');
913
- const acceptBtn = document.getElementById('accept-legal');
914
- acceptBtn.addEventListener('click', () => {{
915
- banner.remove();
916
- }});
917
524
  </script>
918
525
 
919
526
  </body>
920
527
  </html>
921
528
  """
922
- html_path.write_text(html_content)
923
-
924
- config_path = project_path / "config.json"
925
- await asyncio.to_thread(
926
- config_path.write_text,
529
+ html_path.write_text(html_content)
530
+ config_path = project_path / "config.json"
531
+ await asyncio.to_thread(
532
+ config_path.write_text,
927
533
  f"""{{
928
- "app_metadata": {{
929
- "name": "{descr}",
930
- "version": "1.0.0",
931
- "author": "{host}"
932
- }},
933
- "data_gateways": [
534
+ "app_metadata": {{
535
+ "name": "{descr}",
536
+ "version": "1.0.0",
537
+ "author": "{host}"
538
+ }},
539
+ "data_gateways": [
540
+ {{
541
+ "node_id": "{node_id}",
542
+ "actions": [
934
543
  {{
935
- "type": "transmitter",
936
- "id": "{txID}",
937
- "info": "Ping Your Node"
544
+ "action": "ping_node",
545
+ "info": "Ping Node"
938
546
  }}
939
- ],
940
- "legals": {{
941
- "terms": "https://url_to_your/terms",
942
- "privacy_policy": "https://url_to_your/privacy_policy",
943
- "last_update" : "DD/MM/YYYY"
547
+ ]
944
548
  }}
549
+ ],
550
+ "legals": {{
551
+ "terms": "https://url_to_your/terms",
552
+ "privacy_policy": "https://url_to_your/privacy_policy"
553
+ }},
554
+ "public_key": "{pem_public_oneline}"
945
555
  }}"""
946
- )
947
-
948
- nodemd_path = project_path / "NODE.md"
949
- await asyncio.to_thread(nodemd_path.write_text, f"""### NODE.md of {nodeID}
950
-
951
- Welcome to your Node's documentation! This guide provides several ways for users to interact with your application.
952
-
953
- ***
954
-
955
- ### 💻 Using the CLI
956
-
957
- To ping this Node via the command-line interface, use the following command:
958
-
959
- `neuronum activate --tx {txID} 'ping:node'`
960
-
961
- ***
962
-
963
- ### 🐍 With Python
964
556
 
965
- For programmatic access, use the following Python code snippet. This script utilizes the `neuronum` library to activate the transaction and receive a response.
966
-
967
- ```python
968
- import asyncio
969
- import neuronum
970
-
971
- # Set up Cell connection parameters
972
- cell = neuronum.Cell(
973
- host="host", # Cell host
974
- password="password", # Cell password
975
- network="neuronum.net", # Cell network
976
- synapse="synapse" # Cell synapse
977
557
  )
978
-
979
- async def main():
980
- # Define the transaction ID and data payload
981
- TX = "{txID}"
982
- data = {{"ping": "node"}}
983
-
984
- # Activate the transaction and get the response
985
- tx_response = await cell.activate_tx(TX, data)
986
-
987
- # Print the response from the Node
988
- print(tx_response)
989
-
990
- # Run the main asynchronous function
991
- if __name__ == "__main__":
992
- asyncio.run(main())
993
- ```
994
-
995
- 🤖 Via Cellai (Android App - Currently in Testing)
996
- Download the app from the Google Play Store.
997
- Send the command "Ping Node" to Cellai
998
- """)
999
-
1000
- click.echo(f"Neuronum Node '{nodeID}' initialized!")
558
+ click.echo(f"Neuronum Node '{node_id}' initialized!")
1001
559
 
1002
560
 
1003
561
  @click.command()
@@ -1071,14 +629,12 @@ def start_node(d):
1071
629
  def check_node():
1072
630
  click.echo("Checking Node status...")
1073
631
 
1074
- env_data = {}
1075
632
  try:
1076
- with open(".env", "r") as f:
1077
- for line in f:
1078
- if "=" in line:
1079
- key, value = line.strip().split("=", 1)
1080
- env_data[key] = value
1081
- nodeID = env_data.get("NODE", "")
633
+ with open('config.json', 'r') as f:
634
+ data = json.load(f)
635
+
636
+ nodeID = data['data_gateways'][0]['node_id']
637
+
1082
638
  except FileNotFoundError:
1083
639
  click.echo("Error: .env with credentials not found")
1084
640
  return
@@ -1137,14 +693,11 @@ def restart_node(d):
1137
693
 
1138
694
  start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
1139
695
 
1140
- env_data = {}
1141
696
  try:
1142
- with open(".env", "r") as f:
1143
- for line in f:
1144
- key, value = line.strip().split("=")
1145
- env_data[key] = value
697
+ with open('config.json', 'r') as f:
698
+ data = json.load(f)
1146
699
 
1147
- nodeID = env_data.get("NODE", "")
700
+ nodeID = data['data_gateways'][0]['node_id']
1148
701
 
1149
702
  except FileNotFoundError:
1150
703
  print("Error: .env with credentials not found")
@@ -1222,14 +775,11 @@ async def async_stop_node():
1222
775
 
1223
776
  node_pid_path = Path("status.txt")
1224
777
 
1225
- env_data = {}
1226
778
  try:
1227
- with open(".env", "r") as f:
1228
- for line in f:
1229
- key, value = line.strip().split("=")
1230
- env_data[key] = value
779
+ with open('config.json', 'r') as f:
780
+ data = json.load(f)
1231
781
 
1232
- nodeID = env_data.get("NODE", "")
782
+ nodeID = data['data_gateways'][0]['node_id']
1233
783
 
1234
784
  except FileNotFoundError:
1235
785
  print("Error: .env with credentials not found")
@@ -1265,10 +815,12 @@ async def async_stop_node():
1265
815
  @click.command()
1266
816
  def update_node():
1267
817
  click.echo("Update your Node")
818
+ credentials_folder_path = Path.home() / ".neuronum"
819
+ env_path = credentials_folder_path / ".env"
1268
820
  env_data = {}
1269
821
 
1270
822
  try:
1271
- with open(".env", "r") as f:
823
+ with open(env_path, "r") as f:
1272
824
  for line in f:
1273
825
  key, value = line.strip().split("=")
1274
826
  env_data[key] = value
@@ -1315,20 +867,26 @@ def update_node():
1315
867
  asyncio.run(async_update_node(node_type, descr, partners))
1316
868
 
1317
869
  async def async_update_node(node_type: str, descr: str, partners:str) -> None:
870
+ credentials_folder_path = Path.home() / ".neuronum"
871
+ env_path = credentials_folder_path / ".env"
1318
872
  env_data = {}
1319
873
 
1320
874
  try:
1321
- with open(".env", "r") as f:
875
+ with open(env_path, "r") as f:
1322
876
  for line in f:
1323
877
  key, value = line.strip().split("=")
1324
878
  env_data[key] = value
1325
879
 
1326
- nodeID = env_data.get("NODE", "")
1327
880
  host = env_data.get("HOST", "")
1328
881
  password = env_data.get("PASSWORD", "")
1329
882
  network = env_data.get("NETWORK", "")
1330
883
  synapse = env_data.get("SYNAPSE", "")
1331
884
 
885
+ with open('config.json', 'r') as f:
886
+ data = json.load(f)
887
+
888
+ nodeID = data['data_gateways'][0]['node_id']
889
+
1332
890
  except FileNotFoundError:
1333
891
  click.echo("Error: .env with credentials not found")
1334
892
  return
@@ -1337,17 +895,14 @@ async def async_update_node(node_type: str, descr: str, partners:str) -> None:
1337
895
  return
1338
896
 
1339
897
  try:
1340
- with open("NODE.md", "r") as f:
1341
- nodemd_file = f.read()
1342
-
1343
898
  with open("config.json", "r") as f:
1344
899
  config_file = f.read()
1345
900
 
1346
901
  except FileNotFoundError:
1347
- click.echo("Error: NODE.md file not found")
902
+ click.echo("Error: Config File not found")
1348
903
  return
1349
904
  except Exception as e:
1350
- click.echo(f"Error reading NODE.md file: {e}")
905
+ click.echo(f"Error reading Config file: {e}")
1351
906
  return
1352
907
 
1353
908
  if node_type == "partners":
@@ -1360,7 +915,6 @@ async def async_update_node(node_type: str, descr: str, partners:str) -> None:
1360
915
  "password": password,
1361
916
  "synapse": synapse,
1362
917
  "node_type": node_type,
1363
- "nodemd_file": nodemd_file,
1364
918
  "config_file": config_file,
1365
919
  "descr": descr,
1366
920
  }
@@ -1371,23 +925,24 @@ async def async_update_node(node_type: str, descr: str, partners:str) -> None:
1371
925
  response.raise_for_status()
1372
926
  data = await response.json()
1373
927
  nodeID = data["nodeID"]
1374
- node_url = data["node_url"]
1375
928
  except aiohttp.ClientError as e:
1376
929
  click.echo(f"Error sending request: {e}")
1377
930
  return
1378
931
 
1379
932
  if node_type == "public":
1380
- click.echo(f"Neuronum Node '{nodeID}' updated! Visit: {node_url}")
933
+ click.echo(f"Neuronum Node '{nodeID}' updated!")
1381
934
  else:
1382
935
  click.echo(f"Neuronum Node '{nodeID}' updated!")
1383
936
 
1384
937
 
1385
938
  def update_node_at_start():
1386
939
  click.echo("Update your Node")
940
+ credentials_folder_path = Path.home() / ".neuronum"
941
+ env_path = credentials_folder_path / ".env"
1387
942
  env_data = {}
1388
943
 
1389
944
  try:
1390
- with open(".env", "r") as f:
945
+ with open(env_path, "r") as f:
1391
946
  for line in f:
1392
947
  key, value = line.strip().split("=")
1393
948
  env_data[key] = value
@@ -1434,20 +989,26 @@ def update_node_at_start():
1434
989
  asyncio.run(async_update_node_at_start(node_type, descr, partners))
1435
990
 
1436
991
  async def async_update_node_at_start(node_type: str, descr: str, partners:str) -> None:
992
+ credentials_folder_path = Path.home() / ".neuronum"
993
+ env_path = credentials_folder_path / ".env"
1437
994
  env_data = {}
1438
995
 
1439
996
  try:
1440
- with open(".env", "r") as f:
997
+ with open(env_path, "r") as f:
1441
998
  for line in f:
1442
999
  key, value = line.strip().split("=")
1443
1000
  env_data[key] = value
1444
1001
 
1445
- nodeID = env_data.get("NODE", "")
1446
1002
  host = env_data.get("HOST", "")
1447
1003
  password = env_data.get("PASSWORD", "")
1448
1004
  network = env_data.get("NETWORK", "")
1449
1005
  synapse = env_data.get("SYNAPSE", "")
1450
1006
 
1007
+ with open('config.json', 'r') as f:
1008
+ data = json.load(f)
1009
+
1010
+ nodeID = data['data_gateways'][0]['node_id']
1011
+
1451
1012
  except FileNotFoundError:
1452
1013
  click.echo("Error: .env with credentials not found")
1453
1014
  return
@@ -1456,17 +1017,14 @@ async def async_update_node_at_start(node_type: str, descr: str, partners:str) -
1456
1017
  return
1457
1018
 
1458
1019
  try:
1459
- with open("NODE.md", "r") as f:
1460
- nodemd_file = f.read()
1461
-
1462
1020
  with open("config.json", "r") as f:
1463
1021
  config_file = f.read()
1464
1022
 
1465
1023
  except FileNotFoundError:
1466
- click.echo("Error: NODE.md file not found")
1024
+ click.echo("Error: File not found")
1467
1025
  return
1468
1026
  except Exception as e:
1469
- click.echo(f"Error reading NODE.md file: {e}")
1027
+ click.echo(f"Error reading file: {e}")
1470
1028
  return
1471
1029
 
1472
1030
  if node_type == "partners":
@@ -1479,7 +1037,6 @@ async def async_update_node_at_start(node_type: str, descr: str, partners:str) -
1479
1037
  "password": password,
1480
1038
  "synapse": synapse,
1481
1039
  "node_type": node_type,
1482
- "nodemd_file": nodemd_file,
1483
1040
  "config_file": config_file,
1484
1041
  "descr": descr,
1485
1042
  }
@@ -1490,13 +1047,12 @@ async def async_update_node_at_start(node_type: str, descr: str, partners:str) -
1490
1047
  response.raise_for_status()
1491
1048
  data = await response.json()
1492
1049
  nodeID = data["nodeID"]
1493
- node_url = data["node_url"]
1494
1050
  except aiohttp.ClientError as e:
1495
1051
  click.echo(f"Error sending request: {e}")
1496
1052
  return
1497
1053
 
1498
1054
  if node_type == "public":
1499
- click.echo(f"Neuronum Node '{nodeID}' updated! Visit: {node_url}")
1055
+ click.echo(f"Neuronum Node '{nodeID}' updated!")
1500
1056
  else:
1501
1057
  click.echo(f"Neuronum Node '{nodeID}' updated!")
1502
1058
 
@@ -1506,20 +1062,26 @@ def delete_node():
1506
1062
  asyncio.run(async_delete_node())
1507
1063
 
1508
1064
  async def async_delete_node():
1065
+ credentials_folder_path = Path.home() / ".neuronum"
1066
+ env_path = credentials_folder_path / ".env"
1509
1067
  env_data = {}
1510
1068
 
1511
1069
  try:
1512
- with open(".env", "r") as f:
1070
+ with open(env_path, "r") as f:
1513
1071
  for line in f:
1514
1072
  key, value = line.strip().split("=")
1515
1073
  env_data[key] = value
1516
1074
 
1517
- nodeID = env_data.get("NODE", "")
1518
1075
  host = env_data.get("HOST", "")
1519
1076
  password = env_data.get("PASSWORD", "")
1520
1077
  network = env_data.get("NETWORK", "")
1521
1078
  synapse = env_data.get("SYNAPSE", "")
1522
1079
 
1080
+ with open('config.json', 'r') as f:
1081
+ data = json.load(f)
1082
+
1083
+ nodeID = data['data_gateways'][0]['node_id']
1084
+
1523
1085
  except FileNotFoundError:
1524
1086
  click.echo("Error: .env with credentials not found")
1525
1087
  return
@@ -1548,130 +1110,6 @@ async def async_delete_node():
1548
1110
  click.echo(f"Neuronum Node '{nodeID}' deleted!")
1549
1111
 
1550
1112
 
1551
- @click.command()
1552
- @click.option('--tx', required=True, help="Transmitter ID")
1553
- @click.argument('kvpairs', nargs=-1)
1554
- def activate(tx, kvpairs):
1555
- try:
1556
- data = dict(pair.split(':', 1) for pair in kvpairs)
1557
- except ValueError:
1558
- click.echo("Invalid input. Use key:value pairs.")
1559
- return
1560
-
1561
- asyncio.run(async_activate(tx, data))
1562
-
1563
- async def async_activate(tx, data):
1564
- credentials_folder_path = Path.home() / ".neuronum"
1565
- env_path = credentials_folder_path / ".env"
1566
- env_data = {}
1567
-
1568
- try:
1569
- with open(env_path, "r") as f:
1570
- for line in f:
1571
- key, value = line.strip().split("=")
1572
- env_data[key] = value
1573
- except FileNotFoundError:
1574
- click.echo("No cell connected. Try: neuronum connect-cell")
1575
- return
1576
- except Exception as e:
1577
- click.echo(f"Error reading .env: {e}")
1578
- return
1579
-
1580
- cell = neuronum.Cell(
1581
- host=env_data.get("HOST", ""),
1582
- password=env_data.get("PASSWORD", ""),
1583
- network=env_data.get("NETWORK", ""),
1584
- synapse=env_data.get("SYNAPSE", "")
1585
- )
1586
-
1587
- tx_response = await cell.activate_tx(tx, data)
1588
- click.echo(tx_response)
1589
-
1590
-
1591
- @click.command()
1592
- @click.option('--ctx', required=True, help="Circuit ID")
1593
- @click.argument('label', nargs=-1)
1594
- def load(ctx, label):
1595
- if len(label) > 1 and all(Path(x).exists() for x in label):
1596
- label = "*"
1597
- else:
1598
- label = " ".join(label)
1599
-
1600
- asyncio.run(async_load(ctx, label))
1601
-
1602
-
1603
- async def async_load(ctx, label):
1604
- credentials_folder_path = Path.home() / ".neuronum"
1605
- env_path = credentials_folder_path / ".env"
1606
- env_data = {}
1607
-
1608
- try:
1609
- with open(env_path, "r") as f:
1610
- for line in f:
1611
- key, value = line.strip().split("=")
1612
- env_data[key] = value
1613
- except FileNotFoundError:
1614
- click.echo("No cell connected. Try: neuronum connect-cell")
1615
- return
1616
- except Exception as e:
1617
- click.echo(f"Error reading .env: {e}")
1618
- return
1619
-
1620
- cell = neuronum.Cell(
1621
- host=env_data.get("HOST", ""),
1622
- password=env_data.get("PASSWORD", ""),
1623
- network=env_data.get("NETWORK", ""),
1624
- synapse=env_data.get("SYNAPSE", "")
1625
- )
1626
-
1627
- data = await cell.load(label, ctx)
1628
- click.echo(data)
1629
-
1630
-
1631
- @click.command()
1632
- @click.option('--stx', default=None, help="Stream ID (optional)")
1633
- def sync(stx):
1634
- asyncio.run(async_sync(stx))
1635
-
1636
-
1637
- async def async_sync(stx):
1638
- credentials_folder_path = Path.home() / ".neuronum"
1639
- env_path = credentials_folder_path / ".env"
1640
- env_data = {}
1641
-
1642
- try:
1643
- with open(env_path, "r") as f:
1644
- for line in f:
1645
- key, value = line.strip().split("=")
1646
- env_data[key] = value
1647
- except FileNotFoundError:
1648
- click.echo("No cell connected. Try: neuronum connect-cell")
1649
- return
1650
- except Exception as e:
1651
- click.echo(f"Error reading .env: {e}")
1652
- return
1653
-
1654
- cell = neuronum.Cell(
1655
- host=env_data.get("HOST", ""),
1656
- password=env_data.get("PASSWORD", ""),
1657
- network=env_data.get("NETWORK", ""),
1658
- synapse=env_data.get("SYNAPSE", "")
1659
- )
1660
-
1661
- if stx:
1662
- print(f"Listening to Stream '{stx}'! Close connection with CTRL+C")
1663
- else:
1664
- print(f"Listening to '{cell.host}' private Stream! Close connection with CTRL+C")
1665
- async for operation in cell.sync() if stx is None else cell.sync(stx):
1666
- label = operation.get("label")
1667
- data = operation.get("data")
1668
- ts = operation.get("time")
1669
- stxID = operation.get("stxID")
1670
- operator = operation.get("operator")
1671
- txID = operation.get("txID")
1672
- print(label, data, ts, operator, txID, stxID)
1673
-
1674
-
1675
1113
  cli.add_command(create_cell)
1676
1114
  cli.add_command(connect_cell)
1677
1115
  cli.add_command(view_cell)
@@ -1684,9 +1122,6 @@ cli.add_command(restart_node)
1684
1122
  cli.add_command(stop_node)
1685
1123
  cli.add_command(check_node)
1686
1124
  cli.add_command(delete_node)
1687
- cli.add_command(activate)
1688
- cli.add_command(load)
1689
- cli.add_command(sync)
1690
1125
 
1691
1126
 
1692
1127
  if __name__ == "__main__":