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 +154 -719
- neuronum/__init__.py +1 -2
- neuronum/neuronum.py +268 -447
- neuronum-8.1.0.dist-info/METADATA +124 -0
- neuronum-8.1.0.dist-info/RECORD +10 -0
- neuronum-7.0.4.dist-info/METADATA +0 -171
- neuronum-7.0.4.dist-info/RECORD +0 -10
- {neuronum-7.0.4.dist-info → neuronum-8.1.0.dist-info}/WHEEL +0 -0
- {neuronum-7.0.4.dist-info → neuronum-8.1.0.dist-info}/entry_points.txt +0 -0
- {neuronum-7.0.4.dist-info → neuronum-8.1.0.dist-info}/licenses/LICENSE.md +0 -0
- {neuronum-7.0.4.dist-info → neuronum-8.1.0.dist-info}/top_level.txt +0 -0
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
|
-
|
|
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(
|
|
259
|
+
asyncio.run(async_init_node(descr))
|
|
259
260
|
|
|
260
|
-
async def async_init_node(
|
|
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
|
-
|
|
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 + "_" +
|
|
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
|
-
|
|
314
|
-
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
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
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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
|
-
|
|
668
|
-
|
|
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
|
|
671
|
-
import
|
|
672
|
-
|
|
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
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
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
|
-
|
|
701
|
-
async for
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
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
|
-
|
|
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
|
-
|
|
716
|
-
"json": f"{{
|
|
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
|
|
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
|
-
|
|
728
|
-
|
|
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.
|
|
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 {
|
|
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">
|
|
851
|
-
<p class="data-value
|
|
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
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
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
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
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
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
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
|
-
|
|
936
|
-
|
|
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(
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
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(
|
|
1143
|
-
|
|
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 =
|
|
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(
|
|
1228
|
-
|
|
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 =
|
|
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(
|
|
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(
|
|
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:
|
|
902
|
+
click.echo("Error: Config File not found")
|
|
1348
903
|
return
|
|
1349
904
|
except Exception as e:
|
|
1350
|
-
click.echo(f"Error reading
|
|
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!
|
|
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(
|
|
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(
|
|
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:
|
|
1024
|
+
click.echo("Error: File not found")
|
|
1467
1025
|
return
|
|
1468
1026
|
except Exception as e:
|
|
1469
|
-
click.echo(f"Error reading
|
|
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!
|
|
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(
|
|
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__":
|