nep-cli 0.2.1 → 0.2.2

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.
package/bin/image.html CHANGED
@@ -5,10 +5,10 @@
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>NEP+ Image</title>
8
- <!-- Include Vue 2 and Vuetify 2 from CDN -->
9
- <link href="https://cdn.jsdelivr.net/npm/vuetify@2.6.3/dist/vuetify.min.css" rel="stylesheet">
10
- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
11
- <script src="https://cdn.jsdelivr.net/npm/vuetify@2.6.3/dist/vuetify.js"></script>
8
+ <!-- Include Vue 2 and Vuetify 2 -->
9
+ <link href="assets/vuetify/vuetify.min.css" rel="stylesheet">
10
+ <script src="assets/vue.js"></script>
11
+ <script src="assets/vuetify/vuetify.js"></script>
12
12
  </head>
13
13
 
14
14
  <body>
@@ -16,13 +16,14 @@
16
16
  <v-app style="background-color: #121212;">
17
17
  <v-container>
18
18
  <v-toolbar flat dense dark color="transparent">
19
- <v-toolbar-title> Topic: {{ topic }}</v-toolbar-title>
19
+ <v-toolbar-title> Topic {{ topic }}</v-toolbar-title>
20
20
  <v-spacer></v-spacer>
21
21
  </v-toolbar>
22
22
  <v-row>
23
23
  <v-col>
24
24
  <v-card color="#1A1A1A">
25
- <v-img :src="imageSrc" alt="Streamed Image" contain></v-img>
25
+ <v-slider v-model="imageHeight" min="100" max="800" step="10" ></v-slider>
26
+ <v-img :src="imageSrc" :height="imageHeight + 'px'" alt="Streamed Image" contain></v-img>
26
27
  </v-card>
27
28
  </v-col>
28
29
  </v-row>
@@ -30,6 +31,7 @@
30
31
  </v-app>
31
32
  </div>
32
33
  <script src="/socket.io/socket.io.js"></script> <!-- Include the Socket.IO client library -->
34
+
33
35
  <script>
34
36
 
35
37
  // Function to get the value of a URL parameter by name
@@ -54,8 +56,14 @@
54
56
  return {
55
57
  imageSrc: '', // Initialize the image source as empty
56
58
  topic: "",
59
+ imageHeight: 400, // Initialize the image height
57
60
  };
58
61
  },
62
+ watch: {
63
+ topic(newVal) {
64
+ document.title = newVal ? `NEP+ Image ${newVal}` : 'NEP+ Image';
65
+ }
66
+ },
59
67
  mounted() {
60
68
  this.topic = getParameterByName('topic');
61
69
  const image = document.getElementById('image');
package/bin/index.js CHANGED
@@ -18,8 +18,8 @@ const fs = require('fs');
18
18
  const zmqc = require("zeromq/v5-compat");
19
19
 
20
20
  const PORT_MASTER_INFO = 50001; // Default port for master info
21
- const PORT_IMAGES = 50060;
22
- const PORT_JSON = 50080;
21
+ const PORT_SERVER = 50050;
22
+
23
23
 
24
24
 
25
25
  program
@@ -120,28 +120,6 @@ var onRegisteredTopic = function (node_request, topic_register, topic) {
120
120
  }
121
121
 
122
122
 
123
- var onResetTopic = function (node_request) {
124
-
125
- var topic = node_request["topic"]
126
- nep_configuration["current_port"] = Math.max(nep_configuration["current_port"], node_request["port"] + 2);
127
-
128
- if (node_request["socket"] === "publisher" || node_request["socket"] === "subscriber") {
129
- // Create new broker for many2many communication
130
- if (node_request["mode"] === "many2many") {
131
- restartBroker(node_request["topic"], node_request["port"]);
132
- }
133
-
134
- if ("msg_type" in node_request) {
135
- topic_register[topic] = { "port": node_request["port"], "socket": node_request["socket"], 'ip': nep_configuration["IP"], "mode": node_request["mode"], "msg_type": node_request["msg_type"] }
136
- }
137
- else {
138
- topic_register[topic] = { "port": node_request["port"], "socket": node_request["socket"], 'ip': nep_configuration["IP"], "mode": node_request["mode"], "msg_type": "json" }
139
- }
140
-
141
- topic_register[topic]["nodes"] = [];
142
- }
143
-
144
- }
145
123
 
146
124
  var onNewTopic = function (node_request, topic_register) {
147
125
 
@@ -197,16 +175,6 @@ var createBroker = function (topic) {
197
175
  }
198
176
  }
199
177
 
200
- var restartBroker = function (topic, port) {
201
- try {
202
- var broker = new nep.Broker(nep_configuration["IP"], port + 1, port)
203
- //var brokersubex = new BrokerBridgeSUBEX(conf["IP"],IP_EXTERNAL, conf["current_port"] , conf["current_port"])
204
- // Add broker defined to list of brokers
205
- nep_configuration["brokers"][topic] = broker
206
- } catch (error) {
207
- console.log("NEP ERROR: ports " + String(port) + " and " + String(port + 1) + " not avaliable")
208
- }
209
- }
210
178
 
211
179
  var m2mResponse = function (node_request, topic_register, topic) {
212
180
  if ("msg_type" in node_request) {
@@ -239,17 +207,6 @@ var updatePID = function (node_request, topic_register, topic) {
239
207
  }
240
208
 
241
209
 
242
- var restartTopics = function (node_request) {
243
- console.log(node_request)
244
- // Get topic name
245
- var topic = String(node_request['topic'])
246
-
247
- console.log(" --- Reset topic : *" + topic + "* ---")
248
- // Create new broker
249
- onResetTopic(node_request);
250
- onUpdateTopicList(topic_register)
251
- }
252
-
253
210
  const processMsg = (json_msg, nodes_register, topic_register) => {
254
211
  const node_request = JSON.parse(json_msg);
255
212
  const { node, socket, topic, pid } = node_request;
@@ -297,6 +254,10 @@ function startShowServer(port, topic, ip) {
297
254
  const io = socketIo(server);
298
255
  const path = require('path');
299
256
 
257
+ // Serve static files from node_modules
258
+ app.use(express.static(path.join(__dirname)));
259
+
260
+
300
261
  const node = new nep.Node("nep-cli-sub");
301
262
  const config = node.hybrid(ip)
302
263
 
@@ -330,6 +291,7 @@ function startJsonServer(port, topic, ip, msg_type = "json") {
330
291
  const server = http.createServer(app);
331
292
  const io = socketIo(server);
332
293
  const path = require('path');
294
+ app.use(express.static(path.join(__dirname)));
333
295
 
334
296
  const node = new nep.Node("nep-cli-sub");
335
297
  const config = node.hybrid(ip)
@@ -364,6 +326,68 @@ function startJsonServer(port, topic, ip, msg_type = "json") {
364
326
  }
365
327
 
366
328
 
329
+ const createRequester = (master_ip, port) => {
330
+ const requester = new zmq.Request();
331
+ requester.connect(`tcp://${master_ip}:${port}`);
332
+ return requester;
333
+ };
334
+
335
+ const sendRequest = async (requester, msg) => {
336
+ const message = JSON.stringify(msg);
337
+ await requester.send(message);
338
+ const [result] = await requester.receive();
339
+ return JSON.parse(result.toString());
340
+ };
341
+
342
+ const selectTopic = async (results, topic) => {
343
+ if (!topic || !results["input"].includes(topic)) {
344
+ const autoComplete = new AutoComplete({
345
+ name: 'topic',
346
+ message: !topic ? 'Select a topic:' : 'Invalid topic. Select a valid topic:',
347
+ choices: results["input"],
348
+ });
349
+ topic = await autoComplete.run();
350
+ }
351
+ return topic;
352
+ };
353
+
354
+ const selectIndex = async (index, maxIndex = 9) => {
355
+ const choices = ["0","1","2","3","4","5","6","7","8","9"];
356
+
357
+ const autoComplete = new AutoComplete({
358
+ name: 'index',
359
+ message: !index ? 'Select a index:' : 'Invalid index. Select a valid index:',
360
+ choices: choices,
361
+ });
362
+ index = await autoComplete.run();
363
+
364
+ return index;
365
+ };
366
+
367
+ const selectMsgType = async (allowedFormats, msg_type, topic) => {
368
+ var msgType = topic.split('/').pop();
369
+ if (msgType && msgType !== topic) {
370
+ msg_type = msgType;
371
+ }
372
+
373
+ if (!msg_type || !allowedFormats.includes(msg_type)) {
374
+ const autoComplete = new AutoComplete({
375
+ name: 'msg_type',
376
+ message: !msg_type ? 'Select a message type:' : `Select a valid format:`,
377
+ choices: allowedFormats,
378
+ });
379
+ msg_type = await autoComplete.run();
380
+ }
381
+ return msg_type;
382
+ };
383
+
384
+ const openSub = (master_ip, topic, msg_type, callback) => {
385
+ const node = new nep.Node("nep-cli-sub");
386
+ const conf = node.hybrid(master_ip);
387
+ const sub = node.new_sub(topic, msg_type, callback, conf);
388
+ };
389
+
390
+
367
391
  program
368
392
  .command('app')
369
393
  .description('Open NEP+ App')
@@ -388,6 +412,53 @@ program
388
412
  });
389
413
 
390
414
 
415
+ program
416
+ .command('publish <topic> <message>')
417
+ .description('Publish a message to a NEP+ topic')
418
+ .action((topic, message) => {
419
+
420
+ var openpub = function (master_ip = "127.0.0.1") {
421
+
422
+ var pubFunction = function () {
423
+
424
+ var msg = message.replace(/'/g, '"')
425
+ console.log(JSON.parse(msg))
426
+ pub.publish(JSON.parse(msg))
427
+ console.log("Message published")
428
+
429
+ process.exit(1)
430
+ }
431
+
432
+
433
+ var node = new nep.Node("nep-cli-pub")
434
+ var pub = node.new_pub(topic, "json")
435
+ setTimeout(pubFunction, 1000)
436
+ }
437
+
438
+ async function run() {
439
+ var requester = new zmq.Request;
440
+ var master_ip = "127.0.0.1"
441
+
442
+ requester.connect("tcp://" + master_ip + ":" + PORT_MASTER_INFO);
443
+
444
+ let msg = { "input": "topics" }
445
+ var message = JSON.stringify(msg);
446
+ await requester.send(message)
447
+ const [result] = await requester.receive()
448
+ var results = JSON.parse(result.toString())
449
+ //console.log(results);
450
+ if (results["input"].includes(topic)) {
451
+ console.log("")
452
+ openpub(master_ip)
453
+ }
454
+ else {
455
+ console.log("Topic is not registered");
456
+ }
457
+ }
458
+ run()
459
+ });
460
+
461
+
391
462
  program
392
463
  .command('open [programName]')
393
464
  .description('Open a NEP+ GUI')
@@ -433,7 +504,7 @@ program
433
504
 
434
505
  program
435
506
  .command('master')
436
- .description('Start NEP master in localhost')
507
+ .description('Fetches and displays the list of NEP+ topics from the NEP master')
437
508
  .action(async () => {
438
509
  try {
439
510
  var node = new nep.Node("nep-cli");
@@ -452,7 +523,7 @@ program
452
523
 
453
524
  program
454
525
  .command('topics')
455
- .description('Get list of NEP+ topics')
526
+ .description('Displays the list of NEP+ topics')
456
527
  .action(async () => {
457
528
  const master_ip = "127.0.0.1";
458
529
  const TIMEOUT_DURATION = 5000; // Timeout duration in milliseconds
@@ -503,141 +574,119 @@ program
503
574
  await getTopics(); // Call the function to get topics
504
575
  });
505
576
 
506
- program
507
- .command('show <topic> <msg_type> [index]')
508
- .description('Show images published to a NEP+ topic in the default browser')
509
- .action(async (topic, msg_type, index = 0) => {
510
- const ip = "127.0.0.1";
511
- const topicid = `${topic}`;
512
- const port = PORT_IMAGES + parseInt(index);
513
-
514
- const allowedFormats = ["json", "images", "dictionary"];
515
- if (!allowedFormats.includes(msg_type)) {
516
- console.error(`Format '${msg_type}' no allowed. Allowed formats are: ${allowedFormats.join(', ')}`);
517
- return;
518
- }
577
+ program
578
+ .command('show [topic] [index]')
579
+ .description('Displays messages published to a specified NEP+ topic in the default browser. ' +
580
+ 'The [index] parameter specifies the index of the message to display and must be a number between 0 and 49. '
581
+ )
582
+ .action(async (topic, index) => {
583
+ const ip = "127.0.0.1";
519
584
 
520
- if(msg_type === "images"){
585
+ const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
586
+ const master_ip = "127.0.0.1";
587
+
588
+ const requester = createRequester(master_ip, PORT_MASTER_INFO);
589
+ const results = await sendRequest(requester, { "input": "topics" });
590
+
591
+ topic = await selectTopic(results, topic);
592
+ var msg_type = await selectMsgType(allowedFormats, msg_type, topic);
593
+ index = await selectIndex(index);
594
+
595
+ const topicid = `${index}: ${topic}`;
596
+ if(parseInt(index) < 50)
597
+ {
598
+ const port = PORT_SERVER + parseInt(index);
599
+ console.log(PORT_SERVER)
600
+ console.log(index)
601
+ console.log(PORT_SERVER + parseInt(index))
602
+
603
+ if (msg_type === "images") {
521
604
  open(`http://localhost:${port}/?port=${port}&topic=${encodeURIComponent(topicid)}`);
522
605
  startShowServer(port, topic, ip);
523
606
  }
524
- else if (msg_type === "json" || msg_type === "dictionary")
525
- {
607
+ else if (msg_type === "json" || msg_type === "dictionary") {
526
608
  open(`http://localhost:${port}/?port=${port}&topic=${encodeURIComponent(topicid)}`);
527
609
  startJsonServer(port, topic, ip, msg_type);
528
610
  }
529
- });
611
+
612
+ }
613
+ else {
614
+ console.log("Index must be between 0 and 49");
615
+ }
616
+
617
+ });
618
+
530
619
 
531
620
  program
532
- .command('echo <topic> <msg_type>')
621
+ .command('echo [topic]')
533
622
  .description('Subscribe to a NEP+ topic and display raw string messages')
534
623
  .action(async (topic, msg_type) => {
535
-
536
-
537
-
538
624
  const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
539
- if (!allowedFormats.includes(msg_type)) {
540
- console.error(`Format '${msg_type}' no allowed. Allowed formats are: ${allowedFormats.join(', ')}`);
541
- return;
542
- }
543
-
544
- var opensub = function (master_ip = "127.0.0.1", msg_type) {
545
- var callback = function (msg) {
546
- var date = new Date();
547
- var dateString = date.toISOString();
548
- console.log(dateString);
549
-
550
- if (msg.length > 10000) {
551
- console.log("the message is too long to be displayed");
552
- } else {
553
- console.log(msg);
554
- }
555
- };
556
-
557
- var node = new nep.Node("nep-cli-sub");
558
- var conf = node.hybrid(master_ip);
559
- sub = node.new_sub(topic, msg_type, callback, conf);
560
- };
625
+ const master_ip = "127.0.0.1";
561
626
 
627
+ const requester = createRequester(master_ip, PORT_MASTER_INFO);
628
+ const results = await sendRequest(requester, { "input": "topics" });
562
629
 
563
- var master_ip = "127.0.0.1";
564
- async function run() {
565
- var requester = new zmq.Request();
566
- requester.connect("tcp://" + master_ip + ":" + PORT_MASTER_INFO);
630
+ topic = await selectTopic(results, topic);
631
+ msg_type = await selectMsgType(allowedFormats, msg_type, topic);
567
632
 
568
- let msg = { "input": "topics" };
569
- var message = JSON.stringify(msg);
570
- await requester.send(message);
571
- const [result] = await requester.receive();
572
- var results = JSON.parse(result.toString());
633
+ const callback = (msg) => {
634
+ const date = new Date();
635
+ const dateString = date.toISOString();
636
+ console.log(dateString);
573
637
 
574
- if (results["state"] === "failure") {
575
- console.log("Topic is not registered");
638
+ if (msg.length > 10000) {
639
+ console.log("the message is too long to be displayed");
576
640
  } else {
577
- console.log("");
578
- if (results["input"].includes(topic)) {
579
- opensub(master_ip, msg_type);
580
- } else {
581
- console.log("Topic is not registered");
582
- }
641
+ console.log(msg);
583
642
  }
584
- }
643
+ };
585
644
 
586
- run();
645
+ openSub(master_ip, topic, msg_type, callback);
587
646
  });
588
647
 
589
648
  program
590
- .command('hz <topic> <msg_type>')
591
- .description('Monitor the publishing rate of a NEP+ topic in localhost')
649
+ .command('hz [topic]')
650
+ .description('Monitor the publishing rate of a NEP+ topic')
592
651
  .action(async (topic, msg_type) => {
593
- const interfaces = os.networkInterfaces();
594
- const master_ip = "127.0.0.1"; // Change to the appropriate master IP
652
+ const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
653
+ const master_ip = "127.0.0.1";
595
654
 
596
- const opensub = function () {
597
- const timestamps = []; // To store message timestamps
655
+ const requester = createRequester(master_ip, PORT_MASTER_INFO);
656
+ const results = await sendRequest(requester, { "input": "topics" });
657
+ const timestamps = []; // Store timestamps of received messages
598
658
 
599
- const callback = function (msg) {
600
- const date = new Date();
601
- const timestamp = date.getTime();
659
+ topic = await selectTopic(results, topic);
660
+ msg_type = await selectMsgType(allowedFormats, msg_type, topic);
602
661
 
603
- timestamps.push(timestamp);
662
+ const callback = function (msg) {
663
+ const date = new Date();
664
+ const timestamp = date.getTime();
604
665
 
605
- // Remove timestamps older than ten seconds
606
- const tenSecondsAgo = timestamp - 10000;
607
- while (timestamps[0] < tenSecondsAgo) {
608
- timestamps.shift();
609
- }
610
- };
666
+ timestamps.push(timestamp);
611
667
 
612
- const allowedFormats = ["json", "msgpack", "bytes", "images", "dictionary", "string"];
613
- if (!allowedFormats.includes(msg_type)) {
614
- console.error(`Format '${msg_type}' no allowed. Allowed formats are: ${allowedFormats.join(', ')}`);
615
- return;
668
+ // Remove timestamps older than ten seconds
669
+ const tenSecondsAgo = timestamp - 10000;
670
+ while (timestamps[0] < tenSecondsAgo) {
671
+ timestamps.shift();
616
672
  }
617
- const node = new nep.Node("nep-cli-sub");
618
- const conf = node.hybrid(master_ip);
619
-
620
- console.log("Topic:", topic)
621
- console.log("Message type:", msg_type)
673
+ };
622
674
 
623
- const sub = node.new_sub(topic, msg_type, callback, conf);
675
+ setInterval(() => {
676
+ // Calculate statistics for the last 10 seconds
677
+ const now = Date.now();
678
+ const tenSecondsAgo = now - 10000;
679
+ const recentTimestamps = timestamps.filter((ts) => ts > tenSecondsAgo);
680
+ const rate = recentTimestamps.length / 10; // Messages per second for a 10-second window
681
+
682
+ console.log("Average rate:", rate.toFixed(2), "Hz");
683
+ console.log("Min:", (10 / Math.max(rate, 0.1)).toFixed(2), "s");
684
+ console.log("Max:", (10 / Math.min(rate, 10)).toFixed(2), "s");
685
+ console.log("Std dev:", calculateStdDev(recentTimestamps, now).toFixed(2), "s");
686
+ console.log("Window:", recentTimestamps.length);
624
687
  console.log("");
688
+ }, 1000);
625
689
 
626
- setInterval(() => {
627
- // Calculate statistics for the last 10 seconds
628
- const now = Date.now();
629
- const tenSecondsAgo = now - 10000;
630
- const recentTimestamps = timestamps.filter((ts) => ts > tenSecondsAgo);
631
- const rate = recentTimestamps.length / 10; // Messages per second for a 10-second window
632
-
633
- console.log("Average rate:", rate.toFixed(2), "Hz");
634
- console.log("Min:", (10 / Math.max(rate, 0.1)).toFixed(2), "s");
635
- console.log("Max:", (10 / Math.min(rate, 10)).toFixed(2), "s");
636
- console.log("Std dev:", calculateStdDev(recentTimestamps, now).toFixed(2), "s");
637
- console.log("Window:", recentTimestamps.length);
638
- console.log("");
639
- }, 1000);
640
- };
641
690
 
642
691
  const calculateStdDev = (timestamps) => {
643
692
  if (timestamps.length < 2) {
@@ -666,30 +715,8 @@ program
666
715
  return stdDeviation;
667
716
  };
668
717
 
718
+ openSub(master_ip, topic, msg_type, callback);
669
719
 
670
- async function run() {
671
- const requester = new zmq.Request();
672
- requester.connect(`tcp://${master_ip}:${PORT_MASTER_INFO}`);
673
-
674
- const msg = { "input": "topics" };
675
- const message = JSON.stringify(msg);
676
- await requester.send(message);
677
- const [result] = await requester.receive();
678
- const results = JSON.parse(result.toString());
679
-
680
- if (results["state"] === "failure") {
681
- console.log("Topic is not registered, use *nep topics* to see the list of avaliable topics");
682
- } else {
683
- console.log("");
684
- if (results["input"].includes(topic)) {
685
- opensub();
686
- } else {
687
- console.log("Topic is not registered, use *nep topics* to see the list of avaliable topics");
688
- }
689
- }
690
- }
691
-
692
- run();
693
720
  });
694
721
 
695
722
 
package/bin/json.html CHANGED
@@ -6,9 +6,9 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>NEP+ JSON Data</title>
8
8
  <!-- Include Vue 2 and Vuetify 2 from CDN -->
9
- <link href="https://cdn.jsdelivr.net/npm/vuetify@2.6.3/dist/vuetify.min.css" rel="stylesheet">
10
- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
11
- <script src="https://cdn.jsdelivr.net/npm/vuetify@2.6.3/dist/vuetify.js"></script>
9
+ <link href="assets/vuetify/vuetify.min.css" rel="stylesheet">
10
+ <script src="assets/vue.js"></script>
11
+ <script src="assets/vuetify/vuetify.js"></script>
12
12
  <!-- Include JSON Editor library -->
13
13
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/9.10.2/jsoneditor.min.js"></script>
14
14
  <link href="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/9.10.2/jsoneditor.min.css" rel="stylesheet">
@@ -109,7 +109,7 @@
109
109
  <v-app style="background-color: #0D0D0D;">
110
110
  <v-container>
111
111
  <v-toolbar flat dense dark color="transparent">
112
- <v-toolbar-title> Topic: {{ topic }}</v-toolbar-title>
112
+ <v-toolbar-title> Topic {{ topic }}</v-toolbar-title>
113
113
  <v-spacer></v-spacer>
114
114
  </v-toolbar>
115
115
  <v-row>
@@ -148,6 +148,11 @@
148
148
  topic: '',
149
149
  };
150
150
  },
151
+ watch: {
152
+ topic(newVal) {
153
+ document.title = newVal ? `NEP+ JSON ${newVal}` : 'NEP+ JSON';
154
+ }
155
+ },
151
156
  mounted() {
152
157
  this.topic = getParameterByName('topic');
153
158
  const socket = io.connect(`http://localhost:${port}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nep-cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "main": "./lib/nep.js",
5
5
  "bin": {
6
6
  "nep": "./bin/index.js"
@@ -12,11 +12,10 @@
12
12
  "commander": "^11.0.0",
13
13
  "enquirer": "^2.4.1",
14
14
  "express": "^4.18.2",
15
- "fix-path": "^2.1.0",
16
- "inquirer": "^9.2.10",
17
- "nep-js": "0.3.8",
15
+ "nep-js": "^0.3.8",
18
16
  "open": "7.4.2",
19
17
  "socket.io": "^4.7.2",
18
+ "vue": "^2.7.16",
20
19
  "zeromq": "6.0.0-beta.19"
21
20
  }
22
21
  }