neuronum 6.0.0__tar.gz → 7.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuronum
3
- Version: 6.0.0
3
+ Version: 7.0.0
4
4
  Summary: Official client library to interact with the Neuronum Network
5
5
  Home-page: https://neuronum.net
6
6
  Author: Neuronum Cybernetics
@@ -19,6 +19,7 @@ Requires-Dist: questionary
19
19
  Requires-Dist: python-dotenv
20
20
  Requires-Dist: requests
21
21
  Requires-Dist: psutil
22
+ Requires-Dist: jinja2
22
23
  Dynamic: author
23
24
  Dynamic: author-email
24
25
  Dynamic: classifier
@@ -104,102 +105,27 @@ neuronum connect-cell # connect Cell
104
105
 
105
106
 
106
107
  ### **Build On Neuronum**
107
- To dive deeper into Neuronum App development, visit & build with [Node Examples](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes/examples)
108
+ Visit & build with [Node Examples](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes/examples) to gain deeper knowledge on how to build on Neuronum.
108
109
 
109
- Initialize a Node (app template):
110
+ To get started, initialize a new Node with the command below.
110
111
  ```sh
111
- neuronum init-node --app # initialize a Node with app template
112
+ neuronum init-node
112
113
  ```
113
114
 
114
- This command prompts you to enter a Node description (e.g Test App) and creates a new directory named node_<node_id> containing the following files:
115
+ This command will prompt you for a description (e.g., Test App) and will create
115
116
 
116
- .env
117
- ```env
118
- NODE=your_node_id
119
- HOST=your_cell_id
120
- PASSWORD=your_password
121
- NETWORK=neuronum.net
122
- SYNAPSE=your_synapse # auth token
123
- ```
117
+ 1. A Stream (STX) so your App can receive requests, and a Transmitter (TX) to match those requests
118
+ 2. A new directory named "Test App_<your_node_id>" with the following files
124
119
 
125
- app.py
126
- ```python
127
- import asyncio
128
- import neuronum
129
- import os
130
- from dotenv import load_dotenv
131
-
132
- load_dotenv()
133
- host = os.getenv("HOST")
134
- password = os.getenv("PASSWORD")
135
- network = os.getenv("NETWORK")
136
- synapse = os.getenv("SYNAPSE")
137
-
138
- cell = neuronum.Cell(
139
- host=host,
140
- password=password,
141
- network=network,
142
- synapse=synapse
143
- )
120
+ .env: Stores your Node's credentials for connecting to the network.
144
121
 
145
- async def main():
146
- STX = "id::stx"
147
- async for operation in cell.sync(STX):
148
- txID = operation.get("txID")
149
- client = operation.get("operator")
150
-
151
- if txID == "id::tx":
152
- data = {
153
- "json": f"Hello {client}",
154
- "html": f"""
155
- <!DOCTYPE html>
156
- <html>
157
- <head>
158
- <meta charset="UTF-8">
159
- <title>Greeting Node</title>
160
- </head>
161
- <body>
162
- <div class="card">
163
- <h1>Hello, {client}</h1>
164
- </div>
165
- </body>
166
- </html>
167
- """
168
-
169
- }
170
- await cell.tx_response(txID, client, data)
122
+ app.py: The main Python script that contains your Node's core logic.
171
123
 
172
- asyncio.run(main())
173
- ```
174
-
175
- config.json (required if you want to make your app accessible to Cellai (currently in testing))
176
- ```json
177
- {
178
- "data_gateways": [
179
- {
180
- "type": "stream",
181
- "id": "id::stx",
182
- "info": "provide a detailed description about the stream"
183
- },
184
- {
185
- "type": "transmitter",
186
- "id": "id::tx",
187
- "info": "provide a detailed description about the transmitter"
188
- },
189
- {
190
- "type": "circuit",
191
- "id": "id::ctx",
192
- "info": "provide a detailed description about the circuit"
193
- }
194
- ]
195
- }
196
- ```
124
+ NODE.md: Public documentation that provides instructions for interacting with your Node.
197
125
 
198
- NODE.md (required if you want to publish your App)
199
- ```
200
- ### NODE.md: Create a detailed Markdown File on how to interact with this Node
201
- ```
126
+ config.json: A configuration file that stores metadata about your app and enables Cellai to interact with your Node's data gateways.
202
127
 
128
+ ping.html: A static HTML file used to render web-based responses.
203
129
 
204
130
  Change into Node folder
205
131
  ```sh
@@ -247,6 +173,6 @@ asyncio.run(main())
247
173
  neuronum activate --tx id::tx 'say:hello'
248
174
  ```
249
175
 
250
- #### **Cellai**
251
- Cellai is a mobile interface that lets you interact with Neuronum apps using natural language. It is currently in testing.
176
+ #### **Cellai (in Testing)**
177
+ Cellai is an AI tool for developers to test their apps built on Neuronum and will soon be published to the Google Play Store.
252
178
 
@@ -71,102 +71,27 @@ neuronum connect-cell # connect Cell
71
71
 
72
72
 
73
73
  ### **Build On Neuronum**
74
- To dive deeper into Neuronum App development, visit & build with [Node Examples](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes/examples)
74
+ Visit & build with [Node Examples](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes/examples) to gain deeper knowledge on how to build on Neuronum.
75
75
 
76
- Initialize a Node (app template):
76
+ To get started, initialize a new Node with the command below.
77
77
  ```sh
78
- neuronum init-node --app # initialize a Node with app template
78
+ neuronum init-node
79
79
  ```
80
80
 
81
- This command prompts you to enter a Node description (e.g Test App) and creates a new directory named node_<node_id> containing the following files:
81
+ This command will prompt you for a description (e.g., Test App) and will create
82
82
 
83
- .env
84
- ```env
85
- NODE=your_node_id
86
- HOST=your_cell_id
87
- PASSWORD=your_password
88
- NETWORK=neuronum.net
89
- SYNAPSE=your_synapse # auth token
90
- ```
83
+ 1. A Stream (STX) so your App can receive requests, and a Transmitter (TX) to match those requests
84
+ 2. A new directory named "Test App_<your_node_id>" with the following files
91
85
 
92
- app.py
93
- ```python
94
- import asyncio
95
- import neuronum
96
- import os
97
- from dotenv import load_dotenv
98
-
99
- load_dotenv()
100
- host = os.getenv("HOST")
101
- password = os.getenv("PASSWORD")
102
- network = os.getenv("NETWORK")
103
- synapse = os.getenv("SYNAPSE")
104
-
105
- cell = neuronum.Cell(
106
- host=host,
107
- password=password,
108
- network=network,
109
- synapse=synapse
110
- )
86
+ .env: Stores your Node's credentials for connecting to the network.
111
87
 
112
- async def main():
113
- STX = "id::stx"
114
- async for operation in cell.sync(STX):
115
- txID = operation.get("txID")
116
- client = operation.get("operator")
117
-
118
- if txID == "id::tx":
119
- data = {
120
- "json": f"Hello {client}",
121
- "html": f"""
122
- <!DOCTYPE html>
123
- <html>
124
- <head>
125
- <meta charset="UTF-8">
126
- <title>Greeting Node</title>
127
- </head>
128
- <body>
129
- <div class="card">
130
- <h1>Hello, {client}</h1>
131
- </div>
132
- </body>
133
- </html>
134
- """
135
-
136
- }
137
- await cell.tx_response(txID, client, data)
88
+ app.py: The main Python script that contains your Node's core logic.
138
89
 
139
- asyncio.run(main())
140
- ```
141
-
142
- config.json (required if you want to make your app accessible to Cellai (currently in testing))
143
- ```json
144
- {
145
- "data_gateways": [
146
- {
147
- "type": "stream",
148
- "id": "id::stx",
149
- "info": "provide a detailed description about the stream"
150
- },
151
- {
152
- "type": "transmitter",
153
- "id": "id::tx",
154
- "info": "provide a detailed description about the transmitter"
155
- },
156
- {
157
- "type": "circuit",
158
- "id": "id::ctx",
159
- "info": "provide a detailed description about the circuit"
160
- }
161
- ]
162
- }
163
- ```
90
+ NODE.md: Public documentation that provides instructions for interacting with your Node.
164
91
 
165
- NODE.md (required if you want to publish your App)
166
- ```
167
- ### NODE.md: Create a detailed Markdown File on how to interact with this Node
168
- ```
92
+ config.json: A configuration file that stores metadata about your app and enables Cellai to interact with your Node's data gateways.
169
93
 
94
+ ping.html: A static HTML file used to render web-based responses.
170
95
 
171
96
  Change into Node folder
172
97
  ```sh
@@ -214,6 +139,6 @@ asyncio.run(main())
214
139
  neuronum activate --tx id::tx 'say:hello'
215
140
  ```
216
141
 
217
- #### **Cellai**
218
- Cellai is a mobile interface that lets you interact with Neuronum apps using natural language. It is currently in testing.
142
+ #### **Cellai (in Testing)**
143
+ Cellai is an AI tool for developers to test their apps built on Neuronum and will soon be published to the Google Play Store.
219
144
 
@@ -249,17 +249,14 @@ def delete_cell():
249
249
 
250
250
 
251
251
  @click.command()
252
- @click.option('--sync', multiple=True, default=None, help="Optional stream IDs for sync.")
253
- @click.option('--stream', multiple=True, default=None, help="Optional stream ID for stream.")
254
- @click.option('--app', is_flag=True, help="Generate a Node with app template")
255
- def init_node(sync, stream, app):
252
+ def init_node():
256
253
  descr = click.prompt("Node description: Type up to 25 characters").strip()
257
254
  if descr and len(descr) > 25:
258
255
  click.echo("Description too long. Max 25 characters allowed.")
259
256
  return
260
- asyncio.run(async_init_node(sync, stream, app, descr))
257
+ asyncio.run(async_init_node(descr))
261
258
 
262
- async def async_init_node(sync, stream, app, descr):
259
+ async def async_init_node(descr):
263
260
  credentials_folder_path = Path.home() / ".neuronum"
264
261
  env_path = credentials_folder_path / ".env"
265
262
 
@@ -308,51 +305,37 @@ async def async_init_node(sync, stream, app, descr):
308
305
  click.echo(f"Error sending request: {e}")
309
306
  return
310
307
 
311
- node_filename = "node_" + nodeID.replace("::node", "")
308
+ node_filename = descr + "_" + nodeID.replace("::node", "")
312
309
  project_path = Path(node_filename)
313
310
  project_path.mkdir(exist_ok=True)
314
311
 
315
312
  env_path = project_path / ".env"
316
313
  await asyncio.to_thread(env_path.write_text, f"NODE={nodeID}\nHOST={host}\nPASSWORD={password}\nNETWORK={network}\nSYNAPSE={synapse}\n")
317
-
318
- if app:
319
- config_path = project_path / "config.json"
320
- await asyncio.to_thread(config_path.write_text, """{
321
- "data_gateways": [
322
- {
323
- "type": "stream",
324
- "id": "id::stx",
325
- "info": "provide a detailed description about the stream"
326
- },
327
- {
328
- "type": "transmitter",
329
- "id": "id::tx",
330
- "info": "provide a detailed description about the transmitter"
331
- },
332
- {
333
- "type": "circuit",
334
- "id": "id::ctx",
335
- "info": "provide a detailed description about the circuit"
336
- }
337
- ]
338
- }
339
- """
340
- )
341
314
 
342
- nodemd_path = project_path / "NODE.md"
343
- await asyncio.to_thread(nodemd_path.write_text, """### NODE.md: Create a detailed Markdown File on how to interact with this Node""")
315
+ stx_descr = f"{nodeID} App"
316
+ partners = ["private"]
317
+ stxID = await cell.create_stx(stx_descr, partners)
344
318
 
345
- stx = sync[0] if sync else (stream[0] if stream else host.replace("::cell", "::stx"))
319
+ tx_descr = f"Greet {nodeID}"
320
+ key_values = {
321
+ "ping": "pong",
322
+ }
323
+ STX = stxID
324
+ label = "ping:pong"
325
+ partners = ["private"]
326
+ txID = await cell.create_tx(tx_descr, key_values, STX, label, partners)
346
327
 
347
- if sync:
348
- for stx in sync:
349
- sync_path = project_path / f"sync_{stx.replace('::stx', '')}.py"
350
- sync_path.write_text(f"""\
328
+ app_path = project_path / "app.py"
329
+ app_path.write_text(f"""\
351
330
  import asyncio
352
331
  import neuronum
353
332
  import os
354
333
  from dotenv import load_dotenv
334
+ from jinja2 import Environment, FileSystemLoader
355
335
 
336
+ env = Environment(loader=FileSystemLoader('.'))
337
+ template = env.get_template('ping.html')
338
+
356
339
  load_dotenv()
357
340
  host = os.getenv("HOST")
358
341
  password = os.getenv("PASSWORD")
@@ -366,188 +349,281 @@ cell = neuronum.Cell(
366
349
  synapse=synapse
367
350
  )
368
351
 
369
- async def main():
370
- STX = "{stx}"
371
- async for operation in cell.sync(STX):
372
- label = operation.get("label")
373
- data = operation.get("data")
374
- ts = operation.get("time")
375
- stxID = operation.get("stxID")
376
- operator = operation.get("operator")
377
- print(label, data, ts, stxID, operator)
378
-
379
- asyncio.run(main())
380
- """)
352
+ async def main():
353
+ STX = "{stxID}"
354
+ async for operation in cell.sync(STX):
355
+ txID = operation.get("txID")
356
+ client = operation.get("operator")
357
+ ts = operation.get("time")
358
+ data = operation.get("data")
359
+ operation_id = operation.get("operationID")
360
+
361
+ if txID == "{txID}":
362
+
363
+ def render_html_template(client, ts, data, operation_id):
364
+ return template.render(client=client, ts=ts, data=data, operation_id=operation_id)
381
365
 
366
+ html_content = render_html_template(client, ts, data, operation_id)
382
367
 
383
- if stream:
384
- for stx in stream:
385
- stream_path = project_path / f"stream_{stx.replace('::stx', '')}.py"
386
- stream_path.write_text(f"""\
387
- import asyncio
388
- import neuronum
389
- import os
390
- from dotenv import load_dotenv
391
- import time
392
-
393
- load_dotenv()
394
- host = os.getenv("HOST")
395
- password = os.getenv("PASSWORD")
396
- network = os.getenv("NETWORK")
397
- synapse = os.getenv("SYNAPSE")
368
+ data = {{
369
+ "json": f"{{operation_id}} - Reply from {nodeID}: Pinged by {{client}} at {{ts}} with data: {{data}}",
370
+ "html": html_content
371
+ }}
398
372
 
399
- cell = neuronum.Cell(
400
- host=host,
401
- password=password,
402
- network=network,
403
- synapse=synapse
404
- )
373
+ await cell.notify(f"{{client}}", "{nodeID} Ping","Pinged successfully")
405
374
 
406
- async def main():
407
- STX = "{stx}"
408
- label = "your_label"
409
-
410
- while True:
411
- data = {{
412
- "key1": "value1",
413
- "key2": "value2",
414
- "key3": "value3",
415
- }}
416
- await cell.stream(label, data, STX)
417
- time.sleep(5)
375
+ await cell.tx_response(txID, client, data)
418
376
 
419
377
  asyncio.run(main())
420
378
  """)
421
379
 
422
- if not sync and not stream and not app:
423
- sync_path = project_path / f"sync_{stx.replace('::stx', '')}.py"
424
- sync_path.write_text(f"""\
425
- import asyncio
426
- import neuronum
427
- import os
428
- from dotenv import load_dotenv
380
+ html_path = project_path / "ping.html"
381
+ html_content = f"""\
382
+ <!DOCTYPE html>
383
+ <html>
384
+ <head>
385
+ <style>
386
+ body {{
387
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
388
+ background-color: #121212;
389
+ color: #e0e0e0;
390
+ margin: 0;
391
+ padding: 0;
392
+ display: flex;
393
+ justify-content: center;
394
+ align-items: center;
395
+ min-height: 100vh;
396
+ }}
397
+
398
+ .container {{
399
+ background-color: #1e1e1e;
400
+ border-radius: 12px;
401
+ padding: 40px;
402
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
403
+ width: 100%;
404
+ max-width: 500px;
405
+ text-align: center;
406
+ box-sizing: border-box;
407
+ }}
408
+
409
+ .logo {{
410
+ width: 80px;
411
+ margin-bottom: 25px;
412
+ filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.1));
413
+ }}
414
+
415
+ h1 {{
416
+ font-size: 1.5em;
417
+ font-weight: 600;
418
+ margin-bottom: 5px;
419
+ color: #f5f5f5;
420
+ }}
421
+
422
+ .subtitle {{
423
+ font-size: 0.9em;
424
+ color: #a0a0a0;
425
+ margin-bottom: 30px;
426
+ }}
427
+
428
+ .data-row {{
429
+ background-color: #2a2a2a;
430
+ padding: 12px 15px;
431
+ border-radius: 8px;
432
+ margin-bottom: 10px;
433
+ display: flex;
434
+ justify-content: space-between;
435
+ align-items: center;
436
+ }}
437
+
438
+ .data-label {{
439
+ font-weight: 400;
440
+ color: #a0a0a0;
441
+ margin: 0;
442
+ }}
443
+
444
+ .data-value {{
445
+ font-weight: 500;
446
+ color: #e0e0e0;
447
+ margin: 0;
448
+ }}
449
+
450
+ .data-value.truncated {{
451
+ white-space: nowrap;
452
+ overflow: hidden;
453
+ text-overflow: ellipsis;
454
+ max-width: 60%;
455
+ }}
456
+
457
+ .data-value.client {{
458
+ color: #8cafff;
459
+ }}
460
+ .data-value.timestamp {{
461
+ color: #a1e8a1;
462
+ }}
463
+ .data-value.operation-id {{
464
+ color: #f7a2a2;
465
+ }}
466
+ </style>
467
+ </head>
468
+ <body>
469
+ <div class="container">
470
+ <img class="logo" src="https://neuronum.net/static/logo.png" alt="Neuronum Logo">
471
+
472
+ <h1>Reply from {nodeID}</h1>
473
+ <p class="subtitle">Pinged successfully.</p>
474
+
475
+ <div class="data-row">
476
+ <p class="data-label">Client</p>
477
+ <p class="data-value client">{{{{client}}}}</p>
478
+ </div>
479
+
480
+ <div class="data-row">
481
+ <p class="data-label">Timestamp</p>
482
+ <p class="data-value timestamp">{{{{ts}}}}</p>
483
+ </div>
484
+
485
+ <div class="data-row">
486
+ <p class="data-label">Data</p>
487
+ <p class="data-value">{{{{data}}}}</p>
488
+ </div>
489
+
490
+ <div class="data-row">
491
+ <p class="data-label">Operation ID</p>
492
+ <p class="data-value operation-id truncated">{{{{operation_id}}}}</p>
493
+ </div>
494
+
495
+ <button id="send-request-btn" class="api-button">Ping again</button>
496
+ </div>
429
497
 
430
- load_dotenv()
431
- host = os.getenv("HOST")
432
- password = os.getenv("PASSWORD")
433
- network = os.getenv("NETWORK")
434
- synapse = os.getenv("SYNAPSE")
498
+ <script>
499
+ document.getElementById('send-request-btn').addEventListener('click', () => {{
500
+ const apiEndpoint = 'https://neuronum.net/api/activate/{{{{txID}}}}';
435
501
 
436
- cell = neuronum.Cell(
437
- host=host,
438
- password=password,
439
- network=network,
440
- synapse=synapse
502
+ const dataToSend = {{
503
+ "data": {{"ping": "node"}},
504
+ "cell": {{
505
+ "host": CLIENT_CELL,
506
+ "session": CLIENT_SESSION,
507
+ }}
508
+ }};
509
+
510
+ fetch(apiEndpoint, {{
511
+ method: 'POST',
512
+ headers: {{
513
+ 'Content-Type': 'application/json',
514
+ 'Accept': 'application/json'
515
+ }},
516
+ body: JSON.stringify(dataToSend)
517
+ }})
518
+ .then(response => {{
519
+ if (!response.ok) {{
520
+ throw new Error(`HTTP error! status: ${{response.status}}`);
521
+ }}
522
+ return response.json();
523
+ }})
524
+ .then(data => {{
525
+ if (data.success && data.response && data.response.html) {{
526
+ document.open();
527
+ document.write(data.response.html);
528
+ document.close();
529
+ console.log('API Response: Page replaced with new HTML.');
530
+ }} else {{
531
+ console.error('API Response does not contain HTML to replace the page:', data);
532
+ alert('API response error: Expected HTML content to replace the page.');
533
+ }}
534
+ }})
535
+ .catch(error => {{
536
+ console.error('API request failed:', error);
537
+ alert('API request failed. See the console for details.');
538
+ }});
539
+ }});
540
+ </script>
541
+ </body>
542
+ </html>
543
+ """
544
+ html_path.write_text(html_content)
545
+
546
+ config_path = project_path / "config.json"
547
+ await asyncio.to_thread(
548
+ config_path.write_text,
549
+ f"""{{
550
+ "app_metadata": {{
551
+ "name": "{descr}",
552
+ "version": "1.0.0",
553
+ "author": "{host}"
554
+ }},
555
+ "data_gateways": [
556
+ {{
557
+ "type": "transmitter",
558
+ "id": "{txID}",
559
+ "info": "Ping Your Node"
560
+ }}
561
+ ],
562
+ "legals": {{
563
+ "terms_and_conditions": "https://neuronum.net/legals",
564
+ "data_privacy": "https://neuronum.net/legals"
565
+ }}
566
+ }}"""
441
567
  )
442
568
 
443
- async def main():
444
- async for operation in cell.sync():
445
- message = operation.get("data").get("message")
446
- print(message)
569
+ nodemd_path = project_path / "NODE.md"
570
+ await asyncio.to_thread(nodemd_path.write_text, f"""### NODE.md of {nodeID}
447
571
 
448
- asyncio.run(main())
449
- """)
450
-
451
- stream_path = project_path / f"stream_{stx.replace('::stx', '')}.py"
452
- stream_path.write_text(f"""\
453
- import asyncio
454
- import neuronum
455
- import os
456
- from dotenv import load_dotenv
457
- import time
572
+ Welcome to your Node's documentation! This guide provides several ways for users to interact with your application.
458
573
 
459
- load_dotenv()
460
- host = os.getenv("HOST")
461
- password = os.getenv("PASSWORD")
462
- network = os.getenv("NETWORK")
463
- synapse = os.getenv("SYNAPSE")
574
+ ***
464
575
 
465
- cell = neuronum.Cell(
466
- host=host,
467
- password=password,
468
- network=network,
469
- synapse=synapse
470
- )
576
+ ### 💻 Using the CLI
471
577
 
472
- async def main():
473
- label = "Welcome to Neuronum"
474
-
475
- while True:
476
- data = {{
477
- "message": "Hello, Neuronum!"
478
- }}
479
- await cell.stream(label, data)
480
- time.sleep(5)
578
+ To ping this Node via the command-line interface, use the following command:
481
579
 
482
- asyncio.run(main())
483
- """)
484
-
485
- if app and nodeID:
580
+ `neuronum activate --tx {txID} 'ping:node'`
581
+
582
+ ***
486
583
 
487
- descr = f"{nodeID} App"
488
- partners = ["private"]
489
- stxID = await cell.create_stx(descr, partners)
584
+ ### 🌐 Via the Web
490
585
 
586
+ You can also interact with this Node by simply visiting this URL in your web browser:
491
587
 
492
- descr = f"Greet {nodeID}"
493
- key_values = {
494
- "say": "hello",
495
- }
496
- STX = stxID
497
- label = "say:hello"
498
- partners = ["private"]
499
- txID = await cell.create_tx(descr, key_values, STX, label, partners)
588
+ [https://neuronum.net/tx/{txID}](https://neuronum.net/tx/{txID})
500
589
 
590
+ ***
501
591
 
502
- app_path = project_path / "app.py"
503
- app_path.write_text(f"""\
592
+ ### 🐍 With Python
593
+
594
+ For programmatic access, use the following Python code snippet. This script utilizes the `neuronum` library to activate the transaction and receive a response.
595
+
596
+ ```python
504
597
  import asyncio
505
598
  import neuronum
506
- import os
507
- from dotenv import load_dotenv
508
-
509
- load_dotenv()
510
- host = os.getenv("HOST")
511
- password = os.getenv("PASSWORD")
512
- network = os.getenv("NETWORK")
513
- synapse = os.getenv("SYNAPSE")
514
599
 
600
+ # Set up Cell connection parameters
515
601
  cell = neuronum.Cell(
516
- host=host,
517
- password=password,
518
- network=network,
519
- synapse=synapse
602
+ host="host", # Cell host
603
+ password="password", # Cell password
604
+ network="neuronum.net", # Cell network
605
+ synapse="synapse" # Cell synapse
520
606
  )
521
607
 
522
- async def main():
523
- STX = "{stxID}"
524
- async for operation in cell.sync(STX):
525
- txID = operation.get("txID")
526
- client = operation.get("operator")
527
-
528
- if txID == "{txID}":
529
- data = {{
530
- "json": f"Hello {{client}} from {nodeID}",
531
- "html": f\"\"\"
532
- <!DOCTYPE html>
533
- <html>
534
- <head>
535
- <meta charset="UTF-8">
536
- <title>Greeting Node</title>
537
- </head>
538
- <body>
539
- <div class="card">
540
- <h1>Hello, {{client}}</h1>
541
- <p>Greetings from <span class="node">{nodeID}</span></p>
542
- </div>
543
- </body>
544
- </html>
545
- \"\"\"
546
-
547
- }}
548
- await cell.tx_response(txID, client, data)
608
+ async def main():
609
+ # Define the transaction ID and data payload
610
+ TX = "{txID}"
611
+ data = {{"ping": "node"}}
612
+
613
+ # Activate the transaction and get the response
614
+ tx_response = await cell.activate_tx(TX, data)
615
+
616
+ # Print the response from the Node
617
+ print(tx_response)
618
+
619
+ # Run the main asynchronous function
620
+ if __name__ == "__main__":
621
+ asyncio.run(main())
622
+ ```
549
623
 
550
- asyncio.run(main())
624
+ 🤖 Via Cellai (Android App - Currently in Testing)
625
+ Download the app from the Google Play Store.
626
+ Send the command "Ping Node" to Cellai
551
627
  """)
552
628
 
553
629
  click.echo(f"Neuronum Node '{nodeID}' initialized!")
@@ -556,6 +632,7 @@ asyncio.run(main())
556
632
  @click.command()
557
633
  @click.option('--d', is_flag=True, help="Start node in detached mode")
558
634
  def start_node(d):
635
+ update_node()
559
636
  pid_file = Path.cwd() / "status.txt"
560
637
  system_name = platform.system()
561
638
  active_pids = []
@@ -586,27 +663,24 @@ def start_node(d):
586
663
  click.echo("Starting Node...")
587
664
 
588
665
  project_path = Path.cwd()
589
- script_files = glob.glob("sync_*.py") + glob.glob("stream_*.py") + glob.glob("app.py")
666
+ script_files = glob.glob("stream.py") + glob.glob("app.py")
590
667
  processes = []
591
668
 
592
669
  for script in script_files:
593
670
  script_path = project_path / script
594
671
  if script_path.exists():
595
- python_cmd = "pythonw" if system_name == "Windows" else "python"
596
672
 
597
673
  if d:
598
674
  process = subprocess.Popen(
599
- ["nohup", python_cmd, str(script_path), "&"] if system_name != "Windows"
600
- else [python_cmd, str(script_path)],
675
+ ["nohup", sys.executable, str(script_path), "&"] if system_name != "Windows"
676
+ else ["pythonw", str(script_path)],
601
677
  stdout=subprocess.DEVNULL,
602
678
  stderr=subprocess.DEVNULL,
603
679
  start_new_session=True
604
680
  )
605
681
  else:
606
682
  process = subprocess.Popen(
607
- [sys.executable, str(script_path)],
608
- stdout=None,
609
- stderr=None
683
+ [sys.executable, str(script_path)]
610
684
  )
611
685
 
612
686
  processes.append(process.pid)
@@ -678,11 +752,6 @@ def check_node():
678
752
  continue
679
753
 
680
754
  if running_pids:
681
- for pid in running_pids:
682
- proc = psutil.Process(pid)
683
- mem = proc.memory_info().rss / (1024 * 1024) # in MB
684
- cpu = proc.cpu_percent(interval=0.1)
685
- click.echo(f"PID {pid} → Memory: {mem:.2f} MB | CPU: {cpu:.1f}%")
686
755
  click.echo(f"Node {nodeID} is running. Active PIDs: {', '.join(map(str, running_pids))}")
687
756
  else:
688
757
  click.echo(f"Node {nodeID} is not running.")
@@ -691,6 +760,7 @@ def check_node():
691
760
  @click.command()
692
761
  @click.option('--d', is_flag=True, help="Restart node in detached mode")
693
762
  def restart_node(d):
763
+ update_node()
694
764
  pid_file = Path.cwd() / "status.txt"
695
765
  system_name = platform.system()
696
766
 
@@ -740,27 +810,23 @@ def restart_node(d):
740
810
 
741
811
  click.echo(f"Starting Node {nodeID}...")
742
812
  project_path = Path.cwd()
743
- script_files = glob.glob("sync_*.py") + glob.glob("stream_*.py") + glob.glob("app.py")
813
+ script_files = glob.glob("stream.py") + glob.glob("app.py")
744
814
  processes = []
745
815
 
746
- python_cmd = "pythonw" if system_name == "Windows" else "python"
747
-
748
816
  for script in script_files:
749
817
  script_path = project_path / script
750
818
  if script_path.exists():
751
819
  if d:
752
820
  process = subprocess.Popen(
753
- ["nohup", python_cmd, str(script_path), "&"] if system_name != "Windows"
754
- else [python_cmd, str(script_path)],
821
+ ["nohup", sys.executable, str(script_path), "&"] if system_name != "Windows"
822
+ else ["pythonw", str(script_path)],
755
823
  stdout=subprocess.DEVNULL,
756
824
  stderr=subprocess.DEVNULL,
757
825
  start_new_session=True
758
826
  )
759
827
  else:
760
828
  process = subprocess.Popen(
761
- [sys.executable, str(script_path)],
762
- stdout=None,
763
- stderr=None
829
+ [sys.executable, str(script_path)]
764
830
  )
765
831
 
766
832
  processes.append(process.pid)
@@ -825,13 +891,35 @@ async def async_stop_node():
825
891
  click.echo("Error: Unable to stop some node processes.")
826
892
 
827
893
 
828
- @click.command()
829
894
  def update_node():
830
895
  click.echo("Update your Node")
831
- node_type = questionary.select(
832
- "Who can view your Node?:",
833
- choices=["public", "private", "partners"]
834
- ).ask()
896
+ env_data = {}
897
+
898
+ try:
899
+ with open(".env", "r") as f:
900
+ for line in f:
901
+ key, value = line.strip().split("=")
902
+ env_data[key] = value
903
+
904
+ host = env_data.get("HOST", "")
905
+
906
+ except FileNotFoundError:
907
+ click.echo("Error: .env with credentials not found")
908
+ return
909
+ except Exception as e:
910
+ click.echo(f"Error reading .env file: {e}")
911
+ return
912
+
913
+ if host.startswith("CMTY_"):
914
+ node_type = questionary.select(
915
+ "Community Cells can only create private Nodes",
916
+ choices=["private"]
917
+ ).ask()
918
+ else:
919
+ node_type = questionary.select(
920
+ "Who can view your Node?:",
921
+ choices=["public", "private", "partners"]
922
+ ).ask()
835
923
  partners = "None"
836
924
  if node_type == "partners":
837
925
  prompt_msg = (
@@ -1103,7 +1191,6 @@ cli.add_command(start_node)
1103
1191
  cli.add_command(restart_node)
1104
1192
  cli.add_command(stop_node)
1105
1193
  cli.add_command(check_node)
1106
- cli.add_command(update_node)
1107
1194
  cli.add_command(delete_node)
1108
1195
  cli.add_command(activate)
1109
1196
  cli.add_command(load)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuronum
3
- Version: 6.0.0
3
+ Version: 7.0.0
4
4
  Summary: Official client library to interact with the Neuronum Network
5
5
  Home-page: https://neuronum.net
6
6
  Author: Neuronum Cybernetics
@@ -19,6 +19,7 @@ Requires-Dist: questionary
19
19
  Requires-Dist: python-dotenv
20
20
  Requires-Dist: requests
21
21
  Requires-Dist: psutil
22
+ Requires-Dist: jinja2
22
23
  Dynamic: author
23
24
  Dynamic: author-email
24
25
  Dynamic: classifier
@@ -104,102 +105,27 @@ neuronum connect-cell # connect Cell
104
105
 
105
106
 
106
107
  ### **Build On Neuronum**
107
- To dive deeper into Neuronum App development, visit & build with [Node Examples](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes/examples)
108
+ Visit & build with [Node Examples](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes/examples) to gain deeper knowledge on how to build on Neuronum.
108
109
 
109
- Initialize a Node (app template):
110
+ To get started, initialize a new Node with the command below.
110
111
  ```sh
111
- neuronum init-node --app # initialize a Node with app template
112
+ neuronum init-node
112
113
  ```
113
114
 
114
- This command prompts you to enter a Node description (e.g Test App) and creates a new directory named node_<node_id> containing the following files:
115
+ This command will prompt you for a description (e.g., Test App) and will create
115
116
 
116
- .env
117
- ```env
118
- NODE=your_node_id
119
- HOST=your_cell_id
120
- PASSWORD=your_password
121
- NETWORK=neuronum.net
122
- SYNAPSE=your_synapse # auth token
123
- ```
117
+ 1. A Stream (STX) so your App can receive requests, and a Transmitter (TX) to match those requests
118
+ 2. A new directory named "Test App_<your_node_id>" with the following files
124
119
 
125
- app.py
126
- ```python
127
- import asyncio
128
- import neuronum
129
- import os
130
- from dotenv import load_dotenv
131
-
132
- load_dotenv()
133
- host = os.getenv("HOST")
134
- password = os.getenv("PASSWORD")
135
- network = os.getenv("NETWORK")
136
- synapse = os.getenv("SYNAPSE")
137
-
138
- cell = neuronum.Cell(
139
- host=host,
140
- password=password,
141
- network=network,
142
- synapse=synapse
143
- )
120
+ .env: Stores your Node's credentials for connecting to the network.
144
121
 
145
- async def main():
146
- STX = "id::stx"
147
- async for operation in cell.sync(STX):
148
- txID = operation.get("txID")
149
- client = operation.get("operator")
150
-
151
- if txID == "id::tx":
152
- data = {
153
- "json": f"Hello {client}",
154
- "html": f"""
155
- <!DOCTYPE html>
156
- <html>
157
- <head>
158
- <meta charset="UTF-8">
159
- <title>Greeting Node</title>
160
- </head>
161
- <body>
162
- <div class="card">
163
- <h1>Hello, {client}</h1>
164
- </div>
165
- </body>
166
- </html>
167
- """
168
-
169
- }
170
- await cell.tx_response(txID, client, data)
122
+ app.py: The main Python script that contains your Node's core logic.
171
123
 
172
- asyncio.run(main())
173
- ```
174
-
175
- config.json (required if you want to make your app accessible to Cellai (currently in testing))
176
- ```json
177
- {
178
- "data_gateways": [
179
- {
180
- "type": "stream",
181
- "id": "id::stx",
182
- "info": "provide a detailed description about the stream"
183
- },
184
- {
185
- "type": "transmitter",
186
- "id": "id::tx",
187
- "info": "provide a detailed description about the transmitter"
188
- },
189
- {
190
- "type": "circuit",
191
- "id": "id::ctx",
192
- "info": "provide a detailed description about the circuit"
193
- }
194
- ]
195
- }
196
- ```
124
+ NODE.md: Public documentation that provides instructions for interacting with your Node.
197
125
 
198
- NODE.md (required if you want to publish your App)
199
- ```
200
- ### NODE.md: Create a detailed Markdown File on how to interact with this Node
201
- ```
126
+ config.json: A configuration file that stores metadata about your app and enables Cellai to interact with your Node's data gateways.
202
127
 
128
+ ping.html: A static HTML file used to render web-based responses.
203
129
 
204
130
  Change into Node folder
205
131
  ```sh
@@ -247,6 +173,6 @@ asyncio.run(main())
247
173
  neuronum activate --tx id::tx 'say:hello'
248
174
  ```
249
175
 
250
- #### **Cellai**
251
- Cellai is a mobile interface that lets you interact with Neuronum apps using natural language. It is currently in testing.
176
+ #### **Cellai (in Testing)**
177
+ Cellai is an AI tool for developers to test their apps built on Neuronum and will soon be published to the Google Play Store.
252
178
 
@@ -5,3 +5,4 @@ questionary
5
5
  python-dotenv
6
6
  requests
7
7
  psutil
8
+ jinja2
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='neuronum',
5
- version='6.0.0',
5
+ version='7.0.0',
6
6
  author='Neuronum Cybernetics',
7
7
  author_email='welcome@neuronum.net',
8
8
  description='Official client library to interact with the Neuronum Network',
@@ -27,6 +27,7 @@ setup(
27
27
  'python-dotenv',
28
28
  'requests',
29
29
  'psutil',
30
+ 'jinja2',
30
31
  ],
31
32
  entry_points={
32
33
  "console_scripts": [
File without changes
File without changes
File without changes
File without changes
File without changes